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

Unified Diff: tools/win/IdleWakeups/system_information_sampler.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/system_information_sampler.h ('k') | tools/win/OWNERS » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/win/IdleWakeups/system_information_sampler.cpp
diff --git a/tools/win/IdleWakeups/system_information_sampler.cpp b/tools/win/IdleWakeups/system_information_sampler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0f4c6fc8f4429ba7243d627e70a105fe2ff5dde5
--- /dev/null
+++ b/tools/win/IdleWakeups/system_information_sampler.cpp
@@ -0,0 +1,314 @@
+// 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 "system_information_sampler.h"
+
+// From ntdef.h
+typedef struct _UNICODE_STRING {
+ USHORT Length;
+ USHORT MaximumLength;
+ PWCH Buffer;
+} UNICODE_STRING;
+
+// From <wdm.h>
+typedef LONG KPRIORITY;
+typedef LONG KWAIT_REASON; // Full definition is in wdm.h
+
+// From ntddk.h
+typedef struct _VM_COUNTERS {
+ SIZE_T PeakVirtualSize;
+ SIZE_T VirtualSize;
+ ULONG PageFaultCount;
+ // Padding here in 64-bit
+ SIZE_T PeakWorkingSetSize;
+ SIZE_T WorkingSetSize;
+ SIZE_T QuotaPeakPagedPoolUsage;
+ SIZE_T QuotaPagedPoolUsage;
+ SIZE_T QuotaPeakNonPagedPoolUsage;
+ SIZE_T QuotaNonPagedPoolUsage;
+ SIZE_T PagefileUsage;
+ SIZE_T PeakPagefileUsage;
+} VM_COUNTERS;
+
+// Two possibilities available from here:
+// http://stackoverflow.com/questions/28858849/where-is-system-information-class-defined
+
+typedef enum _SYSTEM_INFORMATION_CLASS {
+ SystemBasicInformation = 0,
+ SystemPerformanceInformation = 2,
+ SystemTimeOfDayInformation = 3,
+ SystemProcessInformation = 5, // This is the number that we need
+ SystemProcessorPerformanceInformation = 8,
+ SystemInterruptInformation = 23,
+ SystemExceptionInformation = 33,
+ SystemRegistryQuotaInformation = 37,
+ SystemLookasideInformation = 45
+} SYSTEM_INFORMATION_CLASS;
+
+// https://msdn.microsoft.com/en-us/library/gg750647.aspx?f=255&MSPPError=-2147217396
+typedef struct {
+ HANDLE UniqueProcess; // Actually process ID
+ HANDLE UniqueThread; // Actually thread ID
+} CLIENT_ID;
+
+// From http://alax.info/blog/1182, with corrections and modifications
+// Originally from
+// http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FSystem%20Information%2FStructures%2FSYSTEM_THREAD.html
+struct SYSTEM_THREAD_INFORMATION {
+ ULONGLONG KernelTime;
+ ULONGLONG UserTime;
+ ULONGLONG CreateTime;
+ ULONG WaitTime;
+ // Padding here in 64-bit
+ PVOID StartAddress;
+ CLIENT_ID ClientId;
+ KPRIORITY Priority;
+ LONG BasePriority;
+ ULONG ContextSwitchCount;
+ ULONG State;
+ KWAIT_REASON WaitReason;
+};
+#if _M_X64
+static_assert(sizeof(SYSTEM_THREAD_INFORMATION) == 80,
+ "Structure size mismatch");
+#else
+static_assert(sizeof(SYSTEM_THREAD_INFORMATION) == 64,
+ "Structure size mismatch");
+#endif
+
+// From http://alax.info/blog/1182, with corrections and modifications
+// Originally from
+// http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FSystem%20Information%2FStructures%2FSYSTEM_THREAD.html
+struct SYSTEM_PROCESS_INFORMATION {
+ ULONG NextEntryOffset;
+ ULONG NumberOfThreads;
+ // 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
+ ULONGLONG WorkingSetPrivateSize;
+ ULONG HardFaultCount;
+ ULONG Reserved1;
+ ULONGLONG CycleTime;
+ ULONGLONG CreateTime;
+ ULONGLONG UserTime;
+ ULONGLONG KernelTime;
+ UNICODE_STRING ImageName;
+ KPRIORITY BasePriority;
+ HANDLE ProcessId;
+ HANDLE ParentProcessId;
+ ULONG HandleCount;
+ ULONG Reserved2[2];
+ // Padding here in 64-bit
+ VM_COUNTERS VirtualMemoryCounters;
+ size_t Reserved3;
+ IO_COUNTERS IoCounters;
+ SYSTEM_THREAD_INFORMATION Threads[1];
+};
+#if _M_X64
+static_assert(sizeof(SYSTEM_PROCESS_INFORMATION) == 336,
+ "Structure size mismatch");
+#else
+static_assert(sizeof(SYSTEM_PROCESS_INFORMATION) == 248,
+ "Structure size mismatch");
+#endif
+
+// ntstatus.h conflicts with windows.h so define this locally.
+#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
+#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
+#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
+
+typedef NTSTATUS(WINAPI* NTQUERYSYSTEMINFORMATION)(
+ SYSTEM_INFORMATION_CLASS SystemInformationClass,
+ PVOID SystemInformation,
+ ULONG SystemInformationLength,
+ PULONG ReturnLength);
+
+__declspec(noreturn) void oops(const char* pMessage) {
+ printf("%s\n", pMessage);
+ exit(0);
+}
+
+// Simple memory buffer wrapper for passing the data out of
+// QuerySystemProcessInformation.
+class ByteBuffer {
+ public:
+ explicit ByteBuffer(size_t capacity) : size_(0), capacity_(0) {
+ if (capacity > 0)
+ grow(capacity);
+ }
+
+ ~ByteBuffer() {}
+
+ BYTE* data() { return data_.get(); }
+
+ size_t size() { return size_; }
+
+ void set_size(size_t new_size) { size_ = new_size; }
+
+ size_t capacity() { return capacity_; }
+
+ void grow(size_t new_capacity) {
+ capacity_ = new_capacity;
+ data_.reset(new BYTE[new_capacity]);
+ }
+
+ private:
+ std::unique_ptr<BYTE[]> data_;
+ size_t size_;
+ size_t capacity_;
+
+ ByteBuffer& operator=(const ByteBuffer&) = delete;
+ ByteBuffer(const ByteBuffer&) = delete;
+};
+
+// Wrapper for NtQuerySystemProcessInformation with buffer reallocation logic.
+bool QuerySystemProcessInformation(ByteBuffer* buffer) {
+ typedef NTSTATUS(WINAPI * NTQUERYSYSTEMINFORMATION)(
+ SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation,
+ ULONG SystemInformationLength, PULONG ReturnLength);
+
+ HMODULE ntdll = GetModuleHandle(L"ntdll.dll");
+ if (!ntdll) {
+ oops("Couldn't load ntdll.dll");
+ }
+
+ NTQUERYSYSTEMINFORMATION nt_query_system_information_ptr =
+ reinterpret_cast<NTQUERYSYSTEMINFORMATION>(
+ GetProcAddress(ntdll, "NtQuerySystemInformation"));
+ if (!nt_query_system_information_ptr)
+ oops("Couldn't find NtQuerySystemInformation");
+
+ NTSTATUS result;
+
+ // There is a potential race condition between growing the buffer and new
+ // processes being created. Try a few times before giving up.
+ for (int i = 0; i < 10; i++) {
+ ULONG data_size = 0;
+ ULONG buffer_size = static_cast<ULONG>(buffer->capacity());
+ result = nt_query_system_information_ptr(
+ SystemProcessInformation, buffer->data(), buffer_size, &data_size);
+
+ if (result == STATUS_SUCCESS) {
+ buffer->set_size(data_size);
+ break;
+ }
+
+ if (result == STATUS_INFO_LENGTH_MISMATCH ||
+ result == STATUS_BUFFER_TOO_SMALL) {
+ // Insufficient buffer. Grow to the returned |data_size| plus 10% extra
+ // to avoid frequent reallocations and try again.
+ buffer->grow(static_cast<ULONG>(data_size * 1.1));
+ } else {
+ // An error other than the two above.
+ break;
+ }
+ }
+
+ return result == STATUS_SUCCESS;
+}
+
+SystemInformationSampler::SystemInformationSampler(
+ const wchar_t* process_name) {
+ lstrcpyn(target_process_name_, process_name,
+ sizeof(target_process_name_) / sizeof(wchar_t));
+
+ QueryPerformanceFrequency(&perf_frequency_);
+ QueryPerformanceCounter(&initial_counter_);
+}
+
+SystemInformationSampler::~SystemInformationSampler() {}
+
+std::unique_ptr<ProcessDataSnapshot> SystemInformationSampler::TakeSnapshot() {
+ // Preallocate the buffer with the size determined on the previous call to
+ // QuerySystemProcessInformation. This should be sufficient most of the time.
+ // QuerySystemProcessInformation will grow the buffer if necessary.
+ ByteBuffer data_buffer(previous_buffer_size_);
+
+ if (!QuerySystemProcessInformation(&data_buffer))
+ return std::unique_ptr<ProcessDataSnapshot>();
+
+ previous_buffer_size_ = data_buffer.capacity();
+
+ std::unique_ptr<ProcessDataSnapshot> snapshot(new ProcessDataSnapshot);
+
+ LARGE_INTEGER perf_counter_value;
+ QueryPerformanceCounter(&perf_counter_value);
+ snapshot->timestamp = static_cast<double>(
+ (perf_counter_value.QuadPart - initial_counter_.QuadPart) /
+ perf_frequency_.QuadPart);
+
+ for (size_t offset = 0; offset < data_buffer.size();) {
+ auto pi = reinterpret_cast<const SYSTEM_PROCESS_INFORMATION*>(
+ data_buffer.data() + offset);
+
+ // Validate that the offset is valid and all needed data is within
+ // the buffer boundary.
+ if (offset + sizeof(SYSTEM_PROCESS_INFORMATION) > data_buffer.size())
+ break;
+ if (offset + sizeof(SYSTEM_PROCESS_INFORMATION) +
+ (pi->NumberOfThreads - 1) * sizeof(SYSTEM_THREAD_INFORMATION) >
+ data_buffer.size())
+ break;
+
+ if (pi->ImageName.Buffer) {
+ // Validate that the image name is within the buffer boundary.
+ // ImageName.Length seems to be in bytes rather than characters.
+ size_t image_name_offset =
+ reinterpret_cast<BYTE*>(pi->ImageName.Buffer) - data_buffer.data();
+ if (image_name_offset + pi->ImageName.Length > data_buffer.size())
+ break;
+
+ // Check if this is a chrome process. Ignore all other processes.
+ if (wcsncmp(target_process_name_filter(), pi->ImageName.Buffer,
+ lstrlen(target_process_name_filter())) == 0) {
+ // Collect enough data to be able to do a diff between two snapshots.
+ // Some threads might stop or new threads might be created between two
+ // snapshots. If a thread with a large number of context switches gets
+ // terminated the total number of context switches for the process might
+ // go down and the delta would be negative.
+ // To avoid that we need to compare thread IDs between two snapshots and
+ // not count context switches for threads that are missing in the most
+ // recent snapshot.
+ ProcessData process_data;
+
+ process_data.cpu_time = pi->KernelTime + pi->UserTime;
+ process_data.working_set = pi->WorkingSetPrivateSize;
+
+ // Iterate over threads and store each thread's ID and number of context
+ // switches.
+ for (ULONG thread_index = 0; thread_index < pi->NumberOfThreads;
+ ++thread_index) {
+ const SYSTEM_THREAD_INFORMATION* ti = &pi->Threads[thread_index];
+ if (ti->ClientId.UniqueProcess != pi->ProcessId)
+ continue;
+
+ ThreadData thread_data;
+ thread_data.thread_id = ti->ClientId.UniqueThread;
+ thread_data.context_switches = ti->ContextSwitchCount;
+ process_data.threads.push_back(thread_data);
+ }
+
+ // Order thread data by thread ID to help diff two snapshots.
+ std::sort(process_data.threads.begin(), process_data.threads.end(),
+ [](const ThreadData& l, const ThreadData r) {
+ return l.thread_id < r.thread_id;
+ });
+
+ snapshot->processes.insert(
+ std::make_pair(pi->ProcessId, std::move(process_data)));
+ }
+ }
+
+ // Check for end of the list.
+ if (!pi->NextEntryOffset)
+ break;
+
+ // Jump to the next entry.
+ offset += pi->NextEntryOffset;
+ }
+
+ return snapshot;
+}
« no previous file with comments | « tools/win/IdleWakeups/system_information_sampler.h ('k') | tools/win/OWNERS » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698