| OLD | NEW |
| 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/command_line.h" | 14 #include "base/command_line.h" |
| 14 #include "base/path_service.h" | 15 #include "base/path_service.h" |
| 15 #include "base/time/time.h" | 16 #include "base/time/time.h" |
| 16 #include "chrome/browser/task_manager/task_manager_observer.h" | 17 #include "chrome/browser/task_manager/task_manager_observer.h" |
| 17 #include "chrome/common/chrome_constants.h" | 18 #include "chrome/common/chrome_constants.h" |
| 18 #include "content/public/browser/browser_thread.h" | 19 #include "content/public/browser/browser_thread.h" |
| 19 | 20 |
| 20 namespace task_manager { | 21 namespace task_manager { |
| 21 | 22 |
| 22 namespace { | 23 namespace { |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 213 ULONG context_switches; | 214 ULONG context_switches; |
| 214 }; | 215 }; |
| 215 | 216 |
| 216 // Per-process data extracted from SYSTEM_PROCESS_INFORMATION and stored in a | 217 // Per-process data extracted from SYSTEM_PROCESS_INFORMATION and stored in a |
| 217 // snapshot. This structure is accessed only on the worker thread. | 218 // snapshot. This structure is accessed only on the worker thread. |
| 218 struct ProcessData { | 219 struct ProcessData { |
| 219 ProcessData() = default; | 220 ProcessData() = default; |
| 220 ProcessData(ProcessData&&) = default; | 221 ProcessData(ProcessData&&) = default; |
| 221 | 222 |
| 222 int64_t physical_bytes; | 223 int64_t physical_bytes; |
| 224 base::Time start_time; |
| 225 base::TimeDelta cpu_time; |
| 223 std::vector<ThreadData> threads; | 226 std::vector<ThreadData> threads; |
| 224 | 227 |
| 225 private: | 228 private: |
| 226 DISALLOW_COPY_AND_ASSIGN(ProcessData); | 229 DISALLOW_COPY_AND_ASSIGN(ProcessData); |
| 227 }; | 230 }; |
| 228 | 231 |
| 229 typedef std::map<base::ProcessId, ProcessData> ProcessDataMap; | 232 typedef std::map<base::ProcessId, ProcessData> ProcessDataMap; |
| 230 | 233 |
| 231 ULONG CountContextSwitchesDelta(const ProcessData& prev_process_data, | 234 ULONG CountContextSwitchesDelta(const ProcessData& prev_process_data, |
| 232 const ProcessData& new_process_data) { | 235 const ProcessData& new_process_data) { |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 277 if ((*iter_to_advance)->first == process_id) { | 280 if ((*iter_to_advance)->first == process_id) { |
| 278 return &((*iter_to_advance)++)->second; | 281 return &((*iter_to_advance)++)->second; |
| 279 } | 282 } |
| 280 if ((*iter_to_advance)->first > process_id) | 283 if ((*iter_to_advance)->first > process_id) |
| 281 break; | 284 break; |
| 282 } | 285 } |
| 283 | 286 |
| 284 return nullptr; | 287 return nullptr; |
| 285 } | 288 } |
| 286 | 289 |
| 290 // A wrapper function converting ticks (in units of 100 ns) to Time. |
| 291 base::Time ConvertTicksToTime(uint64_t ticks) { |
| 292 FILETIME ft = bit_cast<FILETIME, uint64_t>(ticks); |
| 293 return base::Time::FromFileTime(ft); |
| 294 } |
| 295 |
| 296 // A wrapper function converting ticks (in units of 100 ns) to TimeDelta. |
| 297 base::TimeDelta ConvertTicksToTimeDelta(uint64_t ticks) { |
| 298 return base::TimeDelta::FromMicroseconds(ticks / 10); |
| 299 } |
| 300 |
| 287 } // namespace | 301 } // namespace |
| 288 | 302 |
| 289 // ProcessDataSnapshot gets created and accessed only on the worker thread. | 303 // ProcessDataSnapshot gets created and accessed only on the worker thread. |
| 290 // This is used to calculate metrics like Idle Wakeups / sec that require | 304 // This is used to calculate metrics like Idle Wakeups / sec that require |
| 291 // a delta between two snapshots. | 305 // a delta between two snapshots. |
| 292 // Please note that ProcessDataSnapshot has to be outside of anonymous namespace | 306 // Please note that ProcessDataSnapshot has to be outside of anonymous namespace |
| 293 // in order to match the declaration in shared_sampler.h. | 307 // in order to match the declaration in shared_sampler.h. |
| 294 struct ProcessDataSnapshot { | 308 struct ProcessDataSnapshot { |
| 295 ProcessDataMap processes; | 309 ProcessDataMap processes; |
| 296 base::TimeTicks timestamp; | 310 base::TimeTicks timestamp; |
| 297 }; | 311 }; |
| 298 | 312 |
| 299 SharedSampler::SharedSampler( | 313 SharedSampler::SharedSampler( |
| 300 const scoped_refptr<base::SequencedTaskRunner>& blocking_pool_runner) | 314 const scoped_refptr<base::SequencedTaskRunner>& blocking_pool_runner) |
| 301 : refresh_flags_(0), previous_buffer_size_(0), | 315 : refresh_flags_(0), previous_buffer_size_(0), |
| 302 supported_image_names_(GetSupportedImageNames()), | 316 supported_image_names_(GetSupportedImageNames()), |
| 303 blocking_pool_runner_(blocking_pool_runner) { | 317 blocking_pool_runner_(blocking_pool_runner) { |
| 304 DCHECK(blocking_pool_runner.get()); | 318 DCHECK(blocking_pool_runner.get()); |
| 305 | 319 |
| 306 // This object will be created on the UI thread, however the sequenced checker | 320 // This object will be created on the UI thread, however the sequenced checker |
| 307 // will be used to assert we're running the expensive operations on one of the | 321 // will be used to assert we're running the expensive operations on one of the |
| 308 // blocking pool threads. | 322 // blocking pool threads. |
| 309 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 323 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 310 worker_pool_sequenced_checker_.DetachFromSequence(); | 324 worker_pool_sequenced_checker_.DetachFromSequence(); |
| 311 } | 325 } |
| 312 | 326 |
| 313 SharedSampler::~SharedSampler() {} | 327 SharedSampler::~SharedSampler() {} |
| 314 | 328 |
| 315 int64_t SharedSampler::GetSupportedFlags() const { | 329 int64_t SharedSampler::GetSupportedFlags() const { |
| 316 return REFRESH_TYPE_IDLE_WAKEUPS | REFRESH_TYPE_PHYSICAL_MEMORY; | 330 return REFRESH_TYPE_IDLE_WAKEUPS | REFRESH_TYPE_PHYSICAL_MEMORY | |
| 331 REFRESH_TYPE_START_TIME | REFRESH_TYPE_CPU_TIME; |
| 317 } | 332 } |
| 318 | 333 |
| 319 SharedSampler::Callbacks::Callbacks() {} | 334 SharedSampler::Callbacks::Callbacks() {} |
| 320 | 335 |
| 321 SharedSampler::Callbacks::~Callbacks() {} | 336 SharedSampler::Callbacks::~Callbacks() {} |
| 322 | 337 |
| 323 SharedSampler::Callbacks::Callbacks(Callbacks&& other) { | 338 SharedSampler::Callbacks::Callbacks(Callbacks&& other) { |
| 324 on_idle_wakeups = std::move(other.on_idle_wakeups); | 339 on_idle_wakeups = std::move(other.on_idle_wakeups); |
| 325 on_physical_memory = std::move(other.on_physical_memory); | 340 on_physical_memory = std::move(other.on_physical_memory); |
| 341 on_start_time = std::move(other.on_start_time); |
| 342 on_cpu_time = std::move(other.on_cpu_time); |
| 326 } | 343 } |
| 327 | 344 |
| 328 void SharedSampler::RegisterCallbacks( | 345 void SharedSampler::RegisterCallbacks( |
| 329 base::ProcessId process_id, | 346 base::ProcessId process_id, |
| 330 const OnIdleWakeupsCallback& on_idle_wakeups, | 347 const OnIdleWakeupsCallback& on_idle_wakeups, |
| 331 const OnPhysicalMemoryCallback& on_physical_memory) { | 348 const OnPhysicalMemoryCallback& on_physical_memory, |
| 349 const OnStartTimeCallback& on_start_time, |
| 350 const OnCpuTimeCallback& on_cpu_time) { |
| 332 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 351 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 333 | 352 |
| 334 if (process_id == 0) | 353 if (process_id == 0) |
| 335 return; | 354 return; |
| 336 | 355 |
| 337 Callbacks callbacks; | 356 Callbacks callbacks; |
| 338 callbacks.on_idle_wakeups = on_idle_wakeups; | 357 callbacks.on_idle_wakeups = on_idle_wakeups; |
| 339 callbacks.on_physical_memory = on_physical_memory; | 358 callbacks.on_physical_memory = on_physical_memory; |
| 359 callbacks.on_start_time = on_start_time; |
| 360 callbacks.on_cpu_time = on_cpu_time; |
| 340 bool result = callbacks_map_.insert( | 361 bool result = callbacks_map_.insert( |
| 341 std::make_pair(process_id, std::move(callbacks))).second; | 362 std::make_pair(process_id, std::move(callbacks))).second; |
| 342 DCHECK(result); | 363 DCHECK(result); |
| 343 } | 364 } |
| 344 | 365 |
| 345 void SharedSampler::UnregisterCallbacks(base::ProcessId process_id) { | 366 void SharedSampler::UnregisterCallbacks(base::ProcessId process_id) { |
| 346 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 367 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 347 | 368 |
| 348 if (process_id == 0) | 369 if (process_id == 0) |
| 349 return; | 370 return; |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 486 // terminated the total number of context switches for the process might | 507 // terminated the total number of context switches for the process might |
| 487 // go down and the delta would be negative. | 508 // go down and the delta would be negative. |
| 488 // To avoid that we need to compare thread IDs between two snapshots and | 509 // To avoid that we need to compare thread IDs between two snapshots and |
| 489 // not count context switches for threads that are missing in the most | 510 // not count context switches for threads that are missing in the most |
| 490 // recent snapshot. | 511 // recent snapshot. |
| 491 ProcessData process_data; | 512 ProcessData process_data; |
| 492 | 513 |
| 493 process_data.physical_bytes = | 514 process_data.physical_bytes = |
| 494 static_cast<int64_t>(pi->WorkingSetPrivateSize); | 515 static_cast<int64_t>(pi->WorkingSetPrivateSize); |
| 495 | 516 |
| 517 process_data.start_time = ConvertTicksToTime(pi->CreateTime); |
| 518 |
| 519 process_data.cpu_time = |
| 520 ConvertTicksToTimeDelta(pi->KernelTime + pi->UserTime); |
| 521 |
| 496 // Iterate over threads and store each thread's ID and number of context | 522 // Iterate over threads and store each thread's ID and number of context |
| 497 // switches. | 523 // switches. |
| 498 for (ULONG thread_index = 0; thread_index < pi->NumberOfThreads; | 524 for (ULONG thread_index = 0; thread_index < pi->NumberOfThreads; |
| 499 ++thread_index) { | 525 ++thread_index) { |
| 500 const SYSTEM_THREAD_INFORMATION* ti = &pi->Threads[thread_index]; | 526 const SYSTEM_THREAD_INFORMATION* ti = &pi->Threads[thread_index]; |
| 501 if (ti->ClientId.UniqueProcess != pi->ProcessId) | 527 if (ti->ClientId.UniqueProcess != pi->ProcessId) |
| 502 continue; | 528 continue; |
| 503 | 529 |
| 504 ThreadData thread_data; | 530 ThreadData thread_data; |
| 505 thread_data.thread_id = static_cast<base::PlatformThreadId>( | 531 thread_data.thread_id = static_cast<base::PlatformThreadId>( |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 563 // Process is missing in the previous snapshot. | 589 // Process is missing in the previous snapshot. |
| 564 // Use entire number of context switches of the current process. | 590 // Use entire number of context switches of the current process. |
| 565 idle_wakeups_delta = CountContextSwitchesDelta(ProcessData(), process); | 591 idle_wakeups_delta = CountContextSwitchesDelta(ProcessData(), process); |
| 566 } | 592 } |
| 567 | 593 |
| 568 RefreshResult result; | 594 RefreshResult result; |
| 569 result.process_id = process_id; | 595 result.process_id = process_id; |
| 570 result.idle_wakeups_per_second = | 596 result.idle_wakeups_per_second = |
| 571 static_cast<int>(round(idle_wakeups_delta / time_delta)); | 597 static_cast<int>(round(idle_wakeups_delta / time_delta)); |
| 572 result.physical_bytes = process.physical_bytes; | 598 result.physical_bytes = process.physical_bytes; |
| 599 result.start_time = process.start_time; |
| 600 result.cpu_time = process.cpu_time; |
| 573 results->push_back(result); | 601 results->push_back(result); |
| 574 } | 602 } |
| 575 } | 603 } |
| 576 | 604 |
| 577 void SharedSampler::MakeResultsFromSnapshot(const ProcessDataSnapshot& snapshot, | 605 void SharedSampler::MakeResultsFromSnapshot(const ProcessDataSnapshot& snapshot, |
| 578 RefreshResults* results) { | 606 RefreshResults* results) { |
| 579 for (const auto& pair : snapshot.processes) { | 607 for (const auto& pair : snapshot.processes) { |
| 580 RefreshResult result; | 608 RefreshResult result; |
| 581 result.process_id = pair.first; | 609 result.process_id = pair.first; |
| 582 // Use 0 for Idle Wakeups / sec in this case. This is consistent with | 610 // Use 0 for Idle Wakeups / sec in this case. This is consistent with |
| 583 // ProcessMetrics::CalculateIdleWakeupsPerSecond implementation. | 611 // ProcessMetrics::CalculateIdleWakeupsPerSecond implementation. |
| 584 result.idle_wakeups_per_second = 0; | 612 result.idle_wakeups_per_second = 0; |
| 585 result.physical_bytes = pair.second.physical_bytes; | 613 result.physical_bytes = pair.second.physical_bytes; |
| 614 result.start_time = pair.second.start_time; |
| 615 result.cpu_time = pair.second.cpu_time; |
| 586 results->push_back(result); | 616 results->push_back(result); |
| 587 } | 617 } |
| 588 } | 618 } |
| 589 | 619 |
| 590 void SharedSampler::OnRefreshDone( | 620 void SharedSampler::OnRefreshDone( |
| 591 std::unique_ptr<RefreshResults> refresh_results) { | 621 std::unique_ptr<RefreshResults> refresh_results) { |
| 592 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 622 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 593 DCHECK_NE(0, refresh_flags_); | 623 DCHECK_NE(0, refresh_flags_); |
| 594 | 624 |
| 595 size_t result_index = 0; | 625 size_t result_index = 0; |
| 596 | 626 |
| 597 for (const auto& callback_entry : callbacks_map_) { | 627 for (const auto& callback_entry : callbacks_map_) { |
| 598 base::ProcessId process_id = callback_entry.first; | 628 base::ProcessId process_id = callback_entry.first; |
| 599 // A sentinel value of -1 is used when the result isn't available. | 629 // A sentinel value of -1 is used when the result isn't available. |
| 600 // Task manager will use this to display 'N/A'. | 630 // Task manager will use this to display '-'. |
| 601 int idle_wakeups_per_second = -1; | 631 int idle_wakeups_per_second = -1; |
| 602 int64_t physical_bytes = -1; | 632 int64_t physical_bytes = -1; |
| 633 base::Time start_time; |
| 634 base::TimeDelta cpu_time; |
| 603 | 635 |
| 604 // Match refresh result by |process_id|. | 636 // Match refresh result by |process_id|. |
| 605 // This relies on refresh results being ordered by Process ID. | 637 // This relies on refresh results being ordered by Process ID. |
| 606 // Please note that |refresh_results| might contain some extra entries that | 638 // Please note that |refresh_results| might contain some extra entries that |
| 607 // don't exist in |callbacks_map_| if there is more than one instance of | 639 // don't exist in |callbacks_map_| if there is more than one instance of |
| 608 // Chrome. It might be missing some entries too if there is a race condition | 640 // Chrome. It might be missing some entries too if there is a race condition |
| 609 // between getting process information on the worker thread and adding a | 641 // between getting process information on the worker thread and adding a |
| 610 // corresponding TaskGroup to the task manager. | 642 // corresponding TaskGroup to the task manager. |
| 611 for (; result_index < refresh_results->size(); ++result_index) { | 643 for (; result_index < refresh_results->size(); ++result_index) { |
| 612 const auto& result = (*refresh_results)[result_index]; | 644 const auto& result = (*refresh_results)[result_index]; |
| 613 if (result.process_id == process_id) { | 645 if (result.process_id == process_id) { |
| 614 // Data matched in |refresh_results|. | 646 // Data matched in |refresh_results|. |
| 615 idle_wakeups_per_second = result.idle_wakeups_per_second; | 647 idle_wakeups_per_second = result.idle_wakeups_per_second; |
| 616 physical_bytes = result.physical_bytes; | 648 physical_bytes = result.physical_bytes; |
| 649 start_time = result.start_time; |
| 650 cpu_time = result.cpu_time; |
| 617 ++result_index; | 651 ++result_index; |
| 618 break; | 652 break; |
| 619 } | 653 } |
| 620 | 654 |
| 621 if (result.process_id > process_id) { | 655 if (result.process_id > process_id) { |
| 622 // An entry corresponding to |process_id| is missing. See above. | 656 // An entry corresponding to |process_id| is missing. See above. |
| 623 break; | 657 break; |
| 624 } | 658 } |
| 625 } | 659 } |
| 626 | 660 |
| 627 if (TaskManagerObserver::IsResourceRefreshEnabled(REFRESH_TYPE_IDLE_WAKEUPS, | 661 if (TaskManagerObserver::IsResourceRefreshEnabled(REFRESH_TYPE_IDLE_WAKEUPS, |
| 628 refresh_flags_)) { | 662 refresh_flags_)) { |
| 629 DCHECK(callback_entry.second.on_idle_wakeups); | 663 DCHECK(callback_entry.second.on_idle_wakeups); |
| 630 callback_entry.second.on_idle_wakeups.Run(idle_wakeups_per_second); | 664 callback_entry.second.on_idle_wakeups.Run(idle_wakeups_per_second); |
| 631 } | 665 } |
| 632 | 666 |
| 633 if (TaskManagerObserver::IsResourceRefreshEnabled( | 667 if (TaskManagerObserver::IsResourceRefreshEnabled( |
| 634 REFRESH_TYPE_PHYSICAL_MEMORY, refresh_flags_)) { | 668 REFRESH_TYPE_PHYSICAL_MEMORY, refresh_flags_)) { |
| 635 DCHECK(callback_entry.second.on_physical_memory); | 669 DCHECK(callback_entry.second.on_physical_memory); |
| 636 callback_entry.second.on_physical_memory.Run(physical_bytes); | 670 callback_entry.second.on_physical_memory.Run(physical_bytes); |
| 637 } | 671 } |
| 672 |
| 673 if (TaskManagerObserver::IsResourceRefreshEnabled(REFRESH_TYPE_START_TIME, |
| 674 refresh_flags_)) { |
| 675 DCHECK(callback_entry.second.on_start_time); |
| 676 callback_entry.second.on_start_time.Run(start_time); |
| 677 } |
| 678 |
| 679 if (TaskManagerObserver::IsResourceRefreshEnabled(REFRESH_TYPE_CPU_TIME, |
| 680 refresh_flags_)) { |
| 681 DCHECK(callback_entry.second.on_cpu_time); |
| 682 callback_entry.second.on_cpu_time.Run(cpu_time); |
| 683 } |
| 638 } | 684 } |
| 639 | 685 |
| 640 // Reset refresh_results_ to trigger RefreshOnWorkerThread next time Refresh | 686 // Reset refresh_results_ to trigger RefreshOnWorkerThread next time Refresh |
| 641 // is called. | 687 // is called. |
| 642 refresh_flags_ = 0; | 688 refresh_flags_ = 0; |
| 643 } | 689 } |
| 644 | 690 |
| 645 } // namespace task_manager | 691 } // namespace task_manager |
| OLD | NEW |