Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1254)

Side by Side Diff: chrome/browser/task_manager/sampling/shared_sampler_win.cc

Issue 2657393003: Add unit-test for SharedSampler handling of zero-thread processes. (Closed)
Patch Set: Working Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698