summaryrefslogtreecommitdiffstats
path: root/ipc/chromium/src/base/process_util_win.cc
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/chromium/src/base/process_util_win.cc')
-rw-r--r--ipc/chromium/src/base/process_util_win.cc489
1 files changed, 489 insertions, 0 deletions
diff --git a/ipc/chromium/src/base/process_util_win.cc b/ipc/chromium/src/base/process_util_win.cc
new file mode 100644
index 000000000..12fbc23a5
--- /dev/null
+++ b/ipc/chromium/src/base/process_util_win.cc
@@ -0,0 +1,489 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2009 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.
+
+// We need extended process and thread attribute support
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+
+#include "base/process_util.h"
+
+#include <windows.h>
+#include <winternl.h>
+#include <psapi.h>
+
+#include "base/histogram.h"
+#include "base/logging.h"
+#include "base/win_util.h"
+
+#include <algorithm>
+#include "prenv.h"
+
+#include "mozilla/WindowsVersion.h"
+
+namespace {
+
+// System pagesize. This value remains constant on x86/64 architectures.
+const int PAGESIZE_KB = 4;
+
+// HeapSetInformation function pointer.
+typedef BOOL (WINAPI* HeapSetFn)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T);
+
+typedef BOOL (WINAPI * InitializeProcThreadAttributeListFn)(
+ LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,
+ DWORD dwAttributeCount,
+ DWORD dwFlags,
+ PSIZE_T lpSize
+);
+
+typedef BOOL (WINAPI * DeleteProcThreadAttributeListFn)(
+ LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList
+);
+
+typedef BOOL (WINAPI * UpdateProcThreadAttributeFn)(
+ LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,
+ DWORD dwFlags,
+ DWORD_PTR Attribute,
+ PVOID lpValue,
+ SIZE_T cbSize,
+ PVOID lpPreviousValue,
+ PSIZE_T lpReturnSize
+);
+
+static InitializeProcThreadAttributeListFn InitializeProcThreadAttributeListPtr;
+static DeleteProcThreadAttributeListFn DeleteProcThreadAttributeListPtr;
+static UpdateProcThreadAttributeFn UpdateProcThreadAttributePtr;
+
+static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG");
+
+} // namespace
+
+namespace base {
+
+ProcessId GetCurrentProcId() {
+ return ::GetCurrentProcessId();
+}
+
+ProcessHandle GetCurrentProcessHandle() {
+ return ::GetCurrentProcess();
+}
+
+bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) {
+ // TODO(phajdan.jr): Take even more permissions out of this list.
+ ProcessHandle result = OpenProcess(PROCESS_DUP_HANDLE |
+ PROCESS_TERMINATE |
+ PROCESS_QUERY_INFORMATION |
+ SYNCHRONIZE,
+ FALSE, pid);
+
+ if (result == NULL) {
+ return false;
+ }
+
+ *handle = result;
+ return true;
+}
+
+bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) {
+ ProcessHandle result = OpenProcess(PROCESS_DUP_HANDLE |
+ PROCESS_TERMINATE |
+ PROCESS_QUERY_INFORMATION |
+ PROCESS_VM_READ |
+ SYNCHRONIZE,
+ FALSE, pid);
+
+ if (result == NULL) {
+ return false;
+ }
+
+ *handle = result;
+ return true;
+}
+
+void CloseProcessHandle(ProcessHandle process) {
+ // closing a handle twice on Windows can be catastrophic - after the first
+ // close the handle value may be reused, so the second close will kill that
+ // other new handle.
+ BOOL ok = CloseHandle(process);
+ DCHECK(ok);
+}
+
+// Helper for GetProcId()
+bool GetProcIdViaGetProcessId(ProcessHandle process, DWORD* id) {
+ // Dynamically get a pointer to GetProcessId().
+ typedef DWORD (WINAPI *GetProcessIdFunction)(HANDLE);
+ static GetProcessIdFunction GetProcessIdPtr = NULL;
+ static bool initialize_get_process_id = true;
+ if (initialize_get_process_id) {
+ initialize_get_process_id = false;
+ HMODULE kernel32_handle = GetModuleHandle(L"kernel32.dll");
+ if (!kernel32_handle) {
+ NOTREACHED();
+ return false;
+ }
+ GetProcessIdPtr = reinterpret_cast<GetProcessIdFunction>(GetProcAddress(
+ kernel32_handle, "GetProcessId"));
+ }
+ if (!GetProcessIdPtr)
+ return false;
+ // Ask for the process ID.
+ *id = (*GetProcessIdPtr)(process);
+ return true;
+}
+
+// Helper for GetProcId()
+bool GetProcIdViaNtQueryInformationProcess(ProcessHandle process, DWORD* id) {
+ // Dynamically get a pointer to NtQueryInformationProcess().
+ typedef NTSTATUS (WINAPI *NtQueryInformationProcessFunction)(
+ HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
+ static NtQueryInformationProcessFunction NtQueryInformationProcessPtr = NULL;
+ static bool initialize_query_information_process = true;
+ if (initialize_query_information_process) {
+ initialize_query_information_process = false;
+ // According to nsylvain, ntdll.dll is guaranteed to be loaded, even though
+ // the Windows docs seem to imply that you should LoadLibrary() it.
+ HMODULE ntdll_handle = GetModuleHandle(L"ntdll.dll");
+ if (!ntdll_handle) {
+ NOTREACHED();
+ return false;
+ }
+ NtQueryInformationProcessPtr =
+ reinterpret_cast<NtQueryInformationProcessFunction>(GetProcAddress(
+ ntdll_handle, "NtQueryInformationProcess"));
+ }
+ if (!NtQueryInformationProcessPtr)
+ return false;
+ // Ask for the process ID.
+ PROCESS_BASIC_INFORMATION info;
+ ULONG bytes_returned;
+ NTSTATUS status = (*NtQueryInformationProcessPtr)(process,
+ ProcessBasicInformation,
+ &info, sizeof info,
+ &bytes_returned);
+ if (!SUCCEEDED(status) || (bytes_returned != (sizeof info)))
+ return false;
+
+ *id = static_cast<DWORD>(info.UniqueProcessId);
+ return true;
+}
+
+ProcessId GetProcId(ProcessHandle process) {
+ // Get a handle to |process| that has PROCESS_QUERY_INFORMATION rights.
+ HANDLE current_process = GetCurrentProcess();
+ HANDLE process_with_query_rights;
+ if (DuplicateHandle(current_process, process, current_process,
+ &process_with_query_rights, PROCESS_QUERY_INFORMATION,
+ false, 0)) {
+ // Try to use GetProcessId(), if it exists. Fall back on
+ // NtQueryInformationProcess() otherwise (< Win XP SP1).
+ DWORD id;
+ bool success =
+ GetProcIdViaGetProcessId(process_with_query_rights, &id) ||
+ GetProcIdViaNtQueryInformationProcess(process_with_query_rights, &id);
+ CloseHandle(process_with_query_rights);
+ if (success)
+ return id;
+ }
+
+ // We're screwed.
+ NOTREACHED();
+ return 0;
+}
+
+// from sandbox_policy_base.cc in a later version of the chromium ipc code...
+bool IsInheritableHandle(HANDLE handle) {
+ if (!handle)
+ return false;
+ if (handle == INVALID_HANDLE_VALUE)
+ return false;
+ // File handles (FILE_TYPE_DISK) and pipe handles are known to be
+ // inheritable. Console handles (FILE_TYPE_CHAR) are not
+ // inheritable via PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
+ DWORD handle_type = GetFileType(handle);
+ return handle_type == FILE_TYPE_DISK || handle_type == FILE_TYPE_PIPE;
+}
+
+void LoadThreadAttributeFunctions() {
+ HMODULE kernel32 = GetModuleHandle(L"kernel32.dll");
+ InitializeProcThreadAttributeListPtr =
+ reinterpret_cast<InitializeProcThreadAttributeListFn>
+ (GetProcAddress(kernel32, "InitializeProcThreadAttributeList"));
+ DeleteProcThreadAttributeListPtr =
+ reinterpret_cast<DeleteProcThreadAttributeListFn>
+ (GetProcAddress(kernel32, "DeleteProcThreadAttributeList"));
+ UpdateProcThreadAttributePtr =
+ reinterpret_cast<UpdateProcThreadAttributeFn>
+ (GetProcAddress(kernel32, "UpdateProcThreadAttribute"));
+}
+
+// Creates and returns a "thread attribute list" to pass to the child process.
+// On return, is a pointer to a THREAD_ATTRIBUTE_LIST or NULL if either the
+// functions we need aren't available (eg, XP or earlier) or the functions we
+// need failed.
+// The result of this function must be passed to FreeThreadAttributeList.
+// Note that the pointer to the HANDLE array ends up embedded in the result of
+// this function and must stay alive until FreeThreadAttributeList is called,
+// hence it is passed in so the owner is the caller of this function.
+LPPROC_THREAD_ATTRIBUTE_LIST CreateThreadAttributeList(HANDLE *handlesToInherit,
+ int handleCount) {
+ if (!InitializeProcThreadAttributeListPtr ||
+ !DeleteProcThreadAttributeListPtr ||
+ !UpdateProcThreadAttributePtr)
+ LoadThreadAttributeFunctions();
+ // shouldn't happen as we are only called for Vista+, but better safe than sorry...
+ if (!InitializeProcThreadAttributeListPtr ||
+ !DeleteProcThreadAttributeListPtr ||
+ !UpdateProcThreadAttributePtr)
+ return NULL;
+
+ SIZE_T threadAttrSize;
+ LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList;
+
+ if (!(*InitializeProcThreadAttributeListPtr)(NULL, 1, 0, &threadAttrSize) &&
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ goto fail;
+ lpAttributeList = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>
+ (malloc(threadAttrSize));
+ if (!lpAttributeList ||
+ !(*InitializeProcThreadAttributeListPtr)(lpAttributeList, 1, 0, &threadAttrSize))
+ goto fail;
+
+ if (!(*UpdateProcThreadAttributePtr)(lpAttributeList,
+ 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+ handlesToInherit,
+ sizeof(handlesToInherit[0]) * handleCount,
+ NULL, NULL)) {
+ (*DeleteProcThreadAttributeListPtr)(lpAttributeList);
+ goto fail;
+ }
+ return lpAttributeList;
+
+fail:
+ if (lpAttributeList)
+ free(lpAttributeList);
+ return NULL;
+}
+
+// Frees the data returned by CreateThreadAttributeList.
+void FreeThreadAttributeList(LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList) {
+ // must be impossible to get a NULL DeleteProcThreadAttributeListPtr, as
+ // we already checked it existed when creating the data we are now freeing.
+ (*DeleteProcThreadAttributeListPtr)(lpAttributeList);
+ free(lpAttributeList);
+}
+
+bool LaunchApp(const std::wstring& cmdline,
+ bool wait, bool start_hidden, ProcessHandle* process_handle) {
+
+ // We want to inherit the std handles so dump() statements and assertion
+ // messages in the child process can be seen - but we *do not* want to
+ // blindly have all handles inherited. Vista and later has a technique
+ // where only specified handles are inherited - so we use this technique if
+ // we can. If that technique isn't available (or it fails), we just don't
+ // inherit anything. This can cause us a problem for Windows XP testing,
+ // because we sometimes need the handles to get inherited for test logging to
+ // work. So we also inherit when a specific environment variable is set.
+ DWORD dwCreationFlags = 0;
+ BOOL bInheritHandles = FALSE;
+ // We use a STARTUPINFOEX, but if we can't do the thread attribute thing, we
+ // just pass the size of a STARTUPINFO.
+ STARTUPINFOEX startup_info_ex;
+ ZeroMemory(&startup_info_ex, sizeof(startup_info_ex));
+ STARTUPINFO &startup_info = startup_info_ex.StartupInfo;
+ startup_info.cb = sizeof(startup_info);
+ startup_info.dwFlags = STARTF_USESHOWWINDOW;
+ startup_info.wShowWindow = start_hidden ? SW_HIDE : SW_SHOW;
+
+ LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL;
+ // Don't even bother trying pre-Vista...
+ if (mozilla::IsVistaOrLater()) {
+ // setup our handle array first - if we end up with no handles that can
+ // be inherited we can avoid trying to do the ThreadAttributeList dance...
+ HANDLE handlesToInherit[2];
+ int handleCount = 0;
+ HANDLE stdOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
+ HANDLE stdErr = ::GetStdHandle(STD_ERROR_HANDLE);
+
+ if (IsInheritableHandle(stdOut))
+ handlesToInherit[handleCount++] = stdOut;
+ if (stdErr != stdOut && IsInheritableHandle(stdErr))
+ handlesToInherit[handleCount++] = stdErr;
+
+ if (handleCount) {
+ lpAttributeList = CreateThreadAttributeList(handlesToInherit, handleCount);
+ if (lpAttributeList) {
+ // it's safe to inherit handles, so arrange for that...
+ startup_info.cb = sizeof(startup_info_ex);
+ startup_info.dwFlags |= STARTF_USESTDHANDLES;
+ startup_info.hStdOutput = stdOut;
+ startup_info.hStdError = stdErr;
+ startup_info.hStdInput = INVALID_HANDLE_VALUE;
+ startup_info_ex.lpAttributeList = lpAttributeList;
+ dwCreationFlags |= EXTENDED_STARTUPINFO_PRESENT;
+ bInheritHandles = TRUE;
+ }
+ }
+ } else if (PR_GetEnv("MOZ_WIN_INHERIT_STD_HANDLES_PRE_VISTA")) {
+ // Even if we can't limit what gets inherited, we sometimes want to inherit
+ // stdout/err for testing purposes.
+ startup_info.dwFlags |= STARTF_USESTDHANDLES;
+ startup_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
+ startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE);
+ startup_info.hStdInput = INVALID_HANDLE_VALUE;
+ bInheritHandles = TRUE;
+ }
+
+ PROCESS_INFORMATION process_info;
+ BOOL createdOK = CreateProcess(NULL,
+ const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL,
+ bInheritHandles, dwCreationFlags, NULL, NULL,
+ &startup_info, &process_info);
+ if (lpAttributeList)
+ FreeThreadAttributeList(lpAttributeList);
+ if (!createdOK)
+ return false;
+
+ gProcessLog.print("==> process %d launched child process %d (%S)\n",
+ GetCurrentProcId(),
+ process_info.dwProcessId,
+ cmdline.c_str());
+
+ // Handles must be closed or they will leak
+ CloseHandle(process_info.hThread);
+
+ if (wait)
+ WaitForSingleObject(process_info.hProcess, INFINITE);
+
+ // If the caller wants the process handle, we won't close it.
+ if (process_handle) {
+ *process_handle = process_info.hProcess;
+ } else {
+ CloseHandle(process_info.hProcess);
+ }
+ return true;
+}
+
+bool LaunchApp(const CommandLine& cl,
+ bool wait, bool start_hidden, ProcessHandle* process_handle) {
+ return LaunchApp(cl.command_line_string(), wait,
+ start_hidden, process_handle);
+}
+
+bool KillProcess(ProcessHandle process, int exit_code, bool wait) {
+ bool result = (TerminateProcess(process, exit_code) != FALSE);
+ if (result && wait) {
+ // The process may not end immediately due to pending I/O
+ if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000))
+ DLOG(ERROR) << "Error waiting for process exit: " << GetLastError();
+ } else if (!result) {
+ DLOG(ERROR) << "Unable to terminate process: " << GetLastError();
+ }
+ return result;
+}
+
+bool DidProcessCrash(bool* child_exited, ProcessHandle handle) {
+ DWORD exitcode = 0;
+
+ if (child_exited)
+ *child_exited = true; // On Windows it an error to call this function if
+ // the child hasn't already exited.
+ if (!::GetExitCodeProcess(handle, &exitcode)) {
+ NOTREACHED();
+ return false;
+ }
+ if (exitcode == STILL_ACTIVE) {
+ // The process is likely not dead or it used 0x103 as exit code.
+ NOTREACHED();
+ return false;
+ }
+
+ // Warning, this is not generic code; it heavily depends on the way
+ // the rest of the code kills a process.
+
+ if (exitcode == PROCESS_END_NORMAL_TERMINATON ||
+ exitcode == PROCESS_END_KILLED_BY_USER ||
+ exitcode == PROCESS_END_PROCESS_WAS_HUNG ||
+ exitcode == 0xC0000354 || // STATUS_DEBUGGER_INACTIVE.
+ exitcode == 0xC000013A || // Control-C/end session.
+ exitcode == 0x40010004) { // Debugger terminated process/end session.
+ return false;
+ }
+
+ return true;
+}
+
+void SetCurrentProcessPrivileges(ChildPrivileges privs) {
+
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// ProcesMetrics
+
+ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process),
+ last_time_(0),
+ last_system_time_(0) {
+ SYSTEM_INFO system_info;
+ GetSystemInfo(&system_info);
+ processor_count_ = system_info.dwNumberOfProcessors;
+}
+
+// static
+ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
+ return new ProcessMetrics(process);
+}
+
+ProcessMetrics::~ProcessMetrics() { }
+
+static uint64_t FileTimeToUTC(const FILETIME& ftime) {
+ LARGE_INTEGER li;
+ li.LowPart = ftime.dwLowDateTime;
+ li.HighPart = ftime.dwHighDateTime;
+ return li.QuadPart;
+}
+
+int ProcessMetrics::GetCPUUsage() {
+ FILETIME now;
+ FILETIME creation_time;
+ FILETIME exit_time;
+ FILETIME kernel_time;
+ FILETIME user_time;
+
+ GetSystemTimeAsFileTime(&now);
+
+ if (!GetProcessTimes(process_, &creation_time, &exit_time,
+ &kernel_time, &user_time)) {
+ // We don't assert here because in some cases (such as in the Task Manager)
+ // we may call this function on a process that has just exited but we have
+ // not yet received the notification.
+ return 0;
+ }
+ int64_t system_time = (FileTimeToUTC(kernel_time) + FileTimeToUTC(user_time)) /
+ processor_count_;
+ int64_t time = FileTimeToUTC(now);
+
+ if ((last_system_time_ == 0) || (last_time_ == 0)) {
+ // First call, just set the last values.
+ last_system_time_ = system_time;
+ last_time_ = time;
+ return 0;
+ }
+
+ int64_t system_time_delta = system_time - last_system_time_;
+ int64_t time_delta = time - last_time_;
+ DCHECK(time_delta != 0);
+ if (time_delta == 0)
+ return 0;
+
+ // We add time_delta / 2 so the result is rounded.
+ int cpu = static_cast<int>((system_time_delta * 100 + time_delta / 2) /
+ time_delta);
+
+ last_system_time_ = system_time;
+ last_time_ = time;
+
+ return cpu;
+}
+
+} // namespace base