diff options
Diffstat (limited to 'ipc/chromium/src/base/process_util_win.cc')
-rw-r--r-- | ipc/chromium/src/base/process_util_win.cc | 489 |
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 |