Chromium Code Reviews| 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 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 306 // This object will be created on the UI thread, however the sequenced checker | 309 // 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 | 310 // will be used to assert we're running the expensive operations on one of the |
| 308 // blocking pool threads. | 311 // blocking pool threads. |
| 309 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 312 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 310 worker_pool_sequenced_checker_.DetachFromSequence(); | 313 worker_pool_sequenced_checker_.DetachFromSequence(); |
| 311 } | 314 } |
| 312 | 315 |
| 313 SharedSampler::~SharedSampler() {} | 316 SharedSampler::~SharedSampler() {} |
| 314 | 317 |
| 315 int64_t SharedSampler::GetSupportedFlags() const { | 318 int64_t SharedSampler::GetSupportedFlags() const { |
| 316 return REFRESH_TYPE_IDLE_WAKEUPS | REFRESH_TYPE_PHYSICAL_MEMORY; | 319 return REFRESH_TYPE_IDLE_WAKEUPS | REFRESH_TYPE_PHYSICAL_MEMORY | |
| 320 REFRESH_TYPE_START_TIME | REFRESH_TYPE_CPU_TIME; | |
| 317 } | 321 } |
| 318 | 322 |
| 319 SharedSampler::Callbacks::Callbacks() {} | 323 SharedSampler::Callbacks::Callbacks() {} |
| 320 | 324 |
| 321 SharedSampler::Callbacks::~Callbacks() {} | 325 SharedSampler::Callbacks::~Callbacks() {} |
| 322 | 326 |
| 323 SharedSampler::Callbacks::Callbacks(Callbacks&& other) { | 327 SharedSampler::Callbacks::Callbacks(Callbacks&& other) { |
| 324 on_idle_wakeups = std::move(other.on_idle_wakeups); | 328 on_idle_wakeups = std::move(other.on_idle_wakeups); |
| 325 on_physical_memory = std::move(other.on_physical_memory); | 329 on_physical_memory = std::move(other.on_physical_memory); |
| 330 on_start_time = std::move(other.on_start_time); | |
| 331 on_cpu_time = std::move(other.on_cpu_time); | |
| 326 } | 332 } |
| 327 | 333 |
| 328 void SharedSampler::RegisterCallbacks( | 334 void SharedSampler::RegisterCallbacks( |
| 329 base::ProcessId process_id, | 335 base::ProcessId process_id, |
| 330 const OnIdleWakeupsCallback& on_idle_wakeups, | 336 const OnIdleWakeupsCallback& on_idle_wakeups, |
| 331 const OnPhysicalMemoryCallback& on_physical_memory) { | 337 const OnPhysicalMemoryCallback& on_physical_memory, |
| 338 const OnStartTimeCallback& on_start_time, | |
| 339 const OnCpuTimeCallback& on_cpu_time) { | |
| 332 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 340 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 333 | 341 |
| 334 if (process_id == 0) | 342 if (process_id == 0) |
| 335 return; | 343 return; |
| 336 | 344 |
| 337 Callbacks callbacks; | 345 Callbacks callbacks; |
| 338 callbacks.on_idle_wakeups = on_idle_wakeups; | 346 callbacks.on_idle_wakeups = on_idle_wakeups; |
| 339 callbacks.on_physical_memory = on_physical_memory; | 347 callbacks.on_physical_memory = on_physical_memory; |
| 348 callbacks.on_start_time = on_start_time; | |
| 349 callbacks.on_cpu_time = on_cpu_time; | |
| 340 bool result = callbacks_map_.insert( | 350 bool result = callbacks_map_.insert( |
| 341 std::make_pair(process_id, std::move(callbacks))).second; | 351 std::make_pair(process_id, std::move(callbacks))).second; |
| 342 DCHECK(result); | 352 DCHECK(result); |
| 343 } | 353 } |
| 344 | 354 |
| 345 void SharedSampler::UnregisterCallbacks(base::ProcessId process_id) { | 355 void SharedSampler::UnregisterCallbacks(base::ProcessId process_id) { |
| 346 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 356 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 347 | 357 |
| 348 if (process_id == 0) | 358 if (process_id == 0) |
| 349 return; | 359 return; |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 426 base::FilePath::StringPieceType image_name) const { | 436 base::FilePath::StringPieceType image_name) const { |
| 427 for (const base::FilePath supported_name : supported_image_names_) { | 437 for (const base::FilePath supported_name : supported_image_names_) { |
| 428 if (base::FilePath::CompareEqualIgnoreCase(image_name, | 438 if (base::FilePath::CompareEqualIgnoreCase(image_name, |
| 429 supported_name.value())) | 439 supported_name.value())) |
| 430 return true; | 440 return true; |
| 431 } | 441 } |
| 432 | 442 |
| 433 return false; | 443 return false; |
| 434 } | 444 } |
| 435 | 445 |
| 446 namespace { | |
|
afakhry
2017/01/09 21:03:42
Nit: Usually we have a single anonymous namespace
chengx
2017/01/10 00:39:27
I have moved these two functions to the existing a
| |
| 447 // A wrapper function converting ticks (in units of 100 ns) to Time. | |
| 448 base::Time ConvertTicksToTime(uint64_t ticks) { | |
| 449 FILETIME ft = bit_cast<FILETIME, uint64_t>(ticks); | |
| 450 return base::Time::FromFileTime(ft); | |
| 451 } | |
| 452 | |
| 453 // A wrapper function converting ticks (in units of 100 ns) to TimeDelta. | |
| 454 base::TimeDelta ConvertTicksToTimeDelta(uint64_t ticks) { | |
| 455 return base::TimeDelta::FromMicroseconds(ticks / 10); | |
| 456 } | |
| 457 } | |
|
afakhry
2017/01/09 21:03:42
Nit: } // namespace
chengx
2017/01/10 00:39:27
Done.
| |
| 458 | |
| 436 std::unique_ptr<ProcessDataSnapshot> SharedSampler::CaptureSnapshot() { | 459 std::unique_ptr<ProcessDataSnapshot> SharedSampler::CaptureSnapshot() { |
| 437 DCHECK(worker_pool_sequenced_checker_.CalledOnValidSequence()); | 460 DCHECK(worker_pool_sequenced_checker_.CalledOnValidSequence()); |
| 438 | 461 |
| 439 // Preallocate the buffer with the size determined on the previous call to | 462 // Preallocate the buffer with the size determined on the previous call to |
| 440 // QuerySystemProcessInformation. This should be sufficient most of the time. | 463 // QuerySystemProcessInformation. This should be sufficient most of the time. |
| 441 // QuerySystemProcessInformation will grow the buffer if necessary. | 464 // QuerySystemProcessInformation will grow the buffer if necessary. |
| 442 ByteBuffer data_buffer(previous_buffer_size_); | 465 ByteBuffer data_buffer(previous_buffer_size_); |
| 443 | 466 |
| 444 if (!QuerySystemProcessInformation(&data_buffer)) | 467 if (!QuerySystemProcessInformation(&data_buffer)) |
| 445 return std::unique_ptr<ProcessDataSnapshot>(); | 468 return std::unique_ptr<ProcessDataSnapshot>(); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 480 // terminated the total number of context switches for the process might | 503 // terminated the total number of context switches for the process might |
| 481 // go down and the delta would be negative. | 504 // go down and the delta would be negative. |
| 482 // To avoid that we need to compare thread IDs between two snapshots and | 505 // To avoid that we need to compare thread IDs between two snapshots and |
| 483 // not count context switches for threads that are missing in the most | 506 // not count context switches for threads that are missing in the most |
| 484 // recent snapshot. | 507 // recent snapshot. |
| 485 ProcessData process_data; | 508 ProcessData process_data; |
| 486 | 509 |
| 487 process_data.physical_bytes = | 510 process_data.physical_bytes = |
| 488 static_cast<int64_t>(pi->WorkingSetPrivateSize); | 511 static_cast<int64_t>(pi->WorkingSetPrivateSize); |
| 489 | 512 |
| 513 process_data.start_time = ConvertTicksToTime(pi->CreateTime); | |
| 514 | |
| 515 process_data.cpu_time = | |
| 516 ConvertTicksToTimeDelta(pi->KernelTime + pi->UserTime); | |
| 517 | |
| 490 // Iterate over threads and store each thread's ID and number of context | 518 // Iterate over threads and store each thread's ID and number of context |
| 491 // switches. | 519 // switches. |
| 492 for (ULONG thread_index = 0; thread_index < pi->NumberOfThreads; | 520 for (ULONG thread_index = 0; thread_index < pi->NumberOfThreads; |
| 493 ++thread_index) { | 521 ++thread_index) { |
| 494 const SYSTEM_THREAD_INFORMATION* ti = &pi->Threads[thread_index]; | 522 const SYSTEM_THREAD_INFORMATION* ti = &pi->Threads[thread_index]; |
| 495 if (ti->ClientId.UniqueProcess != pi->ProcessId) | 523 if (ti->ClientId.UniqueProcess != pi->ProcessId) |
| 496 continue; | 524 continue; |
| 497 | 525 |
| 498 ThreadData thread_data; | 526 ThreadData thread_data; |
| 499 thread_data.thread_id = static_cast<base::PlatformThreadId>( | 527 thread_data.thread_id = static_cast<base::PlatformThreadId>( |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 557 // Process is missing in the previous snapshot. | 585 // Process is missing in the previous snapshot. |
| 558 // Use entire number of context switches of the current process. | 586 // Use entire number of context switches of the current process. |
| 559 idle_wakeups_delta = CountContextSwitchesDelta(ProcessData(), process); | 587 idle_wakeups_delta = CountContextSwitchesDelta(ProcessData(), process); |
| 560 } | 588 } |
| 561 | 589 |
| 562 RefreshResult result; | 590 RefreshResult result; |
| 563 result.process_id = process_id; | 591 result.process_id = process_id; |
| 564 result.idle_wakeups_per_second = | 592 result.idle_wakeups_per_second = |
| 565 static_cast<int>(round(idle_wakeups_delta / time_delta)); | 593 static_cast<int>(round(idle_wakeups_delta / time_delta)); |
| 566 result.physical_bytes = process.physical_bytes; | 594 result.physical_bytes = process.physical_bytes; |
| 595 result.start_time = process.start_time; | |
| 596 result.cpu_time = process.cpu_time; | |
| 567 results->push_back(result); | 597 results->push_back(result); |
| 568 } | 598 } |
| 569 } | 599 } |
| 570 | 600 |
| 571 void SharedSampler::MakeResultsFromSnapshot(const ProcessDataSnapshot& snapshot, | 601 void SharedSampler::MakeResultsFromSnapshot(const ProcessDataSnapshot& snapshot, |
| 572 RefreshResults* results) { | 602 RefreshResults* results) { |
| 573 for (const auto& pair : snapshot.processes) { | 603 for (const auto& pair : snapshot.processes) { |
| 574 RefreshResult result; | 604 RefreshResult result; |
| 575 result.process_id = pair.first; | 605 result.process_id = pair.first; |
| 576 // Use 0 for Idle Wakeups / sec in this case. This is consistent with | 606 // Use 0 for Idle Wakeups / sec in this case. This is consistent with |
| 577 // ProcessMetrics::CalculateIdleWakeupsPerSecond implementation. | 607 // ProcessMetrics::CalculateIdleWakeupsPerSecond implementation. |
| 578 result.idle_wakeups_per_second = 0; | 608 result.idle_wakeups_per_second = 0; |
| 579 result.physical_bytes = pair.second.physical_bytes; | 609 result.physical_bytes = pair.second.physical_bytes; |
| 610 result.start_time = pair.second.start_time; | |
| 611 result.cpu_time = pair.second.cpu_time; | |
| 580 results->push_back(result); | 612 results->push_back(result); |
| 581 } | 613 } |
| 582 } | 614 } |
| 583 | 615 |
| 584 void SharedSampler::OnRefreshDone( | 616 void SharedSampler::OnRefreshDone( |
| 585 std::unique_ptr<RefreshResults> refresh_results) { | 617 std::unique_ptr<RefreshResults> refresh_results) { |
| 586 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 618 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 587 DCHECK_NE(0, refresh_flags_); | 619 DCHECK_NE(0, refresh_flags_); |
| 588 | 620 |
| 589 size_t result_index = 0; | 621 size_t result_index = 0; |
| 590 | 622 |
| 591 for (const auto& callback_entry : callbacks_map_) { | 623 for (const auto& callback_entry : callbacks_map_) { |
| 592 base::ProcessId process_id = callback_entry.first; | 624 base::ProcessId process_id = callback_entry.first; |
| 593 // A sentinel value of -1 is used when the result isn't available. | 625 // A sentinel value of -1 is used when the result isn't available. |
| 594 // Task manager will use this to display 'N/A'. | 626 // Task manager will use this to display '-'. |
| 595 int idle_wakeups_per_second = -1; | 627 int idle_wakeups_per_second = -1; |
| 596 int64_t physical_bytes = -1; | 628 int64_t physical_bytes = -1; |
| 629 base::Time start_time; | |
| 630 base::TimeDelta cpu_time; | |
| 597 | 631 |
| 598 // Match refresh result by |process_id|. | 632 // Match refresh result by |process_id|. |
| 599 // This relies on refresh results being ordered by Process ID. | 633 // This relies on refresh results being ordered by Process ID. |
| 600 // Please note that |refresh_results| might contain some extra entries that | 634 // Please note that |refresh_results| might contain some extra entries that |
| 601 // don't exist in |callbacks_map_| if there is more than one instance of | 635 // don't exist in |callbacks_map_| if there is more than one instance of |
| 602 // Chrome. It might be missing some entries too if there is a race condition | 636 // Chrome. It might be missing some entries too if there is a race condition |
| 603 // between getting process information on the worker thread and adding a | 637 // between getting process information on the worker thread and adding a |
| 604 // corresponding TaskGroup to the task manager. | 638 // corresponding TaskGroup to the task manager. |
| 605 for (; result_index < refresh_results->size(); ++result_index) { | 639 for (; result_index < refresh_results->size(); ++result_index) { |
| 606 const auto& result = (*refresh_results)[result_index]; | 640 const auto& result = (*refresh_results)[result_index]; |
| 607 if (result.process_id == process_id) { | 641 if (result.process_id == process_id) { |
| 608 // Data matched in |refresh_results|. | 642 // Data matched in |refresh_results|. |
| 609 idle_wakeups_per_second = result.idle_wakeups_per_second; | 643 idle_wakeups_per_second = result.idle_wakeups_per_second; |
| 610 physical_bytes = result.physical_bytes; | 644 physical_bytes = result.physical_bytes; |
| 645 start_time = result.start_time; | |
| 646 cpu_time = result.cpu_time; | |
| 611 ++result_index; | 647 ++result_index; |
| 612 break; | 648 break; |
| 613 } | 649 } |
| 614 | 650 |
| 615 if (result.process_id > process_id) { | 651 if (result.process_id > process_id) { |
| 616 // An entry corresponding to |process_id| is missing. See above. | 652 // An entry corresponding to |process_id| is missing. See above. |
| 617 break; | 653 break; |
| 618 } | 654 } |
| 619 } | 655 } |
| 620 | 656 |
| 621 if (TaskManagerObserver::IsResourceRefreshEnabled(REFRESH_TYPE_IDLE_WAKEUPS, | 657 if (TaskManagerObserver::IsResourceRefreshEnabled(REFRESH_TYPE_IDLE_WAKEUPS, |
| 622 refresh_flags_)) { | 658 refresh_flags_)) { |
| 623 DCHECK(callback_entry.second.on_idle_wakeups); | 659 DCHECK(callback_entry.second.on_idle_wakeups); |
| 624 callback_entry.second.on_idle_wakeups.Run(idle_wakeups_per_second); | 660 callback_entry.second.on_idle_wakeups.Run(idle_wakeups_per_second); |
| 625 } | 661 } |
| 626 | 662 |
| 627 if (TaskManagerObserver::IsResourceRefreshEnabled( | 663 if (TaskManagerObserver::IsResourceRefreshEnabled( |
| 628 REFRESH_TYPE_PHYSICAL_MEMORY, refresh_flags_)) { | 664 REFRESH_TYPE_PHYSICAL_MEMORY, refresh_flags_)) { |
| 629 DCHECK(callback_entry.second.on_physical_memory); | 665 DCHECK(callback_entry.second.on_physical_memory); |
| 630 callback_entry.second.on_physical_memory.Run(physical_bytes); | 666 callback_entry.second.on_physical_memory.Run(physical_bytes); |
| 631 } | 667 } |
| 668 | |
| 669 if (TaskManagerObserver::IsResourceRefreshEnabled(REFRESH_TYPE_START_TIME, | |
| 670 refresh_flags_)) { | |
| 671 DCHECK(callback_entry.second.on_start_time); | |
| 672 callback_entry.second.on_start_time.Run(start_time); | |
| 673 } | |
| 674 | |
| 675 if (TaskManagerObserver::IsResourceRefreshEnabled(REFRESH_TYPE_CPU_TIME, | |
| 676 refresh_flags_)) { | |
| 677 DCHECK(callback_entry.second.on_cpu_time); | |
| 678 callback_entry.second.on_cpu_time.Run(cpu_time); | |
| 679 } | |
| 632 } | 680 } |
| 633 | 681 |
| 634 // Reset refresh_results_ to trigger RefreshOnWorkerThread next time Refresh | 682 // Reset refresh_results_ to trigger RefreshOnWorkerThread next time Refresh |
| 635 // is called. | 683 // is called. |
| 636 refresh_flags_ = 0; | 684 refresh_flags_ = 0; |
| 637 } | 685 } |
| 638 | 686 |
| 639 } // namespace task_manager | 687 } // namespace task_manager |
| OLD | NEW |