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

Side by Side Diff: base/process/private_working_set_snapshot_win.cc

Issue 1181263005: Make task manager memory data more efficient and meaningful. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add missing include Created 5 years, 6 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
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698