<rdar://problem/13338758>

Make it configurable what to profile.
For Mac, we don't use the dirty page size yet and hence there is no need to gather that. This should be way better in not draining the battery since we are operating between 0% to 0.1% on the Mac after this change.

llvm-svn: 176451
This commit is contained in:
Han Ming Ong 2013-03-04 21:25:51 +00:00
parent 2a513e8218
commit 8764fe7d9a
10 changed files with 201 additions and 105 deletions

View File

@ -1220,22 +1220,22 @@ DNBProcessMemoryRegionInfo (nub_process_t pid, nub_addr_t addr, DNBRegionInfo *r
}
std::string
DNBProcessGetProfileData (nub_process_t pid)
DNBProcessGetProfileData (nub_process_t pid, DNBProfileDataScanType scanType)
{
MachProcessSP procSP;
if (GetProcessSP (pid, procSP))
return procSP->Task().GetProfileData();
return procSP->Task().GetProfileData(scanType);
return std::string("");
}
nub_bool_t
DNBProcessSetEnableAsyncProfiling (nub_process_t pid, nub_bool_t enable, uint64_t interval_usec)
DNBProcessSetEnableAsyncProfiling (nub_process_t pid, nub_bool_t enable, uint64_t interval_usec, DNBProfileDataScanType scan_type)
{
MachProcessSP procSP;
if (GetProcessSP (pid, procSP))
{
procSP->SetEnableAsyncProfiling(enable, interval_usec);
procSP->SetEnableAsyncProfiling(enable, interval_usec, scan_type);
return true;
}

View File

@ -63,8 +63,8 @@ nub_size_t DNBProcessMemoryWrite (nub_process_t pid, nub_addr_t addr, nub
nub_addr_t DNBProcessMemoryAllocate (nub_process_t pid, nub_size_t size, uint32_t permissions) DNB_EXPORT;
nub_bool_t DNBProcessMemoryDeallocate (nub_process_t pid, nub_addr_t addr) DNB_EXPORT;
int DNBProcessMemoryRegionInfo (nub_process_t pid, nub_addr_t addr, DNBRegionInfo *region_info) DNB_EXPORT;
std::string DNBProcessGetProfileData (nub_process_t pid) DNB_EXPORT;
nub_bool_t DNBProcessSetEnableAsyncProfiling (nub_process_t pid, nub_bool_t enable, uint64_t interval_usec) DNB_EXPORT;
std::string DNBProcessGetProfileData (nub_process_t pid, DNBProfileDataScanType scanType) DNB_EXPORT;
nub_bool_t DNBProcessSetEnableAsyncProfiling (nub_process_t pid, nub_bool_t enable, uint64_t interval_usec, DNBProfileDataScanType scan_type) DNB_EXPORT;
//----------------------------------------------------------------------
// Process status

View File

@ -358,6 +358,23 @@ struct DNBRegionInfo
uint32_t permissions;
};
enum DNBProfileDataScanType
{
eProfileHostCPU = (1 << 0),
eProfileCPU = (1 << 1),
eProfileThreadsCPU = (1 << 2), // By default excludes eProfileThreadName and eProfileQueueName.
eProfileThreadName = (1 << 3), // Assume eProfileThreadsCPU, get thread name as well.
eProfileQueueName = (1 << 4), // Assume eProfileThreadsCPU, get queue name as well.
eProfileHostMemory = (1 << 5),
eProfileMemory = (1 << 6), // By default, excludes eProfileMemoryDirtyPage.
eProfileMemoryDirtyPage = (1 << 7), // Assume eProfileMemory, get Dirty Page size as well.
eProfileAll = 0xffffffff
};
typedef nub_bool_t (*DNBCallbackBreakpointHit)(nub_process_t pid, nub_thread_t tid, nub_break_t breakID, void *baton);
typedef nub_addr_t (*DNBCallbackNameToAddress)(nub_process_t pid, const char *name, const char *shlib_regex, void *baton);
typedef nub_size_t (*DNBCallbackCopyExecutableImageInfos)(nub_process_t pid, struct DNBExecutableImageInfo **image_infos, nub_bool_t only_changed, void *baton);

View File

@ -315,10 +315,11 @@ MachProcess::StartSTDIOThread()
}
void
MachProcess::SetEnableAsyncProfiling(bool enable, uint64_t interval_usec)
MachProcess::SetEnableAsyncProfiling(bool enable, uint64_t interval_usec, DNBProfileDataScanType scan_type)
{
m_profile_enabled = enable;
m_profile_interval_usec = interval_usec;
m_profile_scan_type = scan_type;
if (m_profile_enabled && (m_profile_thread == NULL))
{
@ -1411,7 +1412,7 @@ MachProcess::ProfileThread(void *arg)
nub_state_t state = proc->GetState();
if (state == eStateRunning)
{
std::string data = proc->Task().GetProfileData();
std::string data = proc->Task().GetProfileData(proc->GetProfileScanType());
if (!data.empty())
{
proc->SignalAsyncProfileData(data.c_str());

View File

@ -148,7 +148,7 @@ public:
//----------------------------------------------------------------------
// Profile functions
//----------------------------------------------------------------------
void SetEnableAsyncProfiling (bool enable, uint64_t internal_usec);
void SetEnableAsyncProfiling (bool enable, uint64_t internal_usec, DNBProfileDataScanType scan_type);
bool IsProfilingEnabled () { return m_profile_enabled; }
uint64_t ProfileInterval () { return m_profile_interval_usec; }
bool StartProfileThread ();
@ -250,6 +250,9 @@ public:
}
bool ProcessUsingSpringBoard() const { return (m_flags & eMachProcessFlagsUsingSBS) != 0; }
DNBProfileDataScanType GetProfileScanType () { return m_profile_scan_type; }
private:
enum
{
@ -282,6 +285,7 @@ private:
bool m_profile_enabled; // A flag to indicate if profiling is enabled
uint64_t m_profile_interval_usec; // If enable, the profiling interval in microseconds
DNBProfileDataScanType m_profile_scan_type; // Indicates what needs to be profiled
pthread_t m_profile_thread; // Thread ID for the thread that profiles the inferior
PThreadMutex m_profile_data_mutex; // Multithreaded protection for profile info data
std::vector<std::string> m_profile_data; // Profile data, must be protected by m_profile_data_mutex

View File

@ -233,7 +233,7 @@ MachTask::GetMemoryRegionInfo (nub_addr_t addr, DNBRegionInfo *region_info)
} while (0)
// We should consider moving this into each MacThread.
static void get_threads_profile_data(task_t task, nub_process_t pid, std::vector<uint64_t> &threads_id, std::vector<std::string> &threads_name, std::vector<uint64_t> &threads_used_usec)
static void get_threads_profile_data(DNBProfileDataScanType scanType, task_t task, nub_process_t pid, std::vector<uint64_t> &threads_id, std::vector<std::string> &threads_name, std::vector<uint64_t> &threads_used_usec)
{
kern_return_t kr;
thread_act_array_t threads;
@ -243,7 +243,8 @@ static void get_threads_profile_data(task_t task, nub_process_t pid, std::vector
if (kr != KERN_SUCCESS)
return;
for (int i = 0; i < tcnt; i++) {
for (int i = 0; i < tcnt; i++)
{
thread_identifier_info_data_t identifier_info;
mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
kr = ::thread_info(threads[i], THREAD_IDENTIFIER_INFO, (thread_info_t)&identifier_info, &count);
@ -254,22 +255,26 @@ static void get_threads_profile_data(task_t task, nub_process_t pid, std::vector
kr = ::thread_info(threads[i], THREAD_BASIC_INFO, (thread_info_t)&basic_info, &count);
if (kr != KERN_SUCCESS) continue;
if ((basic_info.flags & TH_FLAGS_IDLE) == 0) {
if ((basic_info.flags & TH_FLAGS_IDLE) == 0)
{
nub_thread_t tid = MachThread::GetGloballyUniqueThreadIDForMachPortID (threads[i]);
threads_id.push_back(tid);
if (identifier_info.thread_handle != 0) {
if ((scanType & eProfileThreadName) && (identifier_info.thread_handle != 0))
{
struct proc_threadinfo proc_threadinfo;
int len = ::proc_pidinfo(pid, PROC_PIDTHREADINFO, identifier_info.thread_handle, &proc_threadinfo, PROC_PIDTHREADINFO_SIZE);
if (len && proc_threadinfo.pth_name[0]) {
if (len && proc_threadinfo.pth_name[0])
{
threads_name.push_back(proc_threadinfo.pth_name);
}
else {
else
{
threads_name.push_back("");
}
}
else {
else
{
threads_name.push_back("");
}
struct timeval tv;
@ -289,26 +294,29 @@ static void get_threads_profile_data(task_t task, nub_process_t pid, std::vector
#define RAW_HEXBASE std::setfill('0') << std::hex << std::right
#define DECIMAL std::dec << std::setfill(' ')
std::string
MachTask::GetProfileData ()
MachTask::GetProfileData (DNBProfileDataScanType scanType)
{
std::string result;
static int32_t numCPU = -1;
int32_t mib[] = {CTL_HW, HW_AVAILCPU};
size_t len = sizeof(numCPU);
if (numCPU == -1)
struct host_cpu_load_info host_info;
if (scanType & eProfileHostCPU)
{
if (sysctl(mib, sizeof(mib) / sizeof(int32_t), &numCPU, &len, NULL, 0) != 0)
int32_t mib[] = {CTL_HW, HW_AVAILCPU};
size_t len = sizeof(numCPU);
if (numCPU == -1)
{
if (sysctl(mib, sizeof(mib) / sizeof(int32_t), &numCPU, &len, NULL, 0) != 0)
return result;
}
mach_port_t localHost = mach_host_self();
mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
kern_return_t kr = host_statistics(localHost, HOST_CPU_LOAD_INFO, (host_info_t)&host_info, &count);
if (kr != KERN_SUCCESS)
return result;
}
mach_port_t localHost = mach_host_self();
struct host_cpu_load_info host_info;
mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
kern_return_t kr = host_statistics(localHost, HOST_CPU_LOAD_INFO, (host_info_t)&host_info, &count);
if (kr != KERN_SUCCESS)
return result;
task_t task = TaskPort();
if (task == TASK_NULL)
return result;
@ -322,24 +330,31 @@ MachTask::GetProfileData ()
uint64_t elapsed_usec = 0;
uint64_t task_used_usec = 0;
if (scanType & eProfileCPU)
{
// Get current used time.
struct timeval current_used_time;
struct timeval tv;
TIME_VALUE_TO_TIMEVAL(&task_info.user_time, &current_used_time);
TIME_VALUE_TO_TIMEVAL(&task_info.system_time, &tv);
timeradd(&current_used_time, &tv, &current_used_time);
task_used_usec = current_used_time.tv_sec * 1000000ULL + current_used_time.tv_usec;
struct timeval current_elapsed_time;
int res = gettimeofday(&current_elapsed_time, NULL);
if (res == 0)
{
elapsed_usec = current_elapsed_time.tv_sec * 1000000ULL + current_elapsed_time.tv_usec;
}
}
std::vector<uint64_t> threads_id;
std::vector<std::string> threads_name;
std::vector<uint64_t> threads_used_usec;
// Get current used time.
struct timeval current_used_time;
struct timeval tv;
TIME_VALUE_TO_TIMEVAL(&task_info.user_time, &current_used_time);
TIME_VALUE_TO_TIMEVAL(&task_info.system_time, &tv);
timeradd(&current_used_time, &tv, &current_used_time);
task_used_usec = current_used_time.tv_sec * 1000000ULL + current_used_time.tv_usec;
get_threads_profile_data(task, m_process->ProcessID(), threads_id, threads_name, threads_used_usec);
struct timeval current_elapsed_time;
int res = gettimeofday(&current_elapsed_time, NULL);
if (res == 0)
if (scanType & eProfileThreadsCPU)
{
elapsed_usec = current_elapsed_time.tv_sec * 1000000ULL + current_elapsed_time.tv_usec;
get_threads_profile_data(scanType, task, m_process->ProcessID(), threads_id, threads_name, threads_used_usec);
}
struct vm_statistics vm_stats;
@ -349,53 +364,75 @@ MachTask::GetProfileData ()
mach_vm_size_t vprvt = 0;
mach_vm_size_t vsize = 0;
mach_vm_size_t dirty_size = 0;
if (m_vm_memory.GetMemoryProfile(task, task_info, m_process->GetCPUType(), m_process->ProcessID(), vm_stats, physical_memory, rprvt, rsize, vprvt, vsize, dirty_size))
if (m_vm_memory.GetMemoryProfile(scanType, task, task_info, m_process->GetCPUType(), m_process->ProcessID(), vm_stats, physical_memory, rprvt, rsize, vprvt, vsize, dirty_size))
{
std::ostringstream profile_data_stream;
profile_data_stream << "num_cpu:" << numCPU << ';';
profile_data_stream << "host_user_ticks:" << host_info.cpu_ticks[CPU_STATE_USER] << ';';
profile_data_stream << "host_sys_ticks:" << host_info.cpu_ticks[CPU_STATE_SYSTEM] << ';';
profile_data_stream << "host_idle_ticks:" << host_info.cpu_ticks[CPU_STATE_IDLE] << ';';
profile_data_stream << "elapsed_usec:" << elapsed_usec << ';';
profile_data_stream << "task_used_usec:" << task_used_usec << ';';
int num_threads = threads_id.size();
for (int i=0; i<num_threads; i++) {
profile_data_stream << "thread_used_id:" << std::hex << threads_id[i] << std::dec << ';';
profile_data_stream << "thread_used_usec:" << threads_used_usec[i] << ';';
profile_data_stream << "thread_used_name:";
int len = threads_name[i].size();
if (len) {
const char *thread_name = threads_name[i].c_str();
// Make sure that thread name doesn't interfere with our delimiter.
profile_data_stream << RAW_HEXBASE << std::setw(2);
const uint8_t *ubuf8 = (const uint8_t *)(thread_name);
for (int j=0; j<len; j++)
{
profile_data_stream << (uint32_t)(ubuf8[j]);
}
// Reset back to DECIMAL.
profile_data_stream << DECIMAL;
}
profile_data_stream << ';';
if (scanType & eProfileHostCPU)
{
profile_data_stream << "num_cpu:" << numCPU << ';';
profile_data_stream << "host_user_ticks:" << host_info.cpu_ticks[CPU_STATE_USER] << ';';
profile_data_stream << "host_sys_ticks:" << host_info.cpu_ticks[CPU_STATE_SYSTEM] << ';';
profile_data_stream << "host_idle_ticks:" << host_info.cpu_ticks[CPU_STATE_IDLE] << ';';
}
profile_data_stream << "wired:" << vm_stats.wire_count * vm_page_size << ';';
profile_data_stream << "active:" << vm_stats.active_count * vm_page_size << ';';
profile_data_stream << "inactive:" << vm_stats.inactive_count * vm_page_size << ';';
uint64_t total_used_count = vm_stats.wire_count + vm_stats.inactive_count + vm_stats.active_count;
profile_data_stream << "used:" << total_used_count * vm_page_size << ';';
profile_data_stream << "free:" << vm_stats.free_count * vm_page_size << ';';
profile_data_stream << "total:" << physical_memory << ';';
if (scanType & eProfileCPU)
{
profile_data_stream << "elapsed_usec:" << elapsed_usec << ';';
profile_data_stream << "task_used_usec:" << task_used_usec << ';';
}
if (scanType & eProfileThreadsCPU)
{
int num_threads = threads_id.size();
for (int i=0; i<num_threads; i++)
{
profile_data_stream << "thread_used_id:" << std::hex << threads_id[i] << std::dec << ';';
profile_data_stream << "thread_used_usec:" << threads_used_usec[i] << ';';
if (scanType & eProfileThreadName)
{
profile_data_stream << "thread_used_name:";
int len = threads_name[i].size();
if (len)
{
const char *thread_name = threads_name[i].c_str();
// Make sure that thread name doesn't interfere with our delimiter.
profile_data_stream << RAW_HEXBASE << std::setw(2);
const uint8_t *ubuf8 = (const uint8_t *)(thread_name);
for (int j=0; j<len; j++)
{
profile_data_stream << (uint32_t)(ubuf8[j]);
}
// Reset back to DECIMAL.
profile_data_stream << DECIMAL;
}
profile_data_stream << ';';
}
}
}
if (scanType & eProfileHostMemory)
profile_data_stream << "total:" << physical_memory << ';';
if (scanType & eProfileMemory)
{
profile_data_stream << "wired:" << vm_stats.wire_count * vm_page_size << ';';
profile_data_stream << "active:" << vm_stats.active_count * vm_page_size << ';';
profile_data_stream << "inactive:" << vm_stats.inactive_count * vm_page_size << ';';
uint64_t total_used_count = vm_stats.wire_count + vm_stats.inactive_count + vm_stats.active_count;
profile_data_stream << "used:" << total_used_count * vm_page_size << ';';
profile_data_stream << "free:" << vm_stats.free_count * vm_page_size << ';';
profile_data_stream << "rprvt:" << rprvt << ';';
profile_data_stream << "rsize:" << rsize << ';';
profile_data_stream << "vprvt:" << vprvt << ';';
profile_data_stream << "vsize:" << vsize << ';';
if (scanType & eProfileMemoryDirtyPage)
profile_data_stream << "dirty:" << dirty_size << ';';
}
profile_data_stream << "rprvt:" << rprvt << ';';
profile_data_stream << "rsize:" << rsize << ';';
profile_data_stream << "vprvt:" << vprvt << ';';
profile_data_stream << "vsize:" << vsize << ';';
profile_data_stream << "dirty:" << dirty_size << ';';
profile_data_stream << "--end--;";
result = profile_data_stream.str();

View File

@ -26,6 +26,7 @@
#include <string>
// Other libraries and framework includes
// Project includes
#include "DNBDefs.h"
#include "MachException.h"
#include "MachVMMemory.h"
#include "PThreadMutex.h"
@ -66,7 +67,7 @@ public:
nub_size_t ReadMemory (nub_addr_t addr, nub_size_t size, void *buf);
nub_size_t WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf);
int GetMemoryRegionInfo (nub_addr_t addr, DNBRegionInfo *region_info);
std::string GetProfileData ();
std::string GetProfileData (DNBProfileDataScanType scanType);
nub_addr_t AllocateMemory (nub_size_t size, uint32_t permissions);
nub_bool_t DeallocateMemory (nub_addr_t addr);

View File

@ -397,21 +397,29 @@ static void GetMemorySizes(task_t task, cpu_type_t cputype, nub_process_t pid, m
}
nub_bool_t
MachVMMemory::GetMemoryProfile(task_t task, struct task_basic_info ti, cpu_type_t cputype, nub_process_t pid, vm_statistics_data_t &vm_stats, uint64_t &physical_memory, mach_vm_size_t &rprvt, mach_vm_size_t &rsize, mach_vm_size_t &vprvt, mach_vm_size_t &vsize, mach_vm_size_t &dirty_size)
MachVMMemory::GetMemoryProfile(DNBProfileDataScanType scanType, task_t task, struct task_basic_info ti, cpu_type_t cputype, nub_process_t pid, vm_statistics_data_t &vm_stats, uint64_t &physical_memory, mach_vm_size_t &rprvt, mach_vm_size_t &rsize, mach_vm_size_t &vprvt, mach_vm_size_t &vsize, mach_vm_size_t &dirty_size)
{
static mach_port_t localHost = mach_host_self();
mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
host_statistics(localHost, HOST_VM_INFO, (host_info_t)&vm_stats, &count);
vm_stats.wire_count += GetStolenPages();
physical_memory = GetPhysicalMemory();
// This uses vmmap strategy. We don't use the returned rsize for now. We prefer to match top's version since that's what we do for the rest of the metrics.
GetRegionSizes(task, rsize, dirty_size);
if (scanType & eProfileHostMemory)
physical_memory = GetPhysicalMemory();
GetMemorySizes(task, cputype, pid, rprvt, vprvt);
if (scanType & eProfileMemory)
{
static mach_port_t localHost = mach_host_self();
mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
host_statistics(localHost, HOST_VM_INFO, (host_info_t)&vm_stats, &count);
vm_stats.wire_count += GetStolenPages();
rsize = ti.resident_size;
vsize = ti.virtual_size;
GetMemorySizes(task, cputype, pid, rprvt, vprvt);
rsize = ti.resident_size;
vsize = ti.virtual_size;
}
if (scanType & eProfileMemoryDirtyPage)
{
// This uses vmmap strategy. We don't use the returned rsize for now. We prefer to match top's version since that's what we do for the rest of the metrics.
GetRegionSizes(task, rsize, dirty_size);
}
return true;
}

View File

@ -28,7 +28,7 @@ public:
nub_size_t Write(task_t task, nub_addr_t address, const void *data, nub_size_t data_count);
nub_size_t PageSize();
nub_bool_t GetMemoryRegionInfo(task_t task, nub_addr_t address, DNBRegionInfo *region_info);
nub_bool_t GetMemoryProfile(task_t task, struct task_basic_info ti, cpu_type_t cputype, nub_process_t pid, vm_statistics_data_t &vm_stats, uint64_t &physical_memory, mach_vm_size_t &rprvt, mach_vm_size_t &rsize, mach_vm_size_t &vprvt, mach_vm_size_t &vsize, mach_vm_size_t &dirty_size);
nub_bool_t GetMemoryProfile(DNBProfileDataScanType scanType, task_t task, struct task_basic_info ti, cpu_type_t cputype, nub_process_t pid, vm_statistics_data_t &vm_stats, uint64_t &physical_memory, mach_vm_size_t &rprvt, mach_vm_size_t &rsize, mach_vm_size_t &vprvt, mach_vm_size_t &vsize, mach_vm_size_t &dirty_size);
protected:
nub_size_t MaxBytesLeftInPage(nub_addr_t addr, nub_size_t count);

View File

@ -3645,14 +3645,32 @@ RNBRemote::HandlePacket_MemoryRegionInfo (const char *p)
return SendPacket (ostrm.str());
}
// qGetProfileData;scan_type:0xYYYYYYY
rnb_err_t
RNBRemote::HandlePacket_GetProfileData (const char *p)
{
nub_process_t pid = m_ctx.ProcessID();
if (pid == INVALID_NUB_PROCESS)
return SendPacket ("OK");
std::string data = DNBProcessGetProfileData(pid);
StringExtractor packet(p += sizeof ("qGetProfileData"));
DNBProfileDataScanType scan_type = eProfileAll;
std::string name;
std::string value;
while (packet.GetNameColonValue(name, value))
{
if (name.compare ("scan_type") == 0)
{
std::istringstream iss(value);
uint32_t int_value = 0;
if (iss >> std::hex >> int_value)
{
scan_type = (DNBProfileDataScanType)int_value;
}
}
}
std::string data = DNBProcessGetProfileData(pid, scan_type);
if (!data.empty())
{
return SendPacket (data.c_str());
@ -3663,18 +3681,18 @@ RNBRemote::HandlePacket_GetProfileData (const char *p)
}
}
// QSetEnableAsyncProfiling;enable:[0|1]:interval_usec:XXXXXX;
// QSetEnableAsyncProfiling;enable:[0|1]:interval_usec:XXXXXX;scan_type:0xYYYYYYY
rnb_err_t
RNBRemote::HandlePacket_SetEnableAsyncProfiling (const char *p)
{
nub_process_t pid = m_ctx.ProcessID();
if (pid == INVALID_NUB_PROCESS)
return SendPacket ("");
return SendPacket ("OK");
StringExtractor packet(p += sizeof ("QSetEnableAsyncProfiling:") - 1);
StringExtractor packet(p += sizeof ("QSetEnableAsyncProfiling"));
bool enable = false;
uint64_t interval_usec = 0;
DNBProfileDataScanType scan_type = eProfileAll;
std::string name;
std::string value;
while (packet.GetNameColonValue(name, value))
@ -3687,13 +3705,23 @@ RNBRemote::HandlePacket_SetEnableAsyncProfiling (const char *p)
{
interval_usec = strtoul(value.c_str(), NULL, 10);
}
else if (name.compare ("scan_type") == 0)
{
std::istringstream iss(value);
uint32_t int_value = 0;
if (iss >> std::hex >> int_value)
{
scan_type = (DNBProfileDataScanType)int_value;
}
}
}
if (interval_usec == 0)
{
enable = 0;
}
DNBProcessSetEnableAsyncProfiling(pid, enable, interval_usec);
DNBProcessSetEnableAsyncProfiling(pid, enable, interval_usec, scan_type);
return SendPacket ("OK");
}