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

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

Issue 2178733002: Task manager should support Idle Wakeups on Windows (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Handling missing results in OnRefreshDone. Created 4 years, 4 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 2016 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 "chrome/browser/task_management/sampling/shared_sampler.h"
6
7 #include <windows.h>
8 #include <winternl.h>
9
10 #include <algorithm>
11
12 #include "base/bind.h"
13 #include "base/command_line.h"
14 #include "base/path_service.h"
15 #include "base/time/time.h"
16 #include "chrome/browser/task_management/task_manager_observer.h"
17 #include "chrome/common/chrome_constants.h"
18 #include "content/public/browser/browser_thread.h"
19
20 namespace task_management {
21
22 namespace {
23
24 // From <wdm.h>
25 typedef LONG KPRIORITY;
26 typedef LONG KWAIT_REASON; // Full definition is in wdm.h
27
28 // From ntddk.h
29 typedef struct _VM_COUNTERS {
30 SIZE_T PeakVirtualSize;
31 SIZE_T VirtualSize;
32 ULONG PageFaultCount;
33 // Padding here in 64-bit
34 SIZE_T PeakWorkingSetSize;
35 SIZE_T WorkingSetSize;
36 SIZE_T QuotaPeakPagedPoolUsage;
37 SIZE_T QuotaPagedPoolUsage;
38 SIZE_T QuotaPeakNonPagedPoolUsage;
39 SIZE_T QuotaNonPagedPoolUsage;
40 SIZE_T PagefileUsage;
41 SIZE_T PeakPagefileUsage;
42 } VM_COUNTERS;
43
44 // Two possibilities available from here:
45 // http://stackoverflow.com/questions/28858849/where-is-system-information-class -defined
46
47 typedef enum _SYSTEM_INFORMATION_CLASS {
48 SystemProcessInformation = 5, // This is the number that we need.
49 } SYSTEM_INFORMATION_CLASS;
50
51 // https://msdn.microsoft.com/en-us/library/gg750647.aspx?f=255&MSPPError=-21472 17396
52 typedef struct {
53 HANDLE UniqueProcess; // Actually process ID
54 HANDLE UniqueThread; // Actually thread ID
55 } CLIENT_ID;
56
57 // From http://alax.info/blog/1182, with corrections and modifications
58 // Originally from
59 // http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%2 0Functions%2FSystem%20Information%2FStructures%2FSYSTEM_THREAD.html
60 struct SYSTEM_THREAD_INFORMATION {
61 ULONGLONG KernelTime;
62 ULONGLONG UserTime;
63 ULONGLONG CreateTime;
64 ULONG WaitTime;
65 // Padding here in 64-bit
66 PVOID StartAddress;
67 CLIENT_ID ClientId;
68 KPRIORITY Priority;
69 LONG BasePriority;
70 ULONG ContextSwitchCount;
71 ULONG State;
72 KWAIT_REASON WaitReason;
73 };
74 #if _M_X64
75 static_assert(sizeof(SYSTEM_THREAD_INFORMATION) == 80,
76 "Structure size mismatch");
77 #else
78 static_assert(sizeof(SYSTEM_THREAD_INFORMATION) == 64,
79 "Structure size mismatch");
80 #endif
81
82 // From http://alax.info/blog/1182, with corrections and modifications
83 // Originally from
84 // http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%2 0Functions%2FSystem%20Information%2FStructures%2FSYSTEM_THREAD.html
ncarter (slow) 2016/08/03 22:28:55 Without objection, let's leave these definitions h
85 struct SYSTEM_PROCESS_INFORMATION {
brucedawson 2016/08/03 00:36:55 If this version isn't merged with other versions t
ncarter (slow) 2016/08/03 22:28:55 It's in an anonymous namespace, so I think there's
stanisc 2016/08/04 01:36:01 I thought ODR would be avoided by placing this int
86 ULONG NextEntryOffset;
87 ULONG NumberOfThreads;
88 // 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
89 ULONGLONG WorkingSetPrivateSize;
90 ULONG HardFaultCount;
91 ULONG Reserved1;
92 ULONGLONG CycleTime;
93 ULONGLONG CreateTime;
94 ULONGLONG UserTime;
95 ULONGLONG KernelTime;
96 UNICODE_STRING ImageName;
97 KPRIORITY BasePriority;
98 HANDLE ProcessId;
99 HANDLE ParentProcessId;
100 ULONG HandleCount;
101 ULONG Reserved2[2];
102 // Padding here in 64-bit
103 VM_COUNTERS VirtualMemoryCounters;
104 size_t Reserved3;
105 IO_COUNTERS IoCounters;
106 SYSTEM_THREAD_INFORMATION Threads[1];
107 };
108 #if _M_X64
109 static_assert(sizeof(SYSTEM_PROCESS_INFORMATION) == 336,
110 "Structure size mismatch");
111 #else
112 static_assert(sizeof(SYSTEM_PROCESS_INFORMATION) == 248,
113 "Structure size mismatch");
114 #endif
115
116 // ntstatus.h conflicts with windows.h so define this locally.
117 #define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
118 #define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
119 #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
120
121 // Wrapper for NtQuerySystemProcessInformation with buffer reallocation logic.
122 bool QuerySystemProcessInformation(std::vector<BYTE>* data_buffer,
123 ULONG* data_size) {
124 typedef NTSTATUS(WINAPI * NTQUERYSYSTEMINFORMATION)(
125 SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation,
126 ULONG SystemInformationLength, PULONG ReturnLength);
127
128 HMODULE ntdll = ::GetModuleHandle(L"ntdll.dll");
129 if (!ntdll) {
130 NOTREACHED();
131 return false;
132 }
133
134 NTQUERYSYSTEMINFORMATION nt_query_system_information_ptr =
135 reinterpret_cast<NTQUERYSYSTEMINFORMATION>(
136 ::GetProcAddress(ntdll, "NtQuerySystemInformation"));
137 if (!nt_query_system_information_ptr) {
138 NOTREACHED();
139 return false;
140 }
141
142 ULONG buffer_size = data_buffer->size();
143
144 NTSTATUS result;
145
146 // There is a potential race condition between growing the buffer and new
147 // processes being creating. Try a few times before giving up.
ncarter (slow) 2016/08/03 22:28:55 "being creating" -> "being created"
stanisc 2016/08/04 01:36:01 Done.
148 for (int i = 0; i < 10; i++) {
149 *data_size = 0;
150 result = nt_query_system_information_ptr(
151 SystemProcessInformation,
152 buffer_size > 0 ? data_buffer->data() : nullptr,
153 buffer_size, data_size);
154
155 if (result == STATUS_INFO_LENGTH_MISMATCH ||
156 result == STATUS_BUFFER_TOO_SMALL) {
157 // Insufficient buffer. Resize to the returned |data_size| plus 10% extra
158 // to avoid frequent reallocations and try again.
159 DCHECK_GT(*data_size, buffer_size);
160 buffer_size = static_cast<ULONG>(*data_size * 1.1);
161 data_buffer->resize(buffer_size);
162 } else {
163 // Either STATUS_SUCCESS or an error other than the two above.
164 break;
165 }
166 }
167
168 return result == STATUS_SUCCESS;
169 }
170
171 } // namespace
172
173 // Per-thread data extracted from SYSTEM_THREAD_INFORMATION
174 // and stored in a snapshot.
175 // This structure is accessed only on the worker thread.
ncarter (slow) 2016/08/03 22:28:55 Reflow this comment paragraph. (if you happen to u
stanisc 2016/08/04 01:36:01 Done.
176 struct ThreadData {
177 base::PlatformThreadId thread_id;
178 ULONG context_switches;
179 };
180
181 // Per-process data extracted from SYSTEM_PROCESS_INFORMATION
182 // and stored in a snapshot.
183 // This structure is accessed only on the worker thread.
ncarter (slow) 2016/08/03 22:28:55 Reflow this comment paragraph.
stanisc 2016/08/04 01:36:01 Done.
184 struct ProcessData {
185 ProcessData() = default;
186 ProcessData(ProcessData&&) = default;
187
188 std::vector<ThreadData> threads;
189
190 private:
191 DISALLOW_COPY_AND_ASSIGN(ProcessData);
192 };
193
194 typedef std::map<base::ProcessId, ProcessData> ProcessDataMap;
195
196 // ProcessDataSnapshot gets created and accessed only on the worker thread.
197 // This is used to calculate metrics like Idle Wakeups / sec that require
198 // a delta between two snapshots.
199 struct ProcessDataSnapshot {
200 ProcessDataMap processes;
201 base::TimeTicks timestamp;
202 };
203
204 ULONG CountContextSwitches(const ProcessData& process_data) {
205 ULONG context_switches = 0;
206 for (const auto& thread_data : process_data.threads) {
207 context_switches += thread_data.context_switches;
208 }
209
210 return context_switches;
211 }
212
213 ULONG CountContextSwitchesDelta(const ProcessData& prev_process_data,
ncarter (slow) 2016/08/03 22:39:43 The following definitions ought to be in an anonym
stanisc 2016/08/04 01:36:01 OK. Moved everything to the anonymous namespace. D
214 const ProcessData& new_process_data) {
215 // This one pass algorithm relies on the threads vectors to be
216 // ordered by thread_id.
217 ULONG delta = 0;
218 size_t prev_index = 0;
219
220 for (const auto& new_thread : new_process_data.threads) {
221 ULONG prev_thread_context_switches = 0;
222
223 // Iterate over the process threads from the previous snapshot skipping
224 // threads that don't exist anymore.
brucedawson 2016/08/03 00:36:55 Maybe tweak this comment to clarify that you're no
stanisc 2016/08/04 01:36:01 Done.
225 for (; prev_index < prev_process_data.threads.size(); ++prev_index) {
226 const auto& prev_thread = prev_process_data.threads[prev_index];
227 if (prev_thread.thread_id == new_thread.thread_id) {
228 // Threads match between two snapshots. Use the previous snapshot
229 // thread's context_switches to subtract from the delta.
230 prev_thread_context_switches = prev_thread.context_switches;
231 ++prev_index;
232 break;
233 }
234
235 if (prev_thread.thread_id > new_thread.thread_id) {
236 // This is due to a new thread that didn't exist in the previous
237 // snapshot. Keep the zero value of |prev_thread_context_switches| which
238 // essentially means the entire number of context switches of the new
239 // thread will be added to the delta.
240 break;
241 }
242 }
243
244 delta += new_thread.context_switches - prev_thread_context_switches;
245 }
246
247 return delta;
248 }
249
250 SharedSampler::SharedSampler(
251 const scoped_refptr<base::SequencedTaskRunner>& blocking_pool_runner)
252 : refresh_flags_(0), previous_buffer_size_(0),
253 supported_image_names_(GetSupportedImageNames()),
254 blocking_pool_runner_(blocking_pool_runner) {
255 DCHECK(blocking_pool_runner.get());
256
257 // This object will be created on the UI thread, however the sequenced checker
258 // will be used to assert we're running the expensive operations on one of the
259 // blocking pool threads.
260 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
261 worker_pool_sequenced_checker_.DetachFromSequence();
262 }
263
264 SharedSampler::~SharedSampler() {}
265
266 int64_t SharedSampler::SupportsFlags() const {
267 return REFRESH_TYPE_IDLE_WAKEUPS;
268 }
269
270 SharedSampler::Callbacks::Callbacks() {}
271
272 SharedSampler::Callbacks::~Callbacks() {}
273
274 SharedSampler::Callbacks::Callbacks(Callbacks&& other) {
275 on_idle_wakeups = std::move(other.on_idle_wakeups);
276 }
277
278 void SharedSampler::RegisterCallbacks(
279 base::ProcessId process_id,
280 const OnIdleWakeupsCallback& on_idle_wakeups) {
281 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
282
283 if (process_id == 0)
284 return;
285
286 Callbacks callbacks;
287 callbacks.on_idle_wakeups = on_idle_wakeups;
288 bool result = callbacks_map_.insert(
289 std::make_pair(process_id, std::move(callbacks))).second;
290 DCHECK(result);
291 }
292
293 void SharedSampler::UnregisterCallbacks(base::ProcessId process_id) {
294 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
295
296 if (process_id == 0)
297 return;
298
299 callbacks_map_.erase(process_id);
300
301 if (callbacks_map_.empty())
302 ClearState();
303 }
304
305 void SharedSampler::Refresh(base::ProcessId process_id, int64_t refresh_flags) {
306 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
307 DCHECK(callbacks_map_.find(process_id) != callbacks_map_.end());
308 DCHECK_NE(0, refresh_flags & SupportsFlags());
309
310 if (process_id == 0)
311 return;
312
313 if (refresh_flags_ == 0) {
314 base::PostTaskAndReplyWithResult(
315 blocking_pool_runner_.get(), FROM_HERE,
316 base::Bind(&SharedSampler::RefreshOnWorkerThread, this),
317 base::Bind(&SharedSampler::OnRefreshDone, this));
318 } else {
319 // A group of consecuitive Refresh calls should all specify the same
brucedawson 2016/08/03 00:36:55 consecuitive -> consecutive
stanisc 2016/08/04 01:36:01 Done.
320 // refresh flags.
321 DCHECK_EQ(refresh_flags, refresh_flags_);
322 }
323
324 refresh_flags_ |= refresh_flags;
325 }
326
327 void SharedSampler::ClearState() {
328 previous_snapshot_.reset();
329 }
330
331 std::unique_ptr<SharedSampler::RefreshResults>
332 SharedSampler::RefreshOnWorkerThread() {
333 DCHECK(worker_pool_sequenced_checker_.CalledOnValidSequence());
334
335 std::unique_ptr<RefreshResults> results(new RefreshResults);
336
337 std::unique_ptr<ProcessDataSnapshot> snapshot = CaptureSnapshot();
338 if (snapshot) {
339 if (previous_snapshot_) {
340 MakeResultsFromTwoSnapshots(
341 *previous_snapshot_, *snapshot, results.get());
342 } else {
343 MakeResultsFromSnapshot(*snapshot, results.get());
344 }
345
346 previous_snapshot_ = std::move(snapshot);
347 } else {
348 // Failed to get snapshot. This is unlikely.
349 ClearState();
350 }
351
352 return results;
353 }
354
355 /* static */
356 std::vector<base::FilePath> SharedSampler::GetSupportedImageNames() {
357 const wchar_t kNacl64Exe[] = L"nacl64.exe";
358
359 std::vector<base::FilePath> supported_names;
360
361 base::FilePath current_exe;
362 if (PathService::Get(base::FILE_EXE, &current_exe))
363 supported_names.push_back(current_exe.BaseName());
364
365 supported_names.push_back(
366 base::FilePath(chrome::kBrowserProcessExecutableName));
367 supported_names.push_back(base::FilePath(kNacl64Exe));
368
369 return supported_names;
370 }
371
372 bool SharedSampler::IsSupportedImageName(
373 base::FilePath::StringPieceType image_name) const {
374 for (const base::FilePath supported_name : supported_image_names_) {
375 if (base::FilePath::CompareEqualIgnoreCase(image_name,
376 supported_name.value()))
377 return true;
378 }
379
380 return false;
381 }
382
383 std::unique_ptr<ProcessDataSnapshot> SharedSampler::CaptureSnapshot() {
384 DCHECK(worker_pool_sequenced_checker_.CalledOnValidSequence());
385
386 ULONG data_size;
387
388 // Preallocate the buffer with the size determined on the previous call to
389 // QuerySystemProcessInformation. This should be sufficient most of the time.
390 // QuerySystemProcessInformation will grow the buffer if necessary.
391 std::vector<BYTE> data_buffer(previous_buffer_size_);
brucedawson 2016/08/03 00:36:55 This may be premature optimization, but std::vecto
ncarter (slow) 2016/08/03 22:28:55 Use unique_ptr<BYTE[]> -- possibly wrapped in a cl
stanisc 2016/08/04 01:36:01 OK. I've implemented something similar. I liked th
392
393 if (!QuerySystemProcessInformation(&data_buffer, &data_size))
394 return std::unique_ptr<ProcessDataSnapshot>();
395
396 previous_buffer_size_ = data_buffer.size();
397
398 std::unique_ptr<ProcessDataSnapshot> snapshot(new ProcessDataSnapshot);
399 snapshot->timestamp = base::TimeTicks::Now();
400
401 for (ULONG offset = 0; offset < data_size; ) {
402 auto pi = reinterpret_cast<const SYSTEM_PROCESS_INFORMATION*>(
403 &data_buffer[offset]);
404
405 // Validate that the offset is valid and all needed data is within
406 // the buffer boundary.
407 if (offset + sizeof(SYSTEM_PROCESS_INFORMATION) > data_size)
408 break;
409 if (offset + sizeof(SYSTEM_PROCESS_INFORMATION) +
410 (pi->NumberOfThreads - 1) * sizeof(SYSTEM_THREAD_INFORMATION) >
411 data_size)
412 break;
413
414 if (pi->ImageName.Buffer) {
415 // Validate that the image name is within the buffer boundary.
416 // ImageName.Length seems to be in bytes rather than characters.
417 ULONG image_name_offset =
418 reinterpret_cast<BYTE*>(pi->ImageName.Buffer) - data_buffer.data();
419 if (image_name_offset + pi->ImageName.Length > data_size)
420 break;
421
422 // Check if this is a chrome process. Ignore all other processes.
423 if (IsSupportedImageName(pi->ImageName.Buffer)) {
424 // Collect enough data to be able to do a diff between two snapshots.
425 // Some threads might stop or new threads might be created between two
426 // snapshots. If a thread with a large number of context switches gets
427 // terminated the total number of context switches for the process might
428 // go down and the delta would be negative.
429 // To avoid that we need to compare thread IDs between two snapshots and
430 // not count context switches for threads that are missing in the most
431 // recent snapshot.
432 ProcessData process_data;
433
434 // Iterate over threads and store each thread's ID and number of context
435 // switches.
436 for (ULONG thread_index = 0; thread_index < pi->NumberOfThreads;
437 ++thread_index) {
438 const SYSTEM_THREAD_INFORMATION* ti = &pi->Threads[thread_index];
439 if (ti->ClientId.UniqueProcess != pi->ProcessId)
440 continue;
441
442 ThreadData thread_data;
443 thread_data.thread_id = static_cast<base::PlatformThreadId>(
444 reinterpret_cast<uintptr_t>(ti->ClientId.UniqueThread));
445 thread_data.context_switches = ti->ContextSwitchCount;
446 process_data.threads.push_back(thread_data);
447 }
448
449 // Order thread data by thread ID to help diff two snapshots.
450 std::sort(process_data.threads.begin(), process_data.threads.end(),
451 [](const ThreadData& l, const ThreadData r) {
452 return l.thread_id < r.thread_id;
453 });
454
455 base::ProcessId process_id = static_cast<base::ProcessId>(
456 reinterpret_cast<uintptr_t>(pi->ProcessId));
457 bool inserted = snapshot->processes.insert(
458 std::make_pair(process_id, std::move(process_data))).second;
459 DCHECK(inserted);
brucedawson 2016/08/03 00:36:55 Consider this syntax instead? snapshot->processes
stanisc 2016/08/04 01:36:01 The operator[] version doesn't seem to work with D
460 }
461 }
462
463 // Check for end of the list.
464 if (!pi->NextEntryOffset)
465 break;
466
467 // Jump to the next entry.
468 offset += pi->NextEntryOffset;
469 }
470
471 return snapshot;
472 }
473
474 // Seeks a matching ProcessData by Process ID in a previous snapshot.
475 // This uses the fact that ProcessDataMap entries are ordered by Process ID.
476 const ProcessData* SeekInPreviousSnapshot(
477 base::ProcessId process_id, ProcessDataMap::const_iterator* iter_to_advance,
478 const ProcessDataMap::const_iterator& range_end) {
479 for (; *iter_to_advance != range_end ; ++(*iter_to_advance)) {
480 if ((*iter_to_advance)->first == process_id) {
481 return &((*iter_to_advance)++)->second;
482 }
483 if ((*iter_to_advance)->first > process_id)
484 break;
485 }
486
487 return nullptr;
488 }
489
490 void SharedSampler::MakeResultsFromTwoSnapshots(
491 const ProcessDataSnapshot& prev_snapshot,
492 const ProcessDataSnapshot& snapshot,
493 RefreshResults* results) {
494 // Time delta in seconds.
495 double time_delta = (snapshot.timestamp - prev_snapshot.timestamp)
496 .InMillisecondsF() / 1000;
ncarter (slow) 2016/08/03 22:28:55 You can do .InSecondsF() instead of .InMillisecond
stanisc 2016/08/04 01:36:01 Somehow I didn't see InSecondsF() when I looked at
497
498 // Iterate over processes in both snapshots in parallel. This algorithm relies
499 // on map entries being ordered by Process ID.
500 ProcessDataMap::const_iterator prev_iter = prev_snapshot.processes.begin();
501
502 for (const auto& current_entry : snapshot.processes) {
503 base::ProcessId process_id = current_entry.first;
504 const ProcessData& process = current_entry.second;
505
506 const ProcessData* prev_snapshot_process = SeekInPreviousSnapshot(
507 process_id, &prev_iter, prev_snapshot.processes.end());
508
509 // Delta between the old snapshot and the new snapshot.
510 int idle_wakeups_delta;
511
512 if (prev_snapshot_process) {
513 // Processes match between two snapshots. Diff context switches.
514 idle_wakeups_delta =
515 CountContextSwitchesDelta(*prev_snapshot_process, process);
516 } else {
517 // Process is missing in the previous snapshot.
518 // Use entire number of context switches of the current process.
519 idle_wakeups_delta = CountContextSwitches(process);
ncarter (slow) 2016/08/03 22:28:55 CountContextSwitches(x) seems equivalent to C
stanisc 2016/08/04 01:36:01 Indeed. Done.
520 }
521
522 RefreshResult result;
523 result.process_id = process_id;
524 result.idle_wakeups_per_second =
525 static_cast<int>(round(idle_wakeups_delta / time_delta));
526 results->push_back(result);
527 }
528 }
529
530 void SharedSampler::MakeResultsFromSnapshot(const ProcessDataSnapshot& snapshot,
531 RefreshResults* results) {
532 for (const auto& pair : snapshot.processes) {
533 RefreshResult result;
534 result.process_id = pair.first;
535 // Use 0 for Idle Wakeups / sec in this case. This is consistent with
536 // ProcessMetrics::CalculateIdleWakeupsPerSecond implementation.
537 result.idle_wakeups_per_second = 0;
538 results->push_back(result);
539 }
540 }
541
542 void SharedSampler::OnRefreshDone(
543 std::unique_ptr<RefreshResults> refresh_results) {
544 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
545 DCHECK_NE(0, refresh_flags_);
546
547 size_t result_index = 0;
548
549 for (const auto& callback_entry : callbacks_map_) {
550 base::ProcessId process_id = callback_entry.first;
551 // A sentinel value of -1 is used when the result isn't available.
552 // Task manager will use this to display 'N/A'.
553 int idle_wakeups_per_second = -1;
554
555 // Match refresh result by |process_id|.
556 // This relies on refresh results being ordered by Process ID.
557 // Please note that |refresh_results| might contain some extra entries that
558 // don't exist in |callbacks_map_| if there is more than one instance of
559 // Chrome. It might be missing some entries too if there is a race condition
560 // between getting process information on the worker thread and adding a
561 // corresponding TaskGroup to the task manager.
562 for (; result_index < refresh_results->size(); ++result_index) {
563 const auto& result = (*refresh_results)[result_index];
564 if (result.process_id == process_id) {
565 // Data matched in |refresh_results|.
566 idle_wakeups_per_second = result.idle_wakeups_per_second;
567 ++result_index;
568 break;
569 }
570
571 if (result.process_id > process_id) {
572 // |refresh_results| contains an extra entry for a process that
stanisc 2016/08/01 22:34:27 This comment is wrong. It is the opposite - proces
stanisc 2016/08/04 01:36:01 Done.
573 // isn't tracked by |callbacks_map_|.
574 break;
575 }
576 }
577
578 if (TaskManagerObserver::IsResourceRefreshEnabled(REFRESH_TYPE_IDLE_WAKEUPS,
579 refresh_flags_)) {
580 callback_entry.second.on_idle_wakeups.Run(idle_wakeups_per_second);
581 }
582 }
583
584 // Reset refresh_results_ to trigger RefreshOnWorkerThread next time Refresh
585 // is called.
586 refresh_flags_ = 0;
587 }
588
589 } // namespace task_management
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698