Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/task_manager/sampling/shared_sampler.h" | 5 #include "chrome/browser/task_manager/sampling/shared_sampler.h" |
| 6 | 6 |
| 7 #include <windows.h> | 7 #include <windows.h> |
| 8 #include <winternl.h> | 8 #include <winternl.h> |
| 9 | 9 |
| 10 #include <algorithm> | 10 #include <algorithm> |
| 11 | 11 |
| 12 #include "base/bind.h" | 12 #include "base/bind.h" |
| 13 #include "base/bit_cast.h" | 13 #include "base/bit_cast.h" |
| 14 #include "base/command_line.h" | 14 #include "base/command_line.h" |
| 15 #include "base/path_service.h" | 15 #include "base/path_service.h" |
| 16 #include "base/time/time.h" | 16 #include "base/time/time.h" |
| 17 #include "chrome/browser/task_manager/sampling/shared_sampler_win_defines.h" | |
| 17 #include "chrome/browser/task_manager/task_manager_observer.h" | 18 #include "chrome/browser/task_manager/task_manager_observer.h" |
| 18 #include "chrome/common/chrome_constants.h" | 19 #include "chrome/common/chrome_constants.h" |
| 19 #include "content/public/browser/browser_thread.h" | 20 #include "content/public/browser/browser_thread.h" |
| 20 | 21 |
| 21 namespace task_manager { | 22 namespace task_manager { |
| 22 | 23 |
| 24 static SharedSampler::QuerySystemInformationForTest | |
| 25 g_query_system_information_for_test = nullptr; | |
| 26 | |
| 27 // static | |
| 28 void SharedSampler::SetQuerySystemInformationForTest( | |
| 29 QuerySystemInformationForTest query_system_information) { | |
| 30 g_query_system_information_for_test = query_system_information; | |
| 31 } | |
| 32 | |
| 23 namespace { | 33 namespace { |
| 24 | 34 |
| 25 // From <wdm.h> | |
| 26 typedef LONG KPRIORITY; | |
| 27 typedef LONG KWAIT_REASON; // Full definition is in wdm.h | |
| 28 | |
| 29 // From ntddk.h | |
| 30 typedef struct _VM_COUNTERS { | |
| 31 SIZE_T PeakVirtualSize; | |
| 32 SIZE_T VirtualSize; | |
| 33 ULONG PageFaultCount; | |
| 34 // Padding here in 64-bit | |
| 35 SIZE_T PeakWorkingSetSize; | |
| 36 SIZE_T WorkingSetSize; | |
| 37 SIZE_T QuotaPeakPagedPoolUsage; | |
| 38 SIZE_T QuotaPagedPoolUsage; | |
| 39 SIZE_T QuotaPeakNonPagedPoolUsage; | |
| 40 SIZE_T QuotaNonPagedPoolUsage; | |
| 41 SIZE_T PagefileUsage; | |
| 42 SIZE_T PeakPagefileUsage; | |
| 43 } VM_COUNTERS; | |
| 44 | |
| 45 // Two possibilities available from here: | |
| 46 // http://stackoverflow.com/questions/28858849/where-is-system-information-class -defined | |
| 47 | |
| 48 typedef enum _SYSTEM_INFORMATION_CLASS { | |
| 49 SystemProcessInformation = 5, // This is the number that we need. | |
| 50 } SYSTEM_INFORMATION_CLASS; | |
| 51 | |
| 52 // https://msdn.microsoft.com/en-us/library/gg750647.aspx?f=255&MSPPError=-21472 17396 | |
| 53 typedef struct { | |
| 54 HANDLE UniqueProcess; // Actually process ID | |
| 55 HANDLE UniqueThread; // Actually thread ID | |
| 56 } CLIENT_ID; | |
| 57 | |
| 58 // From http://alax.info/blog/1182, with corrections and modifications | |
| 59 // Originally from | |
| 60 // http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%2 0Functions%2FSystem%20Information%2FStructures%2FSYSTEM_THREAD.html | |
| 61 struct SYSTEM_THREAD_INFORMATION { | |
| 62 ULONGLONG KernelTime; | |
| 63 ULONGLONG UserTime; | |
| 64 ULONGLONG CreateTime; | |
| 65 ULONG WaitTime; | |
| 66 // Padding here in 64-bit | |
| 67 PVOID StartAddress; | |
| 68 CLIENT_ID ClientId; | |
| 69 KPRIORITY Priority; | |
| 70 LONG BasePriority; | |
| 71 ULONG ContextSwitchCount; | |
| 72 ULONG State; | |
| 73 KWAIT_REASON WaitReason; | |
| 74 }; | |
| 75 #if _M_X64 | |
| 76 static_assert(sizeof(SYSTEM_THREAD_INFORMATION) == 80, | |
| 77 "Structure size mismatch"); | |
| 78 #else | |
| 79 static_assert(sizeof(SYSTEM_THREAD_INFORMATION) == 64, | |
| 80 "Structure size mismatch"); | |
| 81 #endif | |
| 82 | |
| 83 // From http://alax.info/blog/1182, with corrections and modifications | |
| 84 // Originally from | |
| 85 // http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%2 0Functions%2FSystem%20Information%2FStructures%2FSYSTEM_THREAD.html | |
| 86 struct SYSTEM_PROCESS_INFORMATION { | |
| 87 ULONG NextEntryOffset; | |
| 88 ULONG NumberOfThreads; | |
| 89 // 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 | |
| 90 ULONGLONG WorkingSetPrivateSize; | |
| 91 ULONG HardFaultCount; | |
| 92 ULONG Reserved1; | |
| 93 ULONGLONG CycleTime; | |
| 94 ULONGLONG CreateTime; | |
| 95 ULONGLONG UserTime; | |
| 96 ULONGLONG KernelTime; | |
| 97 UNICODE_STRING ImageName; | |
| 98 KPRIORITY BasePriority; | |
| 99 HANDLE ProcessId; | |
| 100 HANDLE ParentProcessId; | |
| 101 ULONG HandleCount; | |
| 102 ULONG Reserved2[2]; | |
| 103 // Padding here in 64-bit | |
| 104 VM_COUNTERS VirtualMemoryCounters; | |
| 105 size_t Reserved3; | |
| 106 IO_COUNTERS IoCounters; | |
| 107 SYSTEM_THREAD_INFORMATION Threads[1]; | |
| 108 }; | |
| 109 #if _M_X64 | |
| 110 static_assert(sizeof(SYSTEM_PROCESS_INFORMATION) == 336, | |
| 111 "Structure size mismatch"); | |
| 112 #else | |
| 113 static_assert(sizeof(SYSTEM_PROCESS_INFORMATION) == 248, | |
| 114 "Structure size mismatch"); | |
| 115 #endif | |
| 116 | |
| 117 // ntstatus.h conflicts with windows.h so define this locally. | |
| 118 #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) | |
| 119 #define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L) | |
| 120 #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) | |
| 121 | |
| 122 // Simple memory buffer wrapper for passing the data out of | 35 // Simple memory buffer wrapper for passing the data out of |
| 123 // QuerySystemProcessInformation. | 36 // QuerySystemProcessInformation. |
| 124 class ByteBuffer { | 37 class ByteBuffer { |
| 125 public: | 38 public: |
| 126 explicit ByteBuffer(size_t capacity) | 39 explicit ByteBuffer(size_t capacity) |
| 127 : size_(0), capacity_(0) { | 40 : size_(0), capacity_(0) { |
| 128 if (capacity > 0) | 41 if (capacity > 0) |
| 129 grow(capacity); | 42 grow(capacity); |
| 130 } | 43 } |
| 131 | 44 |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 151 private: | 64 private: |
| 152 std::unique_ptr<BYTE[]> data_; | 65 std::unique_ptr<BYTE[]> data_; |
| 153 size_t size_; | 66 size_t size_; |
| 154 size_t capacity_; | 67 size_t capacity_; |
| 155 | 68 |
| 156 DISALLOW_COPY_AND_ASSIGN(ByteBuffer); | 69 DISALLOW_COPY_AND_ASSIGN(ByteBuffer); |
| 157 }; | 70 }; |
| 158 | 71 |
| 159 // Wrapper for NtQuerySystemProcessInformation with buffer reallocation logic. | 72 // Wrapper for NtQuerySystemProcessInformation with buffer reallocation logic. |
| 160 bool QuerySystemProcessInformation(ByteBuffer* buffer) { | 73 bool QuerySystemProcessInformation(ByteBuffer* buffer) { |
| 161 typedef NTSTATUS(WINAPI * NTQUERYSYSTEMINFORMATION)( | |
| 162 SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, | |
| 163 ULONG SystemInformationLength, PULONG ReturnLength); | |
| 164 | |
| 165 HMODULE ntdll = ::GetModuleHandle(L"ntdll.dll"); | 74 HMODULE ntdll = ::GetModuleHandle(L"ntdll.dll"); |
| 166 if (!ntdll) { | 75 if (!ntdll) { |
| 167 NOTREACHED(); | 76 NOTREACHED(); |
| 168 return false; | 77 return false; |
| 169 } | 78 } |
| 170 | 79 |
| 171 NTQUERYSYSTEMINFORMATION nt_query_system_information_ptr = | 80 NTQUERYSYSTEMINFORMATION nt_query_system_information_ptr = |
| 172 reinterpret_cast<NTQUERYSYSTEMINFORMATION>( | 81 reinterpret_cast<NTQUERYSYSTEMINFORMATION>( |
| 173 ::GetProcAddress(ntdll, "NtQuerySystemInformation")); | 82 ::GetProcAddress(ntdll, "NtQuerySystemInformation")); |
| 174 if (!nt_query_system_information_ptr) { | 83 if (!nt_query_system_information_ptr) { |
| 175 NOTREACHED(); | 84 NOTREACHED(); |
| 176 return false; | 85 return false; |
| 177 } | 86 } |
| 178 | 87 |
| 179 NTSTATUS result; | 88 NTSTATUS result; |
| 180 | 89 |
| 181 // There is a potential race condition between growing the buffer and new | 90 // There is a potential race condition between growing the buffer and new |
| 182 // processes being created. Try a few times before giving up. | 91 // processes being created. Try a few times before giving up. |
| 183 for (int i = 0; i < 10; i++) { | 92 for (int i = 0; i < 10; i++) { |
| 184 ULONG data_size = 0; | 93 ULONG data_size = 0; |
| 185 ULONG buffer_size = static_cast<ULONG>(buffer->capacity()); | 94 ULONG buffer_size = static_cast<ULONG>(buffer->capacity()); |
| 186 result = nt_query_system_information_ptr( | 95 |
| 187 SystemProcessInformation, | 96 if (g_query_system_information_for_test) { |
|
stanisc
2017/01/30 21:51:14
I wonder if it could be a bit cleaner to hook the
Wez
2017/01/31 03:36:24
It's tricky to arrange that given that we don't wa
| |
| 188 buffer->data(), buffer_size, &data_size); | 97 data_size = |
| 98 g_query_system_information_for_test(buffer->data(), buffer_size); | |
| 99 result = | |
| 100 (data_size > buffer_size) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS; | |
| 101 } else { | |
| 102 result = nt_query_system_information_ptr( | |
| 103 SystemProcessInformation, buffer->data(), buffer_size, &data_size); | |
| 104 } | |
| 189 | 105 |
| 190 if (result == STATUS_SUCCESS) { | 106 if (result == STATUS_SUCCESS) { |
| 191 buffer->set_size(data_size); | 107 buffer->set_size(data_size); |
| 192 break; | 108 break; |
| 193 } | 109 } |
| 194 | 110 |
| 195 if (result == STATUS_INFO_LENGTH_MISMATCH || | 111 if (result == STATUS_INFO_LENGTH_MISMATCH || |
| 196 result == STATUS_BUFFER_TOO_SMALL) { | 112 result == STATUS_BUFFER_TOO_SMALL) { |
| 197 // Insufficient buffer. Grow to the returned |data_size| plus 10% extra | 113 // Insufficient buffer. Grow to the returned |data_size| plus 10% extra |
| 198 // to avoid frequent reallocations and try again. | 114 // to avoid frequent reallocations and try again. |
| (...skipping 304 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 503 if (IsSupportedImageName(pi->ImageName.Buffer)) { | 419 if (IsSupportedImageName(pi->ImageName.Buffer)) { |
| 504 // Collect enough data to be able to do a diff between two snapshots. | 420 // Collect enough data to be able to do a diff between two snapshots. |
| 505 // Some threads might stop or new threads might be created between two | 421 // Some threads might stop or new threads might be created between two |
| 506 // snapshots. If a thread with a large number of context switches gets | 422 // snapshots. If a thread with a large number of context switches gets |
| 507 // terminated the total number of context switches for the process might | 423 // terminated the total number of context switches for the process might |
| 508 // go down and the delta would be negative. | 424 // go down and the delta would be negative. |
| 509 // To avoid that we need to compare thread IDs between two snapshots and | 425 // To avoid that we need to compare thread IDs between two snapshots and |
| 510 // not count context switches for threads that are missing in the most | 426 // not count context switches for threads that are missing in the most |
| 511 // recent snapshot. | 427 // recent snapshot. |
| 512 ProcessData process_data; | 428 ProcessData process_data; |
| 513 | |
| 514 process_data.physical_bytes = | 429 process_data.physical_bytes = |
| 515 static_cast<int64_t>(pi->WorkingSetPrivateSize); | 430 static_cast<int64_t>(pi->WorkingSetPrivateSize); |
| 516 | |
| 517 process_data.start_time = ConvertTicksToTime(pi->CreateTime); | 431 process_data.start_time = ConvertTicksToTime(pi->CreateTime); |
| 518 | |
| 519 process_data.cpu_time = | 432 process_data.cpu_time = |
| 520 ConvertTicksToTimeDelta(pi->KernelTime + pi->UserTime); | 433 ConvertTicksToTimeDelta(pi->KernelTime + pi->UserTime); |
| 521 | 434 |
| 522 // Iterate over threads and store each thread's ID and number of context | 435 // Iterate over threads and store each thread's ID and number of context |
| 523 // switches. | 436 // switches. |
| 524 for (ULONG thread_index = 0; thread_index < pi->NumberOfThreads; | 437 for (ULONG thread_index = 0; thread_index < pi->NumberOfThreads; |
| 525 ++thread_index) { | 438 ++thread_index) { |
| 526 const SYSTEM_THREAD_INFORMATION* ti = &pi->Threads[thread_index]; | 439 const SYSTEM_THREAD_INFORMATION* ti = &pi->Threads[thread_index]; |
| 527 if (ti->ClientId.UniqueProcess != pi->ProcessId) | 440 if (ti->ClientId.UniqueProcess != pi->ProcessId) |
| 528 continue; | 441 continue; |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 682 callback_entry.second.on_cpu_time.Run(cpu_time); | 595 callback_entry.second.on_cpu_time.Run(cpu_time); |
| 683 } | 596 } |
| 684 } | 597 } |
| 685 | 598 |
| 686 // Reset refresh_results_ to trigger RefreshOnWorkerThread next time Refresh | 599 // Reset refresh_results_ to trigger RefreshOnWorkerThread next time Refresh |
| 687 // is called. | 600 // is called. |
| 688 refresh_flags_ = 0; | 601 refresh_flags_ = 0; |
| 689 } | 602 } |
| 690 | 603 |
| 691 } // namespace task_manager | 604 } // namespace task_manager |
| OLD | NEW |