OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "stdafx.h" |
| 6 |
| 7 #include <map> |
| 8 #include <vector> |
| 9 #include <algorithm> |
| 10 |
| 11 #include "power_sampler.h" |
| 12 |
| 13 // From ntdef.h |
| 14 typedef struct _UNICODE_STRING { |
| 15 USHORT Length; |
| 16 USHORT MaximumLength; |
| 17 PWCH Buffer; |
| 18 } UNICODE_STRING; |
| 19 |
| 20 // From <wdm.h> |
| 21 typedef LONG KPRIORITY; |
| 22 typedef LONG KWAIT_REASON; // Full definition is in wdm.h |
| 23 |
| 24 // From ntddk.h |
| 25 typedef struct _VM_COUNTERS { |
| 26 SIZE_T PeakVirtualSize; |
| 27 SIZE_T VirtualSize; |
| 28 ULONG PageFaultCount; |
| 29 // Padding here in 64-bit |
| 30 SIZE_T PeakWorkingSetSize; |
| 31 SIZE_T WorkingSetSize; |
| 32 SIZE_T QuotaPeakPagedPoolUsage; |
| 33 SIZE_T QuotaPagedPoolUsage; |
| 34 SIZE_T QuotaPeakNonPagedPoolUsage; |
| 35 SIZE_T QuotaNonPagedPoolUsage; |
| 36 SIZE_T PagefileUsage; |
| 37 SIZE_T PeakPagefileUsage; |
| 38 } VM_COUNTERS; |
| 39 |
| 40 // Two possibilities available from here: |
| 41 // http://stackoverflow.com/questions/28858849/where-is-system-information-class
-defined |
| 42 |
| 43 typedef enum _SYSTEM_INFORMATION_CLASS { |
| 44 SystemBasicInformation = 0, |
| 45 SystemPerformanceInformation = 2, |
| 46 SystemTimeOfDayInformation = 3, |
| 47 SystemProcessInformation = 5, // This is the number that we need |
| 48 SystemProcessorPerformanceInformation = 8, |
| 49 SystemInterruptInformation = 23, |
| 50 SystemExceptionInformation = 33, |
| 51 SystemRegistryQuotaInformation = 37, |
| 52 SystemLookasideInformation = 45 |
| 53 } SYSTEM_INFORMATION_CLASS; |
| 54 |
| 55 // https://msdn.microsoft.com/en-us/library/gg750647.aspx?f=255&MSPPError=-21472
17396 |
| 56 typedef struct { |
| 57 HANDLE UniqueProcess; // Actually process ID |
| 58 HANDLE UniqueThread; // Actually thread ID |
| 59 } CLIENT_ID; |
| 60 |
| 61 // From http://alax.info/blog/1182, with corrections and modifications |
| 62 // Originally from |
| 63 // http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%2
0Functions%2FSystem%20Information%2FStructures%2FSYSTEM_THREAD.html |
| 64 struct SYSTEM_THREAD_INFORMATION { |
| 65 ULONGLONG KernelTime; |
| 66 ULONGLONG UserTime; |
| 67 ULONGLONG CreateTime; |
| 68 ULONG WaitTime; |
| 69 // Padding here in 64-bit |
| 70 PVOID StartAddress; |
| 71 CLIENT_ID ClientId; |
| 72 KPRIORITY Priority; |
| 73 LONG BasePriority; |
| 74 ULONG ContextSwitchCount; |
| 75 ULONG State; |
| 76 KWAIT_REASON WaitReason; |
| 77 }; |
| 78 #if _M_X64 |
| 79 static_assert(sizeof(SYSTEM_THREAD_INFORMATION) == 80, |
| 80 "Structure size mismatch"); |
| 81 #else |
| 82 static_assert(sizeof(SYSTEM_THREAD_INFORMATION) == 64, |
| 83 "Structure size mismatch"); |
| 84 #endif |
| 85 |
| 86 // From http://alax.info/blog/1182, with corrections and modifications |
| 87 // Originally from |
| 88 // http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%2
0Functions%2FSystem%20Information%2FStructures%2FSYSTEM_THREAD.html |
| 89 struct SYSTEM_PROCESS_INFORMATION { |
| 90 ULONG NextEntryOffset; |
| 91 ULONG NumberOfThreads; |
| 92 // http://processhacker.sourceforge.net/doc/struct___s_y_s_t_e_m___p_r_o_c_e_s
_s___i_n_f_o_r_m_a_t_i_o_n.html |
| 93 ULONGLONG WorkingSetPrivateSize; |
| 94 ULONG HardFaultCount; |
| 95 ULONG Reserved1; |
| 96 ULONGLONG CycleTime; |
| 97 ULONGLONG CreateTime; |
| 98 ULONGLONG UserTime; |
| 99 ULONGLONG KernelTime; |
| 100 UNICODE_STRING ImageName; |
| 101 KPRIORITY BasePriority; |
| 102 HANDLE ProcessId; |
| 103 HANDLE ParentProcessId; |
| 104 ULONG HandleCount; |
| 105 ULONG Reserved2[2]; |
| 106 // Padding here in 64-bit |
| 107 VM_COUNTERS VirtualMemoryCounters; |
| 108 size_t Reserved3; |
| 109 IO_COUNTERS IoCounters; |
| 110 SYSTEM_THREAD_INFORMATION Threads[1]; |
| 111 }; |
| 112 #if _M_X64 |
| 113 static_assert(sizeof(SYSTEM_PROCESS_INFORMATION) == 336, |
| 114 "Structure size mismatch"); |
| 115 #else |
| 116 static_assert(sizeof(SYSTEM_PROCESS_INFORMATION) == 248, |
| 117 "Structure size mismatch"); |
| 118 #endif |
| 119 |
| 120 // ntstatus.h conflicts with windows.h so define this locally. |
| 121 #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) |
| 122 #define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L) |
| 123 #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) |
| 124 |
| 125 typedef NTSTATUS(WINAPI* NTQUERYSYSTEMINFORMATION)( |
| 126 SYSTEM_INFORMATION_CLASS SystemInformationClass, |
| 127 PVOID SystemInformation, |
| 128 ULONG SystemInformationLength, |
| 129 PULONG ReturnLength); |
| 130 |
| 131 __declspec(noreturn) void oops(const char* pMessage) { |
| 132 printf("%s\n", pMessage); |
| 133 exit(0); |
| 134 } |
| 135 |
| 136 // Contains per thread data stored in each data snapshot. |
| 137 struct ThreadData { |
| 138 HANDLE ThreadId; |
| 139 ULONG ContextSwitches; |
| 140 }; |
| 141 |
| 142 typedef std::vector<ThreadData> ThreadsVector; |
| 143 |
| 144 // Contains per process data stored in each data snapshot. |
| 145 struct ProcessData { |
| 146 HANDLE ProcessId; |
| 147 ULONGLONG CpuTime; |
| 148 ULONGLONG WorkingSetPrivateSize; |
| 149 ThreadsVector Threads; |
| 150 }; |
| 151 |
| 152 // A vector of ProcessData represents one snapshot of perf data |
| 153 // collected by the tool each collection interval. |
| 154 typedef std::vector<ProcessData> ProcessesVector; |
| 155 |
| 156 // Result data structure contains a final set of values calculated based on |
| 157 // comparison of two snapshots. These are the values that the tool prints |
| 158 // in the output. |
| 159 struct Result { |
| 160 ULONG IdleWakeupsPerSec; |
| 161 double CpuUsage; |
| 162 ULONGLONG WorkingSet; |
| 163 double Power; |
| 164 }; |
| 165 |
| 166 typedef std::vector<Result> ResultVector; |
| 167 |
| 168 // The following 4 functions are used for sorting of ResultVector. |
| 169 ULONG GetIdleWakeupsPerSec(const Result& r) { |
| 170 return r.IdleWakeupsPerSec; |
| 171 } |
| 172 double GetCpuUsage(const Result& r) { |
| 173 return r.CpuUsage; |
| 174 } |
| 175 ULONGLONG GetWorkingSet(const Result& r) { |
| 176 return r.WorkingSet; |
| 177 } |
| 178 double GetPower(const Result& r) { |
| 179 return r.Power; |
| 180 } |
| 181 |
| 182 template <typename T> |
| 183 T GetMedian(ResultVector* results, T (*getter)(const Result&)) { |
| 184 std::sort(results->begin(), results->end(), |
| 185 [&](const Result& lhs, const Result& rhs) { |
| 186 return getter(lhs) < getter(rhs); |
| 187 }); |
| 188 |
| 189 size_t median_index = results->size() / 2; |
| 190 if (results->size() % 2 != 0) { |
| 191 return getter((*results)[median_index]); |
| 192 } else { |
| 193 return (getter((*results)[median_index - 1]) + |
| 194 getter((*results)[median_index])) / |
| 195 2; |
| 196 } |
| 197 } |
| 198 |
| 199 // This class holds the app state and constains a number of utilities for |
| 200 // collecting and diffing snapshots of data, handling processes, etc. |
| 201 class IdleWakeups { |
| 202 public: |
| 203 IdleWakeups(const wchar_t* process_name); |
| 204 ~IdleWakeups(); |
| 205 |
| 206 double TakeSnapshot(ProcessesVector* processes); |
| 207 Result DiffSnapshots(double time_delta, |
| 208 const ProcessesVector& prev_processes, |
| 209 const ProcessesVector& processes); |
| 210 |
| 211 void OpenProcesses(const ProcessesVector& processes); |
| 212 void CloseProcesses(); |
| 213 |
| 214 const wchar_t* target_process_name_filter() const { |
| 215 return target_process_name; |
| 216 } |
| 217 |
| 218 private: |
| 219 HANDLE GetProcessHandle(const ProcessData& process); |
| 220 void OpenProcess(const ProcessData& process); |
| 221 void CloseProcess(const ProcessData& process); |
| 222 bool GetFinishedProcessCpuTime(const ProcessData& process, |
| 223 ULONGLONG* cpu_usage); |
| 224 |
| 225 static void SortThreads(ThreadsVector* processes); |
| 226 static void SortProcesses(ProcessesVector* processes); |
| 227 |
| 228 static ULONG CountContextSwitches(const ProcessData& process_data); |
| 229 static ULONG DiffContextSwitches(const ProcessData& prev_process_data, |
| 230 const ProcessData& process_data); |
| 231 |
| 232 DWORD NumberOfprocessors() const { return number_of_processors; } |
| 233 |
| 234 NTQUERYSYSTEMINFORMATION nt_query_system_information_ptr; |
| 235 DWORD number_of_processors; |
| 236 wchar_t target_process_name[256]; |
| 237 LARGE_INTEGER perf_frequency; |
| 238 LARGE_INTEGER previous_perf_counter_value; |
| 239 ULONG previous_buffer_size = 0; |
| 240 |
| 241 // The first argument of HANDLE type is actually used for process IDs. This is |
| 242 // consistent with data structures above. |
| 243 std::map<HANDLE, HANDLE> process_id_to_hanle_map; |
| 244 }; |
| 245 |
| 246 IdleWakeups::IdleWakeups(const wchar_t* process_name) { |
| 247 lstrcpyn(target_process_name, process_name, |
| 248 sizeof(target_process_name) / sizeof(wchar_t)); |
| 249 |
| 250 HMODULE ntdll = GetModuleHandle(_T("ntdll.dll")); |
| 251 if (!ntdll) |
| 252 oops("Couldn't load ntdll.dll"); |
| 253 nt_query_system_information_ptr = (NTQUERYSYSTEMINFORMATION)GetProcAddress( |
| 254 ntdll, "NtQuerySystemInformation"); |
| 255 if (!nt_query_system_information_ptr) |
| 256 oops("Couldn't find NtQuerySystemInformation"); |
| 257 |
| 258 SYSTEM_INFO system_info; |
| 259 GetNativeSystemInfo(&system_info); |
| 260 number_of_processors = system_info.dwNumberOfProcessors; |
| 261 printf("Number of processors: %d\n", number_of_processors); |
| 262 |
| 263 QueryPerformanceFrequency(&perf_frequency); |
| 264 } |
| 265 |
| 266 IdleWakeups::~IdleWakeups() { |
| 267 CloseProcesses(); |
| 268 } |
| 269 |
| 270 void IdleWakeups::OpenProcesses(const ProcessesVector& processes) { |
| 271 for (auto& process : processes) { |
| 272 OpenProcess(process); |
| 273 } |
| 274 } |
| 275 |
| 276 void IdleWakeups::CloseProcesses() { |
| 277 for (auto& pair : process_id_to_hanle_map) { |
| 278 CloseHandle(pair.second); |
| 279 } |
| 280 process_id_to_hanle_map.clear(); |
| 281 } |
| 282 |
| 283 HANDLE IdleWakeups::GetProcessHandle(const ProcessData& process_data) { |
| 284 return process_id_to_hanle_map[process_data.ProcessId]; |
| 285 } |
| 286 |
| 287 void IdleWakeups::OpenProcess(const ProcessData& process_data) { |
| 288 process_id_to_hanle_map[process_data.ProcessId] = |
| 289 ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, |
| 290 (DWORD)(ULONGLONG)process_data.ProcessId); |
| 291 } |
| 292 |
| 293 void IdleWakeups::CloseProcess(const ProcessData& process) { |
| 294 HANDLE handle = GetProcessHandle(process); |
| 295 CloseHandle(handle); |
| 296 process_id_to_hanle_map.erase(process.ProcessId); |
| 297 } |
| 298 |
| 299 double IdleWakeups::TakeSnapshot(ProcessesVector* processes) { |
| 300 ULONG data_size; |
| 301 |
| 302 LARGE_INTEGER perf_counter_value; |
| 303 std::vector<BYTE> process_data(previous_buffer_size); |
| 304 |
| 305 for (;;) { |
| 306 data_size = 0; |
| 307 NTSTATUS result = nt_query_system_information_ptr( |
| 308 SystemProcessInformation, |
| 309 previous_buffer_size > 0 ? &process_data[0] : NULL, |
| 310 previous_buffer_size, &data_size); |
| 311 if (result == STATUS_INFO_LENGTH_MISMATCH || |
| 312 result == STATUS_BUFFER_TOO_SMALL) { |
| 313 // Reallocate the buffer. |
| 314 previous_buffer_size = data_size; |
| 315 process_data.resize(data_size); |
| 316 continue; |
| 317 } |
| 318 |
| 319 if (result != STATUS_SUCCESS) |
| 320 oops("NtQuerySystemInformation failed"); |
| 321 |
| 322 QueryPerformanceCounter(&perf_counter_value); |
| 323 break; |
| 324 } |
| 325 |
| 326 for (size_t offset = 0; offset < data_size;) { |
| 327 auto pi = reinterpret_cast<const SYSTEM_PROCESS_INFORMATION*>( |
| 328 process_data.data() + offset); |
| 329 |
| 330 // Validate that the offset is valid and all needed data is within |
| 331 // the buffer boundary. |
| 332 if (offset + sizeof(SYSTEM_PROCESS_INFORMATION) > data_size) |
| 333 break; |
| 334 if (offset + sizeof(SYSTEM_PROCESS_INFORMATION) + |
| 335 (pi->NumberOfThreads - 1) * sizeof(SYSTEM_THREAD_INFORMATION) > |
| 336 data_size) |
| 337 break; |
| 338 |
| 339 if (pi->ImageName.Buffer && |
| 340 wcsncmp(target_process_name_filter(), pi->ImageName.Buffer, |
| 341 lstrlen(target_process_name_filter())) == 0) { |
| 342 // There is no point in recording per-process idle wakeups with any more |
| 343 // precision than they are recorded per-thread because the per-thread |
| 344 // numbers may have wrapped. Therefore idleWakeups can only really be used |
| 345 // to calculate diffs, or for processes that have not run long enough to |
| 346 // accumulate four billion context switches. |
| 347 ProcessData process_data; |
| 348 process_data.ProcessId = pi->ProcessId; |
| 349 process_data.CpuTime = pi->KernelTime + pi->UserTime; |
| 350 process_data.WorkingSetPrivateSize = pi->WorkingSetPrivateSize; |
| 351 for (ULONG thread_index = 0; thread_index < pi->NumberOfThreads; |
| 352 ++thread_index) { |
| 353 const SYSTEM_THREAD_INFORMATION* ti = &pi->Threads[thread_index]; |
| 354 if (ti->ClientId.UniqueProcess != pi->ProcessId) |
| 355 continue; |
| 356 |
| 357 ThreadData thread_data; |
| 358 thread_data.ThreadId = ti->ClientId.UniqueThread; |
| 359 thread_data.ContextSwitches = ti->ContextSwitchCount; |
| 360 process_data.Threads.push_back(thread_data); |
| 361 } |
| 362 |
| 363 SortThreads(&process_data.Threads); |
| 364 |
| 365 processes->push_back(process_data); |
| 366 } |
| 367 |
| 368 // Check for end of the list. |
| 369 if (!pi->NextEntryOffset) |
| 370 break; |
| 371 |
| 372 // Jump to the next entry. |
| 373 offset += pi->NextEntryOffset; |
| 374 } |
| 375 |
| 376 SortProcesses(processes); |
| 377 |
| 378 double time_delta = 0.0; |
| 379 |
| 380 if (previous_perf_counter_value.QuadPart != 0) { |
| 381 time_delta = static_cast<double>(perf_counter_value.QuadPart - |
| 382 previous_perf_counter_value.QuadPart) / |
| 383 perf_frequency.QuadPart; |
| 384 } |
| 385 previous_perf_counter_value = perf_counter_value; |
| 386 |
| 387 return time_delta; |
| 388 } |
| 389 |
| 390 void IdleWakeups::SortThreads(ThreadsVector* threads) { |
| 391 std::sort(threads->begin(), threads->end(), |
| 392 [](const ThreadData& lhs, const ThreadData& rhs) { |
| 393 return lhs.ThreadId < rhs.ThreadId; |
| 394 }); |
| 395 } |
| 396 |
| 397 void IdleWakeups::SortProcesses(ProcessesVector* processes) { |
| 398 std::sort(processes->begin(), processes->end(), |
| 399 [](const ProcessData& lhs, const ProcessData& rhs) { |
| 400 return lhs.ProcessId < rhs.ProcessId; |
| 401 }); |
| 402 } |
| 403 |
| 404 ULONG IdleWakeups::CountContextSwitches(const ProcessData& process_data) { |
| 405 ULONG context_switches = 0; |
| 406 |
| 407 for (const auto& thread_data : process_data.Threads) { |
| 408 context_switches += thread_data.ContextSwitches; |
| 409 } |
| 410 |
| 411 return context_switches; |
| 412 } |
| 413 |
| 414 ULONG IdleWakeups::DiffContextSwitches(const ProcessData& prev_process_data, |
| 415 const ProcessData& process_data) { |
| 416 ULONG context_switches = 0; |
| 417 size_t prev_index = 0; |
| 418 |
| 419 for (const auto& thread_data : process_data.Threads) { |
| 420 ULONG prev_context_switches = 0; |
| 421 |
| 422 for (; prev_index < prev_process_data.Threads.size(); ++prev_index) { |
| 423 const auto& prev_thread_data = prev_process_data.Threads[prev_index]; |
| 424 if (prev_thread_data.ThreadId == thread_data.ThreadId) { |
| 425 prev_context_switches = prev_thread_data.ContextSwitches; |
| 426 ++prev_index; |
| 427 break; |
| 428 } |
| 429 |
| 430 if (prev_thread_data.ThreadId > thread_data.ThreadId) |
| 431 break; |
| 432 } |
| 433 |
| 434 context_switches += thread_data.ContextSwitches - prev_context_switches; |
| 435 } |
| 436 |
| 437 return context_switches; |
| 438 } |
| 439 |
| 440 bool IdleWakeups::GetFinishedProcessCpuTime(const ProcessData& process, |
| 441 ULONGLONG* cpu_time) { |
| 442 HANDLE process_handle = GetProcessHandle(process); |
| 443 |
| 444 FILETIME creation_time, exit_time, kernel_time, user_time; |
| 445 if (GetProcessTimes(process_handle, &creation_time, &exit_time, &kernel_time, |
| 446 &user_time)) { |
| 447 ULARGE_INTEGER ul_kernel_time, ul_user_time; |
| 448 ul_kernel_time.LowPart = kernel_time.dwLowDateTime; |
| 449 ul_kernel_time.HighPart = kernel_time.dwHighDateTime; |
| 450 ul_user_time.LowPart = user_time.dwLowDateTime; |
| 451 ul_user_time.HighPart = user_time.dwHighDateTime; |
| 452 *cpu_time = ul_kernel_time.QuadPart + ul_user_time.QuadPart; |
| 453 return true; |
| 454 } |
| 455 |
| 456 *cpu_time = 0; |
| 457 return false; |
| 458 } |
| 459 |
| 460 Result IdleWakeups::DiffSnapshots(double time_delta, |
| 461 const ProcessesVector& prev_processes, |
| 462 const ProcessesVector& processes) { |
| 463 ULONG idle_wakeups_delta = 0; |
| 464 ULONGLONG cpu_usage_delta = 0; |
| 465 ULONGLONG total_working_set = 0; |
| 466 |
| 467 size_t prev_index = 0; |
| 468 |
| 469 for (const auto& process_data : processes) { |
| 470 const ProcessData* prev_process_data_to_diff = nullptr; |
| 471 ULONGLONG prev_process_cpu_time = 0; |
| 472 |
| 473 for (; prev_index < prev_processes.size(); ++prev_index) { |
| 474 const auto& prev_process_data = prev_processes[prev_index]; |
| 475 |
| 476 if (prev_process_data.ProcessId == process_data.ProcessId) { |
| 477 prev_process_data_to_diff = &prev_process_data; |
| 478 prev_process_cpu_time = prev_process_data.CpuTime; |
| 479 ++prev_index; |
| 480 break; |
| 481 } |
| 482 |
| 483 if (prev_process_data.ProcessId > process_data.ProcessId) |
| 484 break; |
| 485 |
| 486 // Prev process disappeared. |
| 487 ULONGLONG last_known_cpu_time; |
| 488 if (GetFinishedProcessCpuTime(prev_process_data, &last_known_cpu_time)) { |
| 489 cpu_usage_delta += last_known_cpu_time - prev_process_data.CpuTime; |
| 490 } |
| 491 CloseProcess(prev_process_data); |
| 492 } |
| 493 |
| 494 if (prev_process_data_to_diff) { |
| 495 idle_wakeups_delta += |
| 496 DiffContextSwitches(*prev_process_data_to_diff, process_data); |
| 497 } else { |
| 498 // New process that we haven't seen before. |
| 499 OpenProcess(process_data); |
| 500 idle_wakeups_delta += CountContextSwitches(process_data); |
| 501 } |
| 502 |
| 503 cpu_usage_delta += process_data.CpuTime - prev_process_cpu_time; |
| 504 total_working_set += process_data.WorkingSetPrivateSize / 1024; |
| 505 } |
| 506 |
| 507 Result result; |
| 508 result.IdleWakeupsPerSec = |
| 509 static_cast<ULONG>(idle_wakeups_delta / time_delta); |
| 510 // brucedawson: Don't divide by number of processors so that all numbers are |
| 511 // percentage of a core |
| 512 // result.CpuUsage = (double)cpu_usage_delta * 100 / (time_delta * 10000000 * |
| 513 // NumberOfprocessors()); |
| 514 result.CpuUsage = (double)cpu_usage_delta * 100 / (time_delta * 10000000); |
| 515 result.WorkingSet = total_working_set; |
| 516 |
| 517 return result; |
| 518 } |
| 519 |
| 520 HANDLE ctrl_c_pressed = NULL; |
| 521 |
| 522 BOOL WINAPI HandlerFunction(DWORD ctrl_type) { |
| 523 if (ctrl_type == CTRL_C_EVENT) { |
| 524 printf("Ctrl+C pressed...\n"); |
| 525 SetEvent(ctrl_c_pressed); |
| 526 return TRUE; |
| 527 } |
| 528 |
| 529 return FALSE; |
| 530 } |
| 531 |
| 532 const DWORD sleep_time_sec = 2; |
| 533 |
| 534 void PrintHeader() { |
| 535 printf( |
| 536 "------------------------------------------------------------------------" |
| 537 "----------\n"); |
| 538 printf( |
| 539 " Context switches/sec CPU usage Working set " |
| 540 " Power\n"); |
| 541 printf( |
| 542 "------------------------------------------------------------------------" |
| 543 "----------\n"); |
| 544 } |
| 545 |
| 546 #define RESULT_FORMAT_STRING " %20lu %8.2f%c %6.2f MiB %4.2f W\n" |
| 547 |
| 548 int wmain(int argc, wchar_t* argv[]) { |
| 549 ctrl_c_pressed = CreateEvent(NULL, FALSE, FALSE, NULL); |
| 550 SetConsoleCtrlHandler(HandlerFunction, TRUE); |
| 551 |
| 552 PowerSampler power_sampler; |
| 553 |
| 554 IdleWakeups the_app(argc > 1 ? argv[1] : L"chrome.exe"); |
| 555 |
| 556 ProcessesVector prev_chrome_processes; |
| 557 ProcessesVector chrome_processes; |
| 558 |
| 559 // Take the initial snapshot. |
| 560 the_app.TakeSnapshot(&chrome_processes); |
| 561 the_app.OpenProcesses(chrome_processes); |
| 562 |
| 563 ULONG cumulative_idle_wakeups_per_sec = 0; |
| 564 double cumulative_cpu_usage = 0.0; |
| 565 ULONGLONG cumulative_working_set = 0; |
| 566 double cumulative_energy = 0.0; |
| 567 |
| 568 ResultVector results; |
| 569 |
| 570 printf("Capturing perf data for all processes matching %ls\n", |
| 571 the_app.target_process_name_filter()); |
| 572 |
| 573 PrintHeader(); |
| 574 |
| 575 for (;;) { |
| 576 prev_chrome_processes.swap(chrome_processes); |
| 577 chrome_processes.clear(); |
| 578 |
| 579 if (WaitForSingleObject(ctrl_c_pressed, sleep_time_sec * 1000) == |
| 580 WAIT_OBJECT_0) |
| 581 break; |
| 582 |
| 583 double time_delta = the_app.TakeSnapshot(&chrome_processes); |
| 584 |
| 585 Result result = the_app.DiffSnapshots(time_delta, prev_chrome_processes, |
| 586 chrome_processes); |
| 587 power_sampler.SampleCPUPowerState(); |
| 588 result.Power = power_sampler.get_power(L"Processor"); |
| 589 |
| 590 printf("%9u processes" RESULT_FORMAT_STRING, (DWORD)chrome_processes.size(), |
| 591 result.IdleWakeupsPerSec, result.CpuUsage, '%', |
| 592 result.WorkingSet / 1024.0, result.Power); |
| 593 |
| 594 cumulative_idle_wakeups_per_sec += result.IdleWakeupsPerSec; |
| 595 cumulative_cpu_usage += result.CpuUsage; |
| 596 cumulative_working_set += result.WorkingSet; |
| 597 cumulative_energy += result.Power; |
| 598 |
| 599 results.push_back(result); |
| 600 } |
| 601 |
| 602 CloseHandle(ctrl_c_pressed); |
| 603 |
| 604 ULONG sample_count = (ULONG)results.size(); |
| 605 if (sample_count == 0) |
| 606 return 0; |
| 607 |
| 608 PrintHeader(); |
| 609 |
| 610 printf(" Average" RESULT_FORMAT_STRING, |
| 611 cumulative_idle_wakeups_per_sec / sample_count, |
| 612 cumulative_cpu_usage / sample_count, '%', |
| 613 (cumulative_working_set / 1024.0) / sample_count, |
| 614 cumulative_energy / sample_count); |
| 615 |
| 616 Result median_result; |
| 617 |
| 618 median_result.IdleWakeupsPerSec = |
| 619 GetMedian<ULONG>(&results, GetIdleWakeupsPerSec); |
| 620 median_result.CpuUsage = GetMedian<double>(&results, GetCpuUsage); |
| 621 median_result.WorkingSet = GetMedian<ULONGLONG>(&results, GetWorkingSet); |
| 622 median_result.Power = GetMedian<double>(&results, GetPower); |
| 623 |
| 624 printf(" Median" RESULT_FORMAT_STRING, |
| 625 median_result.IdleWakeupsPerSec, median_result.CpuUsage, '%', |
| 626 median_result.WorkingSet / 1024.0, median_result.Power); |
| 627 |
| 628 return 0; |
| 629 } |
OLD | NEW |