| 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 |
| 22 // ntstatus.h conflicts with windows.h so define this locally. |
| 23 #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) |
| 24 #define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L) |
| 25 #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) |
| 26 |
| 21 namespace task_manager { | 27 namespace task_manager { |
| 22 | 28 |
| 29 static SharedSampler::QuerySystemInformationForTest |
| 30 g_query_system_information_for_test = nullptr; |
| 31 |
| 32 // static |
| 33 void SharedSampler::SetQuerySystemInformationForTest( |
| 34 QuerySystemInformationForTest query_system_information) { |
| 35 g_query_system_information_for_test = query_system_information; |
| 36 } |
| 37 |
| 23 namespace { | 38 namespace { |
| 24 | 39 |
| 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 | 40 // Simple memory buffer wrapper for passing the data out of |
| 123 // QuerySystemProcessInformation. | 41 // QuerySystemProcessInformation. |
| 124 class ByteBuffer { | 42 class ByteBuffer { |
| 125 public: | 43 public: |
| 126 explicit ByteBuffer(size_t capacity) | 44 explicit ByteBuffer(size_t capacity) |
| 127 : size_(0), capacity_(0) { | 45 : size_(0), capacity_(0) { |
| 128 if (capacity > 0) | 46 if (capacity > 0) |
| 129 grow(capacity); | 47 grow(capacity); |
| 130 } | 48 } |
| 131 | 49 |
| (...skipping 19 matching lines...) Expand all Loading... |
| 151 private: | 69 private: |
| 152 std::unique_ptr<BYTE[]> data_; | 70 std::unique_ptr<BYTE[]> data_; |
| 153 size_t size_; | 71 size_t size_; |
| 154 size_t capacity_; | 72 size_t capacity_; |
| 155 | 73 |
| 156 DISALLOW_COPY_AND_ASSIGN(ByteBuffer); | 74 DISALLOW_COPY_AND_ASSIGN(ByteBuffer); |
| 157 }; | 75 }; |
| 158 | 76 |
| 159 // Wrapper for NtQuerySystemProcessInformation with buffer reallocation logic. | 77 // Wrapper for NtQuerySystemProcessInformation with buffer reallocation logic. |
| 160 bool QuerySystemProcessInformation(ByteBuffer* buffer) { | 78 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"); | 79 HMODULE ntdll = ::GetModuleHandle(L"ntdll.dll"); |
| 166 if (!ntdll) { | 80 if (!ntdll) { |
| 167 NOTREACHED(); | 81 NOTREACHED(); |
| 168 return false; | 82 return false; |
| 169 } | 83 } |
| 170 | 84 |
| 171 NTQUERYSYSTEMINFORMATION nt_query_system_information_ptr = | 85 NTQUERYSYSTEMINFORMATION nt_query_system_information_ptr = |
| 172 reinterpret_cast<NTQUERYSYSTEMINFORMATION>( | 86 reinterpret_cast<NTQUERYSYSTEMINFORMATION>( |
| 173 ::GetProcAddress(ntdll, "NtQuerySystemInformation")); | 87 ::GetProcAddress(ntdll, "NtQuerySystemInformation")); |
| 174 if (!nt_query_system_information_ptr) { | 88 if (!nt_query_system_information_ptr) { |
| 175 NOTREACHED(); | 89 NOTREACHED(); |
| 176 return false; | 90 return false; |
| 177 } | 91 } |
| 178 | 92 |
| 179 NTSTATUS result; | 93 NTSTATUS result; |
| 180 | 94 |
| 181 // There is a potential race condition between growing the buffer and new | 95 // There is a potential race condition between growing the buffer and new |
| 182 // processes being created. Try a few times before giving up. | 96 // processes being created. Try a few times before giving up. |
| 183 for (int i = 0; i < 10; i++) { | 97 for (int i = 0; i < 10; i++) { |
| 184 ULONG data_size = 0; | 98 ULONG data_size = 0; |
| 185 ULONG buffer_size = static_cast<ULONG>(buffer->capacity()); | 99 ULONG buffer_size = static_cast<ULONG>(buffer->capacity()); |
| 186 result = nt_query_system_information_ptr( | 100 |
| 187 SystemProcessInformation, | 101 if (g_query_system_information_for_test) { |
| 188 buffer->data(), buffer_size, &data_size); | 102 data_size = |
| 103 g_query_system_information_for_test(buffer->data(), buffer_size); |
| 104 result = |
| 105 (data_size > buffer_size) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS; |
| 106 } else { |
| 107 result = nt_query_system_information_ptr( |
| 108 SystemProcessInformation, buffer->data(), buffer_size, &data_size); |
| 109 } |
| 189 | 110 |
| 190 if (result == STATUS_SUCCESS) { | 111 if (result == STATUS_SUCCESS) { |
| 191 buffer->set_size(data_size); | 112 buffer->set_size(data_size); |
| 192 break; | 113 break; |
| 193 } | 114 } |
| 194 | 115 |
| 195 if (result == STATUS_INFO_LENGTH_MISMATCH || | 116 if (result == STATUS_INFO_LENGTH_MISMATCH || |
| 196 result == STATUS_BUFFER_TOO_SMALL) { | 117 result == STATUS_BUFFER_TOO_SMALL) { |
| 197 // Insufficient buffer. Grow to the returned |data_size| plus 10% extra | 118 // Insufficient buffer. Grow to the returned |data_size| plus 10% extra |
| 198 // to avoid frequent reallocations and try again. | 119 // 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)) { | 424 if (IsSupportedImageName(pi->ImageName.Buffer)) { |
| 504 // Collect enough data to be able to do a diff between two snapshots. | 425 // 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 | 426 // 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 | 427 // snapshots. If a thread with a large number of context switches gets |
| 507 // terminated the total number of context switches for the process might | 428 // terminated the total number of context switches for the process might |
| 508 // go down and the delta would be negative. | 429 // go down and the delta would be negative. |
| 509 // To avoid that we need to compare thread IDs between two snapshots and | 430 // 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 | 431 // not count context switches for threads that are missing in the most |
| 511 // recent snapshot. | 432 // recent snapshot. |
| 512 ProcessData process_data; | 433 ProcessData process_data; |
| 513 | |
| 514 process_data.physical_bytes = | 434 process_data.physical_bytes = |
| 515 static_cast<int64_t>(pi->WorkingSetPrivateSize); | 435 static_cast<int64_t>(pi->WorkingSetPrivateSize); |
| 516 | |
| 517 process_data.start_time = ConvertTicksToTime(pi->CreateTime); | 436 process_data.start_time = ConvertTicksToTime(pi->CreateTime); |
| 518 | |
| 519 process_data.cpu_time = | 437 process_data.cpu_time = |
| 520 ConvertTicksToTimeDelta(pi->KernelTime + pi->UserTime); | 438 ConvertTicksToTimeDelta(pi->KernelTime + pi->UserTime); |
| 521 | 439 |
| 522 // Iterate over threads and store each thread's ID and number of context | 440 // Iterate over threads and store each thread's ID and number of context |
| 523 // switches. | 441 // switches. |
| 524 for (ULONG thread_index = 0; thread_index < pi->NumberOfThreads; | 442 for (ULONG thread_index = 0; thread_index < pi->NumberOfThreads; |
| 525 ++thread_index) { | 443 ++thread_index) { |
| 526 const SYSTEM_THREAD_INFORMATION* ti = &pi->Threads[thread_index]; | 444 const SYSTEM_THREAD_INFORMATION* ti = &pi->Threads[thread_index]; |
| 527 if (ti->ClientId.UniqueProcess != pi->ProcessId) | 445 if (ti->ClientId.UniqueProcess != pi->ProcessId) |
| 528 continue; | 446 continue; |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 682 callback_entry.second.on_cpu_time.Run(cpu_time); | 600 callback_entry.second.on_cpu_time.Run(cpu_time); |
| 683 } | 601 } |
| 684 } | 602 } |
| 685 | 603 |
| 686 // Reset refresh_results_ to trigger RefreshOnWorkerThread next time Refresh | 604 // Reset refresh_results_ to trigger RefreshOnWorkerThread next time Refresh |
| 687 // is called. | 605 // is called. |
| 688 refresh_flags_ = 0; | 606 refresh_flags_ = 0; |
| 689 } | 607 } |
| 690 | 608 |
| 691 } // namespace task_manager | 609 } // namespace task_manager |
| OLD | NEW |