OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
ncarter (slow)
2015/06/26 21:46:16
2015, no (c)
brucedawson
2015/06/27 00:13:05
Done.
| |
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) | |
ncarter (slow)
2015/06/26 21:46:16
This isn't needed. Since the file ends in _win, it
brucedawson
2015/06/27 00:13:05
The reason for the #if defined was to make the pre
| |
10 #include <pdh.h> | |
11 #include <pdhmsg.h> | |
12 #include <windows.h> | |
13 #endif | |
14 | |
15 #include "base/win/windows_version.h" | |
16 | |
17 namespace base { | |
18 | |
19 PrivateWorkingSetSnapshot::PrivateWorkingSetSnapshot() | |
20 : query_handle_(nullptr) | |
21 { | |
ncarter (slow)
2015/06/26 21:46:16
"{" always goes on the previous line (here and els
brucedawson
2015/06/27 00:13:05
Huh. Clearly I *know* that rule, and yet...
Okay,
| |
22 // The Pdh APIs are supported on Windows XP, but the "Working Set - Private" | |
23 // counter that PrivateWorkingSetSnapshot depends on is not defined until | |
24 // Windows Vista. Early-out to avoid wasted effort. All queries will return | |
25 // zero and will have to use the fallback calculations. | |
26 if (base::win::GetVersion() < base::win::VERSION_VISTA) | |
27 return; | |
28 | |
29 // Create a Pdh query | |
30 if (PdhOpenQuery(NULL, NULL, &query_handle_) != ERROR_SUCCESS) | |
31 { | |
32 query_handle_ = nullptr; | |
33 return; | |
34 } | |
35 } | |
36 | |
37 PrivateWorkingSetSnapshot::~PrivateWorkingSetSnapshot() | |
38 { | |
39 if (query_handle_) | |
40 PdhCloseQuery(query_handle_); | |
41 } | |
42 | |
43 void PrivateWorkingSetSnapshot::AddToMonitorList( | |
44 const std::string& process_name) | |
45 { | |
46 // Create the magic strings that will return a list of process IDs and a list | |
47 // of private working sets. The 'process_name' variable should be something | |
48 // like "chrome". The '*' character indicates that we want records for all | |
49 // processes whose names start with process_name - all chrome processes, but | |
50 // also all 'chrome_editor.exe' processes or other matching names. The excess | |
51 // information is unavoidable but harmless. | |
52 std::string process_id_query = "\\Process(" + process_name + "*)\\ID Process"; | |
53 std::string private_ws_query = "\\Process(" + process_name + | |
54 "*)\\Working Set - Private"; | |
55 | |
56 // Add the two counters to the query. | |
57 PdhCounterPair new_counters; | |
58 if (PdhAddCounterA(query_handle_, process_id_query.c_str(), | |
59 NULL, &new_counters.process_id_handle) != ERROR_SUCCESS) { | |
ncarter (slow)
2015/06/26 21:46:16
Cases like this should get an additional 4 lines o
brucedawson
2015/06/27 00:13:05
"git cl format" did it somewhat differently from t
ncarter (slow)
2015/06/29 20:03:19
As a rule you won't see me disagreeing with git cl
| |
60 return; | |
61 } | |
62 | |
63 // If adding the second counter fails then we should remove the first one. | |
64 if (PdhAddCounterA(query_handle_, private_ws_query.c_str(), | |
65 NULL, &new_counters.private_ws_handle) != ERROR_SUCCESS) { | |
66 PdhRemoveCounter(new_counters.process_id_handle); | |
67 } | |
68 | |
69 // Record the pair of counter query handles so that we can query them later. | |
70 counter_pairs_.push_back(new_counters); | |
71 } | |
72 | |
73 // Query the system for working-set information for all monitored processes | |
74 // and update the results cache. This function may take a few ms to run. | |
75 // The time it takes seems to be independent of the number of processes it | |
76 // retrieves data for. This makes it faster than using QueryWorkingSet as soon | |
77 // as the process count exceeds two or three. | |
ncarter (slow)
2015/06/26 21:46:16
Don't duplicate comments between the .h and the .c
brucedawson
2015/06/27 00:13:05
Done.
| |
78 void PrivateWorkingSetSnapshot::Sample() | |
79 { | |
80 if (!query_handle_ || counter_pairs_.empty()) | |
81 return; | |
82 | |
83 // Destroy all previous data. | |
84 records_.resize(0); | |
85 // Record the requested data into PDH's internal buffers. | |
86 if (PdhCollectQueryData(query_handle_) != ERROR_SUCCESS) | |
87 return; | |
88 | |
89 for (auto& counter_pair : counter_pairs_) { | |
90 // Find out how much space is required for the two counter arrays. | |
91 // A return code of PDH_MORE_DATA indicates that we should call again with | |
92 // the buffer size returned. | |
93 DWORD buffer_size1 = 0; | |
94 DWORD item_count1 = 0; | |
95 // Process IDs should be retrieved as PDH_FMT_LONG | |
96 if (PdhGetFormattedCounterArray(counter_pair.process_id_handle, | |
97 PDH_FMT_LONG, &buffer_size1, &item_count1, nullptr) != PDH_MORE_DATA) | |
98 continue; | |
99 DWORD buffer_size2 = 0; | |
100 DWORD item_count2 = 0; | |
101 // Working sets should be retrieved as PDH_FMT_LARGE (LONGLONG) | |
102 if (PdhGetFormattedCounterArray(counter_pair.private_ws_handle, | |
ncarter (slow)
2015/06/26 21:46:15
Optimization idea: since we bail if the sizes/coun
brucedawson
2015/06/27 00:13:05
The calls to PdhGetFormattedCounterArray are very
ncarter (slow)
2015/06/29 20:03:19
I'm glad you caught this. Thanks for pointing it o
| |
103 PDH_FMT_LARGE, &buffer_size2, &item_count2, nullptr) != PDH_MORE_DATA) | |
104 continue; | |
105 | |
106 // It is not clear whether Pdh guarantees that the two counters in the same | |
107 // query will execute atomically - if they will see the same set of | |
108 // processes. If they do not then the correspondence between "ID Process" | |
109 // and "Working Set - Private" is lost and we have to discard these results. | |
110 // In testing these values have always matched. If this check fails then | |
111 // the old per-process memory calculations will be used instead. | |
112 if (buffer_size1 != buffer_size2 && item_count1 != item_count2) | |
113 continue; | |
114 | |
115 // Allocate enough space for the results of both queries. | |
116 std::vector<char> buffer(buffer_size1 * 2); | |
117 // Retrieve the process ID data. | |
118 auto process_id_data = reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*> | |
119 (&buffer[0]); | |
ncarter (slow)
2015/06/26 21:46:16
Apparently (I just learned this) C++11 has vector:
brucedawson
2015/06/27 00:13:05
I started changing to .data() but then decided to
| |
120 if (PdhGetFormattedCounterArray(counter_pair.process_id_handle, | |
121 PDH_FMT_LONG, &buffer_size1, &item_count1, process_id_data) | |
122 != ERROR_SUCCESS) | |
123 continue; | |
124 // Retrieve the private working set data. | |
125 auto private_ws_data = reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*> | |
126 (&buffer[buffer_size1]); | |
127 if (PdhGetFormattedCounterArray(counter_pair.private_ws_handle, | |
128 PDH_FMT_LARGE, &buffer_size1, &item_count1, private_ws_data) | |
129 != ERROR_SUCCESS) | |
130 continue; | |
131 | |
132 // Make room for the new set of records. | |
133 size_t start_offset = records_.size(); | |
134 records_.resize(start_offset + item_count1); | |
135 | |
136 for (DWORD i = 0; i < item_count1; ++i) { | |
137 records_[start_offset + i].process_id = | |
138 process_id_data[i].FmtValue.longValue; | |
139 // Integer overflow can happen here if a 32-bit process is monitoring a | |
140 // 64-bit process. Pdh always returns working set in a 64-bit LONGLONG so | |
141 // we set the result to the min of that and the maximum size_t value. | |
142 LONGLONG max_size_t = std::numeric_limits<size_t>::max(); | |
143 records_[start_offset + i].private_ws = std::min(max_size_t, | |
ncarter (slow)
2015/06/26 21:46:16
Could this be a saturated_cast<size_t>(private_ws_
brucedawson
2015/06/27 00:13:05
Ooh -- much better.
| |
144 private_ws_data[i].FmtValue.largeValue); | |
145 } | |
146 } | |
147 | |
148 // The results will include all processes that match the passed in name, | |
149 // regardless of whether they are spawned by the calling process. | |
150 // The results must be sorted by process ID for efficient lookup. | |
151 std::sort(records_.begin(), records_.end()); | |
152 } | |
153 | |
154 // Ask for the working set for a specific process, from the most recent call | |
155 // to Sample. If no data is available then zero will be returned. The result | |
156 // is in bytes. | |
ncarter (slow)
2015/06/26 21:46:16
This comment also is a duplicate of the one in the
brucedawson
2015/06/27 00:13:05
Done.
| |
157 size_t PrivateWorkingSetSnapshot::GetPrivateWorkingSet( | |
158 base::ProcessId process_id) const | |
159 { | |
160 // Do a binary search for the requested process ID and return the working set | |
161 // if found. | |
162 auto p = std::lower_bound(records_.begin(), records_.end(), process_id); | |
163 if (p != records_.end() && p->process_id == process_id) | |
164 return p->private_ws; | |
165 | |
166 return 0; | |
167 } | |
168 | |
169 } // namespace base | |
OLD | NEW |