OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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 "base/process/private_working_set_snapshot.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #if defined(OS_WIN) | |
10 #include <pdh.h> | |
11 #include <pdhmsg.h> | |
12 #include <windows.h> | |
13 #endif | |
ncarter (slow)
2015/06/29 20:03:19
I'm fine For questions like this the best thing to
brucedawson
2015/06/29 22:09:52
I guess if I think of Windows header files as bein
| |
14 | |
15 #include "base/numerics/safe_conversions.h" | |
16 #include "base/win/windows_version.h" | |
17 | |
18 namespace base { | |
19 | |
20 PrivateWorkingSetSnapshot::PrivateWorkingSetSnapshot() { | |
21 // The Pdh APIs are supported on Windows XP, but the "Working Set - Private" | |
22 // counter that PrivateWorkingSetSnapshot depends on is not defined until | |
23 // Windows Vista. Early-out to avoid wasted effort. All queries will return | |
24 // zero and will have to use the fallback calculations. | |
25 if (base::win::GetVersion() < base::win::VERSION_VISTA) | |
26 return; | |
27 | |
28 // Create a Pdh query | |
29 PDH_HQUERY query_handle; | |
30 if (PdhOpenQuery(NULL, NULL, &query_handle) != ERROR_SUCCESS) { | |
31 return; | |
32 } | |
33 | |
34 query_handle_.Set(query_handle); | |
35 } | |
36 | |
37 void PrivateWorkingSetSnapshot::AddToMonitorList( | |
38 const std::string& process_name) { | |
39 if (!query_handle_.IsValid()) | |
40 return; | |
41 | |
42 // Create the magic strings that will return a list of process IDs and a list | |
43 // of private working sets. The 'process_name' variable should be something | |
44 // like "chrome". The '*' character indicates that we want records for all | |
45 // processes whose names start with process_name - all chrome processes, but | |
46 // also all 'chrome_editor.exe' processes or other matching names. The excess | |
47 // information is unavoidable but harmless. | |
48 std::string process_id_query = "\\Process(" + process_name + "*)\\ID Process"; | |
49 std::string private_ws_query = | |
50 "\\Process(" + process_name + "*)\\Working Set - Private"; | |
51 | |
52 // Add the two counters to the query. | |
53 PdhCounterPair new_counters; | |
54 if (PdhAddCounterA(query_handle_.Get(), process_id_query.c_str(), NULL, | |
55 &new_counters.process_id_handle) != ERROR_SUCCESS) { | |
56 return; | |
57 } | |
58 | |
59 // If adding the second counter fails then we should remove the first one. | |
60 if (PdhAddCounterA(query_handle_.Get(), private_ws_query.c_str(), NULL, | |
61 &new_counters.private_ws_handle) != ERROR_SUCCESS) { | |
62 PdhRemoveCounter(new_counters.process_id_handle); | |
63 } | |
64 | |
65 // Record the pair of counter query handles so that we can query them later. | |
66 counter_pairs_.push_back(new_counters); | |
67 } | |
68 | |
69 void PrivateWorkingSetSnapshot::Sample() { | |
70 if (counter_pairs_.empty()) | |
71 return; | |
72 | |
73 // Destroy all previous data. | |
74 records_.resize(0); | |
75 // Record the requested data into PDH's internal buffers. | |
76 if (PdhCollectQueryData(query_handle_.Get()) != ERROR_SUCCESS) | |
77 return; | |
78 | |
79 for (auto& counter_pair : counter_pairs_) { | |
80 // Find out how much space is required for the two counter arrays. | |
81 // A return code of PDH_MORE_DATA indicates that we should call again with | |
82 // the buffer size returned. | |
83 DWORD buffer_size1 = 0; | |
84 DWORD item_count1 = 0; | |
85 // Process IDs should be retrieved as PDH_FMT_LONG | |
86 if (PdhGetFormattedCounterArray(counter_pair.process_id_handle, | |
87 PDH_FMT_LONG, &buffer_size1, &item_count1, | |
88 nullptr) != PDH_MORE_DATA) | |
89 continue; | |
90 | |
91 // Allocate enough space for the results of both queries. | |
92 std::vector<char> buffer(buffer_size1 * 2); | |
93 // Retrieve the process ID data. | |
94 auto process_id_data = | |
95 reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*>(&buffer[0]); | |
ncarter (slow)
2015/06/29 20:03:19
I'm fine leaving this as &buffer[0], but do we nee
brucedawson
2015/06/29 22:09:52
Good point. I meant to add a zero-size check to av
| |
96 DWORD buffer_size2 = buffer_size1; | |
97 DWORD item_count2 = item_count1; | |
98 if (PdhGetFormattedCounterArray(counter_pair.process_id_handle, | |
99 PDH_FMT_LONG, &buffer_size2, &item_count2, | |
100 process_id_data) != ERROR_SUCCESS) | |
101 continue; | |
102 // Make sure we retrieved exactly the amount of data we expected. | |
103 if (buffer_size2 != buffer_size1 || item_count2 != item_count1) | |
104 continue; | |
105 | |
106 // Retrieve the private working set data. | |
107 auto private_ws_data = | |
108 reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*>(&buffer[buffer_size1]); | |
109 if (PdhGetFormattedCounterArray(counter_pair.private_ws_handle, | |
110 PDH_FMT_LARGE, &buffer_size2, &item_count2, | |
111 private_ws_data) != ERROR_SUCCESS) | |
112 continue; | |
113 // Make sure the working-set data size/count matches the process id | |
114 // size/count - otherwise matching them up won't make sense. They do seem to | |
115 // always match. | |
116 if (buffer_size2 != buffer_size1 || item_count2 != item_count1) | |
117 continue; | |
118 | |
119 // Make room for the new set of records. | |
120 size_t start_offset = records_.size(); | |
121 records_.resize(start_offset + item_count1); | |
122 | |
123 for (DWORD i = 0; i < item_count1; ++i) { | |
124 records_[start_offset + i].process_id = | |
125 process_id_data[i].FmtValue.longValue; | |
126 // Integer overflow can happen here if a 32-bit process is monitoring a | |
127 // 64-bit process so we do a saturated_cast. | |
128 records_[start_offset + i].private_ws = | |
129 saturated_cast<size_t>(private_ws_data[i].FmtValue.largeValue); | |
130 } | |
131 } | |
132 | |
133 // The results will include all processes that match the passed in name, | |
134 // regardless of whether they are spawned by the calling process. | |
135 // The results must be sorted by process ID for efficient lookup. | |
136 std::sort(records_.begin(), records_.end()); | |
137 } | |
138 | |
139 size_t PrivateWorkingSetSnapshot::GetPrivateWorkingSet( | |
140 base::ProcessId process_id) const { | |
141 // Do a binary search for the requested process ID and return the working set | |
142 // if found. | |
143 auto p = std::lower_bound(records_.begin(), records_.end(), process_id); | |
144 if (p != records_.end() && p->process_id == process_id) | |
145 return p->private_ws; | |
146 | |
147 return 0; | |
148 } | |
149 | |
150 } // namespace base | |
OLD | NEW |