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

Unified Diff: util/win/process_info.cc

Issue 977003003: win: Add implementation of ProcessInfo (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: . Created 5 years, 10 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
Index: util/win/process_info.cc
diff --git a/util/win/process_info.cc b/util/win/process_info.cc
new file mode 100644
index 0000000000000000000000000000000000000000..78c00db7d7465172481a2138827ba15d903efbf1
--- /dev/null
+++ b/util/win/process_info.cc
@@ -0,0 +1,199 @@
+// Copyright 2015 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/process_info.h"
+
+namespace crashpad {
+
+namespace {
+
+std::wstring ReadUnicodeString(HANDLE process, const UNICODE_STRING& us) {
Mark Mentovai 2015/03/05 17:32:22 In returning, this should have a way to distinguis
scottmg 2015/03/05 20:31:06 Done.
+ std::wstring str;
+ if (us.Length > 0) {
+ str.resize(us.Length / sizeof(wchar_t));
Mark Mentovai 2015/03/05 17:32:22 DCHECK_EQ(us.Length % sizeof(wchar_t), 0)
scottmg 2015/03/05 20:31:06 Done.
+ SIZE_T bytes_read;
+ if (!ReadProcessMemory(
Mark Mentovai 2015/03/05 17:32:22 If I’m reading this correctly, for cross-bitted op
scottmg 2015/03/05 20:31:06 I haven't tested it yet, but yes, I believe that's
+ process, us.Buffer, &str[0], us.Length, &bytes_read) ||
+ bytes_read != us.Length) {
+ PLOG(ERROR) << "ReadProcessMemory UNICODE_STRING";
Mark Mentovai 2015/03/05 17:32:22 #include "base/logging.h". Also, PLOG is incorrect
scottmg 2015/03/05 20:31:06 Done.
+ return std::wstring();
+ }
+ }
+ return str;
+}
+
+template <class T> bool ReadStruct(HANDLE process, void* at, T* into) {
Mark Mentovai 2015/03/05 17:32:22 const void* at, but Using pointer types to refer
scottmg 2015/03/05 20:31:06 Switched to uintptr_t. ReadProcessMemory just uses
+ SIZE_T bytes_read;
+ if (!ReadProcessMemory(process, at, into, sizeof(T), &bytes_read) ||
+ bytes_read != sizeof(T)) {
+ // We don't have a name for the type we're reading, so include the signature
+ // to get the type of T.
+ PLOG(ERROR) << "ReadProcessMemory: " << __FUNCSIG__;
+ return false;
+ }
+ return true;
+}
+
+// This is similar to PEB_LDR_DATA in winternl.h, but includes the
+// InInitializationOrderModuleList field.
+struct FULL_PEB_LDR_DATA {
Mark Mentovai 2015/03/05 17:32:22 Since you don’t need Length or Initialized, you ca
scottmg 2015/03/05 20:31:06 Done.
+ ULONG Length;
+ BOOLEAN Initialized;
+ PVOID Reserved;
+ LIST_ENTRY InLoadOrderModuleList;
Mark Mentovai 2015/03/05 17:32:22 What’s the difference between load order and initi
scottmg 2015/03/05 20:31:07 Load and Memory order seem to always be the same (
+ LIST_ENTRY InMemoryOrderModuleList;
+ LIST_ENTRY InInitializationOrderModuleList;
+};
+
+} // namespace
+
+ProcessInfo::ProcessInfo()
+ : process_basic_information_(),
+ command_line_(),
+ is64bit_(false),
+ iswow64_(FALSE) {
+}
+
+ProcessInfo::~ProcessInfo() {
+}
+
+bool ProcessInfo::Initialize(HANDLE process) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ decltype(NtQueryInformationProcess)* nt_query_information_process =
Mark Mentovai 2015/03/05 17:32:22 I like to wrap these kinds of things in their own
scottmg 2015/03/05 20:31:06 Done.
+ reinterpret_cast<decltype(NtQueryInformationProcess)*>(GetProcAddress(
+ LoadLibrary(L"ntdll.dll"), "NtQueryInformationProcess"));
+ if (!nt_query_information_process) {
+ PLOG(ERROR) << "GetProcAddress NtQueryInformationProcess failed";
+ return false;
+ }
+
+ ULONG bytes_returned;
+ NTSTATUS status =
+ nt_query_information_process(process,
+ ProcessBasicInformation,
+ &process_basic_information_,
+ sizeof(process_basic_information_),
+ &bytes_returned);
+ if (status < 0 || bytes_returned != sizeof(process_basic_information_)) {
+ LOG(ERROR) << "NtQueryInformationProcess";
Mark Mentovai 2015/03/05 17:32:22 If status < 0, the value of status might be a good
scottmg 2015/03/05 20:31:07 Done.
+ return false;
+ }
+
+ // Try to read the process environment block.
+ PEB peb;
+ if (!ReadStruct(process, process_basic_information_.PebBaseAddress, &peb))
Mark Mentovai 2015/03/05 17:32:22 This looks like the first point where cross-bitted
scottmg 2015/03/05 20:31:06 Right, good point. For now I've moved the 32/64 c
+ return false;
+
+ RTL_USER_PROCESS_PARAMETERS process_parameters;
+ if (!ReadStruct(process, peb.ProcessParameters, &process_parameters))
+ return false;
+
+ command_line_ =
+ ReadUnicodeString(process, process_parameters.CommandLine);
+ if (command_line_.empty())
+ return false;
+
+ FULL_PEB_LDR_DATA peb_ldr_data;
+ if (!ReadStruct(process, peb.Ldr, &peb_ldr_data))
+ return false;
+
+ // Walk the PEB LDR structure (doubly-linked list) to get the list of loaded
+ // modules. We awkwardly use the Reserved fields of LDR_DATA_TABLE_ENTRY to
+ // get the modules in InitializationOrder rather than MemoryOrder.
+ LIST_ENTRY* cur = peb_ldr_data.InInitializationOrderModuleList.Flink;
+ LIST_ENTRY* last = peb_ldr_data.InInitializationOrderModuleList.Blink;
+ for (;;) {
Mark Mentovai 2015/03/05 17:32:21 You could hoist some stuff into the for construct
scottmg 2015/03/05 20:31:06 Some hoisted. Unfortunately, it's a "back" pointer
+ // |cur| is the pointer to the LIST_ENTRY embedded in the
+ // LDR_DATA_TABLE_ENTRY, in the target process's address space. So we need
+ // to read from the target, and also offset back to the beginning of the
+ // structure.
+ LDR_DATA_TABLE_ENTRY ldr_data_table_entry;
+ if (!ReadStruct(process,
Mark Mentovai 2015/03/05 17:32:22 Your CL description had me geared up for a truly h
scottmg 2015/03/05 20:31:07 All in the eye of the beholder, I suppose. :)
+ reinterpret_cast<char*>(cur) -
+ offsetof(LDR_DATA_TABLE_ENTRY, Reserved2),
Mark Mentovai 2015/03/05 17:32:22 I see. Presumably, LDR_DATA_TABLE_ENTRY really st
scottmg 2015/03/05 20:31:07 That's right.
+ &ldr_data_table_entry))
+ return false;
Mark Mentovai 2015/03/05 17:32:22 {} around this, its controlling condition is so lo
scottmg 2015/03/05 20:31:06 Done.
+ // TODO(scottmg): Capture TimeDateStamp, Checksum, etc. too?
+ std::wstring module =
+ ReadUnicodeString(process, ldr_data_table_entry.FullDllName);
+ if (module.empty())
+ return false;
Mark Mentovai 2015/03/05 17:32:22 A bad module should probably invalidate that modul
scottmg 2015/03/05 20:31:06 Done. I was concerned that a bad PEB meant the wor
+ modules_.push_back(module);
+ if (cur == last)
+ break;
+ cur = reinterpret_cast<LIST_ENTRY*>(&ldr_data_table_entry.Reserved2)->Flink;
+ }
+
+ decltype(IsWow64Process)* is_wow64_process =
Mark Mentovai 2015/03/05 17:32:22 If you break this one into its own function, you c
scottmg 2015/03/05 20:31:07 Done.
+ reinterpret_cast<decltype(IsWow64Process)*>(
+ GetProcAddress(LoadLibrary(L"kernel32.dll"), "IsWow64Process"));
+ if (!is_wow64_process) {
+ // This means kernel32 doesn't implement this function, so there's no such
+ // thing as WoW64 on this OS.
+ iswow64_ = FALSE;
+ } else if (!is_wow64_process(process, &iswow64_)) {
+ PLOG(ERROR) << "IsWow64Process";
+ return false;
+ }
+
+ if (iswow64_) {
+ // If it's WoW64, then it's 32-on-64.
+ is64bit_ = false;
+ } else {
+ // Otherwise, it's either 32 on 32, or 64 on 64. Use GetSystemInfo() to
+ // distinguish between these two cases.
+ SYSTEM_INFO system_info;
+ GetSystemInfo(&system_info);
+ is64bit_ =
+ system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
+ }
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+bool ProcessInfo::Is64Bit() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return is64bit_;
+}
+
+bool ProcessInfo::IsWow64() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return iswow64_;
+}
+
+pid_t ProcessInfo::ProcessID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return process_basic_information_.UniqueProcessId;
+}
+
+pid_t ProcessInfo::ParentProcessID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return reinterpret_cast<ULONG_PTR>(process_basic_information_.Reserved3);
Mark Mentovai 2015/03/05 17:32:22 Can process_basic_information_ be a union type tha
scottmg 2015/03/05 20:31:06 Added FULL_... for this type too.
+}
+
+bool ProcessInfo::CommandLine(std::wstring* command_line) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *command_line = command_line_;
+ return true;
+}
+
+bool ProcessInfo::Modules(std::vector<std::wstring>* modules) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *modules = modules_;
+ return true;
+}
+
+} // namespace crashpad

Powered by Google App Engine
This is Rietveld 408576698