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

Unified Diff: tools/win/IdleWakeups/idle_wakeups.cpp

Issue 2356753004: IdleWakeups tool (Closed)
Patch Set: Removed TODO from stdafx.cpp and added myself to OWNERS file. Created 4 years, 3 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/win/IdleWakeups/ReadMe.txt ('k') | tools/win/IdleWakeups/power_sampler.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/win/IdleWakeups/idle_wakeups.cpp
diff --git a/tools/win/IdleWakeups/idle_wakeups.cpp b/tools/win/IdleWakeups/idle_wakeups.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cb9dacc9d1396de0dfa6123e49110f2100dfa8c9
--- /dev/null
+++ b/tools/win/IdleWakeups/idle_wakeups.cpp
@@ -0,0 +1,348 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "stdafx.h"
+
+#include <algorithm>
+#include <map>
+#include <vector>
+
+#include "power_sampler.h"
+#include "system_information_sampler.h"
+
+// Result data structure contains a final set of values calculated based on
+// comparison of two snapshots. These are the values that the tool prints
+// in the output.
+struct Result {
+ ULONG idle_wakeups_per_sec;
+ double cpu_usage;
+ ULONGLONG working_set;
+ double power;
+};
+
+typedef std::vector<Result> ResultVector;
+
+// The following 4 functions are used for sorting of ResultVector.
+ULONG GetIdleWakeupsPerSec(const Result& r) {
+ return r.idle_wakeups_per_sec;
+}
+double GetCpuUsage(const Result& r) {
+ return r.cpu_usage;
+}
+ULONGLONG GetWorkingSet(const Result& r) {
+ return r.working_set;
+}
+double GetPower(const Result& r) {
+ return r.power;
+}
+
+template <typename T>
+T GetMedian(ResultVector* results, T (*getter)(const Result&)) {
+ std::sort(results->begin(), results->end(),
+ [&](const Result& lhs, const Result& rhs) {
+ return getter(lhs) < getter(rhs);
+ });
+
+ size_t median_index = results->size() / 2;
+ if (results->size() % 2 != 0) {
+ return getter((*results)[median_index]);
+ } else {
+ return (getter((*results)[median_index - 1]) +
+ getter((*results)[median_index])) /
+ 2;
+ }
+}
+
+// This class holds the app state and constains a number of utilities for
+// collecting and diffing snapshots of data, handling processes, etc.
+class IdleWakeups {
+ public:
+ IdleWakeups();
+ ~IdleWakeups();
+
+ Result DiffSnapshots(const ProcessDataSnapshot& prev_snapshot,
+ const ProcessDataSnapshot& snapshot);
+
+ void OpenProcesses(const ProcessDataSnapshot& snapshot);
+ void CloseProcesses();
+
+ private:
+ HANDLE GetProcessHandle(ProcessId process_id);
+ void OpenProcess(ProcessId process_id);
+ void CloseProcess(ProcessId process_id);
+ bool GetFinishedProcessCpuTime(ProcessId process_id, ULONGLONG* cpu_usage);
+
+ static ULONG CountContextSwitches(const ProcessData& process_data);
+ static ULONG DiffContextSwitches(const ProcessData& prev_process_data,
+ const ProcessData& process_data);
+
+ std::map<ProcessId, HANDLE> process_id_to_hanle_map;
+
+ IdleWakeups& operator=(const IdleWakeups&) = delete;
+ IdleWakeups(const IdleWakeups&) = delete;
+};
+
+IdleWakeups::IdleWakeups() {}
+
+IdleWakeups::~IdleWakeups() {
+ CloseProcesses();
+}
+
+void IdleWakeups::OpenProcesses(const ProcessDataSnapshot& snapshot) {
+ for (auto& pair : snapshot.processes) {
+ OpenProcess(pair.first);
+ }
+}
+
+void IdleWakeups::CloseProcesses() {
+ for (auto& pair : process_id_to_hanle_map) {
+ CloseHandle(pair.second);
+ }
+ process_id_to_hanle_map.clear();
+}
+
+HANDLE IdleWakeups::GetProcessHandle(ProcessId process_id) {
+ return process_id_to_hanle_map[process_id];
+}
+
+void IdleWakeups::OpenProcess(ProcessId process_id) {
+ process_id_to_hanle_map[process_id] = ::OpenProcess(
+ PROCESS_QUERY_LIMITED_INFORMATION, FALSE, (DWORD)(ULONGLONG)process_id);
+}
+
+void IdleWakeups::CloseProcess(ProcessId process_id) {
+ HANDLE handle = GetProcessHandle(process_id);
+ CloseHandle(handle);
+ process_id_to_hanle_map.erase(process_id);
+}
+
+ULONG IdleWakeups::CountContextSwitches(const ProcessData& process_data) {
+ ULONG context_switches = 0;
+
+ for (const auto& thread_data : process_data.threads) {
+ context_switches += thread_data.context_switches;
+ }
+
+ return context_switches;
+}
+
+ULONG IdleWakeups::DiffContextSwitches(const ProcessData& prev_process_data,
+ const ProcessData& process_data) {
+ ULONG context_switches = 0;
+ size_t prev_index = 0;
+
+ for (const auto& thread_data : process_data.threads) {
+ ULONG prev_context_switches = 0;
+
+ for (; prev_index < prev_process_data.threads.size(); ++prev_index) {
+ const auto& prev_thread_data = prev_process_data.threads[prev_index];
+ if (prev_thread_data.thread_id == thread_data.thread_id) {
+ prev_context_switches = prev_thread_data.context_switches;
+ ++prev_index;
+ break;
+ }
+
+ if (prev_thread_data.thread_id > thread_data.thread_id)
+ break;
+ }
+
+ context_switches += thread_data.context_switches - prev_context_switches;
+ }
+
+ return context_switches;
+}
+
+bool IdleWakeups::GetFinishedProcessCpuTime(ProcessId process_id,
+ ULONGLONG* cpu_time) {
+ HANDLE process_handle = GetProcessHandle(process_id);
+
+ FILETIME creation_time, exit_time, kernel_time, user_time;
+ if (GetProcessTimes(process_handle, &creation_time, &exit_time, &kernel_time,
+ &user_time)) {
+ ULARGE_INTEGER ul_kernel_time, ul_user_time;
+ ul_kernel_time.LowPart = kernel_time.dwLowDateTime;
+ ul_kernel_time.HighPart = kernel_time.dwHighDateTime;
+ ul_user_time.LowPart = user_time.dwLowDateTime;
+ ul_user_time.HighPart = user_time.dwHighDateTime;
+ *cpu_time = ul_kernel_time.QuadPart + ul_user_time.QuadPart;
+ return true;
+ }
+
+ *cpu_time = 0;
+ return false;
+}
+
+Result IdleWakeups::DiffSnapshots(const ProcessDataSnapshot& prev_snapshot,
+ const ProcessDataSnapshot& snapshot) {
+ ULONG idle_wakeups_delta = 0;
+ ULONGLONG cpu_usage_delta = 0;
+ ULONGLONG total_working_set = 0;
+
+ ProcessDataMap::const_iterator prev_it = prev_snapshot.processes.begin();
+
+ for (const auto& it : snapshot.processes) {
+ ProcessId process_id = it.first;
+ const ProcessData& process_data = it.second;
+ const ProcessData* prev_process_data_to_diff = nullptr;
+ ULONGLONG prev_process_cpu_time = 0;
+
+ for (; prev_it != prev_snapshot.processes.end(); ++prev_it) {
+ ProcessId prev_process_id = prev_it->first;
+ const ProcessData& prev_process_data = prev_it->second;
+
+ if (prev_process_id == process_id) {
+ prev_process_data_to_diff = &prev_process_data;
+ prev_process_cpu_time = prev_process_data.cpu_time;
+ ++prev_it;
+ break;
+ }
+
+ if (prev_process_id > process_id)
+ break;
+
+ // Prev process disappeared.
+ ULONGLONG last_known_cpu_time;
+ if (GetFinishedProcessCpuTime(prev_process_id, &last_known_cpu_time)) {
+ cpu_usage_delta += last_known_cpu_time - prev_process_data.cpu_time;
+ }
+ CloseProcess(prev_process_id);
+ }
+
+ if (prev_process_data_to_diff) {
+ idle_wakeups_delta +=
+ DiffContextSwitches(*prev_process_data_to_diff, process_data);
+ } else {
+ // New process that we haven't seen before.
+ OpenProcess(process_id);
+ idle_wakeups_delta += CountContextSwitches(process_data);
+ }
+
+ cpu_usage_delta += process_data.cpu_time - prev_process_cpu_time;
+ total_working_set += process_data.working_set / 1024;
+ }
+
+ double time_delta = snapshot.timestamp - prev_snapshot.timestamp;
+ Result result;
+ result.idle_wakeups_per_sec =
+ static_cast<ULONG>(idle_wakeups_delta / time_delta);
+ // brucedawson: Don't divide by number of processors so that all numbers are
+ // percentage of a core
+ // result.cpu_usage = (double)cpu_usage_delta * 100 / (time_delta * 10000000 *
+ // NumberOfprocessors());
+ result.cpu_usage = (double)cpu_usage_delta * 100 / (time_delta * 10000000);
+ result.working_set = total_working_set;
+
+ return result;
+}
+
+HANDLE ctrl_c_pressed = NULL;
+
+BOOL WINAPI HandlerFunction(DWORD ctrl_type) {
+ if (ctrl_type == CTRL_C_EVENT) {
+ printf("Ctrl+C pressed...\n");
+ SetEvent(ctrl_c_pressed);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+const DWORD sleep_time_sec = 2;
+
+void PrintHeader() {
+ printf(
+ "------------------------------------------------------------------------"
+ "----------\n");
+ printf(
+ " Context switches/sec CPU usage Working set "
+ " Power\n");
+ printf(
+ "------------------------------------------------------------------------"
+ "----------\n");
+}
+
+#define RESULT_FORMAT_STRING " %20lu %8.2f%c %6.2f MiB %4.2f W\n"
+
+int wmain(int argc, wchar_t* argv[]) {
+ ctrl_c_pressed = CreateEvent(NULL, FALSE, FALSE, NULL);
+ SetConsoleCtrlHandler(HandlerFunction, TRUE);
+
+ PowerSampler power_sampler;
+ SystemInformationSampler system_information_sampler(argc > 1 ? argv[1]
+ : L"chrome.exe");
+ IdleWakeups the_app;
+
+ // Take the initial snapshot.
+ std::unique_ptr<ProcessDataSnapshot> previous_snapshot =
+ system_information_sampler.TakeSnapshot();
+
+ the_app.OpenProcesses(*previous_snapshot);
+
+ ULONG cumulative_idle_wakeups_per_sec = 0;
+ double cumulative_cpu_usage = 0.0;
+ ULONGLONG cumulative_working_set = 0;
+ double cumulative_energy = 0.0;
+
+ ResultVector results;
+
+ printf("Capturing perf data for all processes matching %ls\n",
+ system_information_sampler.target_process_name_filter());
+
+ PrintHeader();
+
+ for (;;) {
+ if (WaitForSingleObject(ctrl_c_pressed, sleep_time_sec * 1000) ==
+ WAIT_OBJECT_0)
+ break;
+
+ std::unique_ptr<ProcessDataSnapshot> snapshot =
+ system_information_sampler.TakeSnapshot();
+ size_t number_of_processes = snapshot->processes.size();
+
+ Result result = the_app.DiffSnapshots(*previous_snapshot, *snapshot);
+ previous_snapshot = std::move(snapshot);
+
+ power_sampler.SampleCPUPowerState();
+ result.power = power_sampler.get_power(L"Processor");
+
+ printf("%9u processes" RESULT_FORMAT_STRING, (DWORD)number_of_processes,
+ result.idle_wakeups_per_sec, result.cpu_usage, '%',
+ result.working_set / 1024.0, result.power);
+
+ cumulative_idle_wakeups_per_sec += result.idle_wakeups_per_sec;
+ cumulative_cpu_usage += result.cpu_usage;
+ cumulative_working_set += result.working_set;
+ cumulative_energy += result.power;
+
+ results.push_back(result);
+ }
+
+ CloseHandle(ctrl_c_pressed);
+
+ ULONG sample_count = (ULONG)results.size();
+ if (sample_count == 0)
+ return 0;
+
+ PrintHeader();
+
+ printf(" Average" RESULT_FORMAT_STRING,
+ cumulative_idle_wakeups_per_sec / sample_count,
+ cumulative_cpu_usage / sample_count, '%',
+ (cumulative_working_set / 1024.0) / sample_count,
+ cumulative_energy / sample_count);
+
+ Result median_result;
+
+ median_result.idle_wakeups_per_sec =
+ GetMedian<ULONG>(&results, GetIdleWakeupsPerSec);
+ median_result.cpu_usage = GetMedian<double>(&results, GetCpuUsage);
+ median_result.working_set = GetMedian<ULONGLONG>(&results, GetWorkingSet);
+ median_result.power = GetMedian<double>(&results, GetPower);
+
+ printf(" Median" RESULT_FORMAT_STRING,
+ median_result.idle_wakeups_per_sec, median_result.cpu_usage, '%',
+ median_result.working_set / 1024.0, median_result.power);
+
+ return 0;
+}
« no previous file with comments | « tools/win/IdleWakeups/ReadMe.txt ('k') | tools/win/IdleWakeups/power_sampler.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698