ipc/chromium/src/base/process_util_win.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
     2 // Use of this source code is governed by a BSD-style license that can be
     3 // found in the LICENSE file.
     5 // We need extended process and thread attribute support
     6 #undef _WIN32_WINNT
     7 #define _WIN32_WINNT 0x0600
     9 #include "base/process_util.h"
    11 #include <windows.h>
    12 #include <winternl.h>
    13 #include <psapi.h>
    15 #include "base/debug_util.h"
    16 #include "base/histogram.h"
    17 #include "base/logging.h"
    18 #include "base/scoped_handle_win.h"
    19 #include "base/scoped_ptr.h"
    20 #include "base/win_util.h"
    22 #include <algorithm>
    24 namespace {
    26 // System pagesize. This value remains constant on x86/64 architectures.
    27 const int PAGESIZE_KB = 4;
    29 // HeapSetInformation function pointer.
    30 typedef BOOL (WINAPI* HeapSetFn)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T);
    32 typedef BOOL (WINAPI * InitializeProcThreadAttributeListFn)(
    33   LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,
    34   DWORD dwAttributeCount,
    35   DWORD dwFlags,
    36   PSIZE_T lpSize
    37 );
    39 typedef BOOL (WINAPI * DeleteProcThreadAttributeListFn)(
    40   LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList
    41 );
    43 typedef BOOL (WINAPI * UpdateProcThreadAttributeFn)(
    44   LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,
    45   DWORD dwFlags,
    46   DWORD_PTR Attribute,
    47   PVOID lpValue,
    48   SIZE_T cbSize,
    49   PVOID lpPreviousValue,
    50   PSIZE_T lpReturnSize
    51 );
    53 static InitializeProcThreadAttributeListFn InitializeProcThreadAttributeListPtr;
    54 static DeleteProcThreadAttributeListFn DeleteProcThreadAttributeListPtr;
    55 static UpdateProcThreadAttributeFn UpdateProcThreadAttributePtr;
    57 static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG");
    59 }  // namespace
    61 namespace base {
    63 ProcessId GetCurrentProcId() {
    64   return ::GetCurrentProcessId();
    65 }
    67 ProcessHandle GetCurrentProcessHandle() {
    68   return ::GetCurrentProcess();
    69 }
    71 bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) {
    72   // TODO(phajdan.jr): Take even more permissions out of this list.
    73   ProcessHandle result = OpenProcess(PROCESS_DUP_HANDLE |
    74                                          PROCESS_TERMINATE |
    75                                          PROCESS_QUERY_INFORMATION |
    76                                          SYNCHRONIZE,
    77                                      FALSE, pid);
    79   if (result == INVALID_HANDLE_VALUE)
    80     return false;
    82   *handle = result;
    83   return true;
    84 }
    86 bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) {
    87   ProcessHandle result = OpenProcess(PROCESS_DUP_HANDLE |
    88                                          PROCESS_TERMINATE |
    89                                          PROCESS_QUERY_INFORMATION |
    90                                          PROCESS_VM_READ |
    91                                          SYNCHRONIZE,
    92                                      FALSE, pid);
    94   if (result == INVALID_HANDLE_VALUE)
    95     return false;
    97   *handle = result;
    98   return true;
    99 }
   101 void CloseProcessHandle(ProcessHandle process) {
   102   // closing a handle twice on Windows can be catastrophic - after the first
   103   // close the handle value may be reused, so the second close will kill that
   104   // other new handle.
   105   BOOL ok = CloseHandle(process);
   106   DCHECK(ok);
   107 }
   109 // Helper for GetProcId()
   110 bool GetProcIdViaGetProcessId(ProcessHandle process, DWORD* id) {
   111   // Dynamically get a pointer to GetProcessId().
   112   typedef DWORD (WINAPI *GetProcessIdFunction)(HANDLE);
   113   static GetProcessIdFunction GetProcessIdPtr = NULL;
   114   static bool initialize_get_process_id = true;
   115   if (initialize_get_process_id) {
   116     initialize_get_process_id = false;
   117     HMODULE kernel32_handle = GetModuleHandle(L"kernel32.dll");
   118     if (!kernel32_handle) {
   119       NOTREACHED();
   120       return false;
   121     }
   122     GetProcessIdPtr = reinterpret_cast<GetProcessIdFunction>(GetProcAddress(
   123         kernel32_handle, "GetProcessId"));
   124   }
   125   if (!GetProcessIdPtr)
   126     return false;
   127   // Ask for the process ID.
   128   *id = (*GetProcessIdPtr)(process);
   129   return true;
   130 }
   132 // Helper for GetProcId()
   133 bool GetProcIdViaNtQueryInformationProcess(ProcessHandle process, DWORD* id) {
   134   // Dynamically get a pointer to NtQueryInformationProcess().
   135   typedef NTSTATUS (WINAPI *NtQueryInformationProcessFunction)(
   136       HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
   137   static NtQueryInformationProcessFunction NtQueryInformationProcessPtr = NULL;
   138   static bool initialize_query_information_process = true;
   139   if (initialize_query_information_process) {
   140     initialize_query_information_process = false;
   141     // According to nsylvain, ntdll.dll is guaranteed to be loaded, even though
   142     // the Windows docs seem to imply that you should LoadLibrary() it.
   143     HMODULE ntdll_handle = GetModuleHandle(L"ntdll.dll");
   144     if (!ntdll_handle) {
   145       NOTREACHED();
   146       return false;
   147     }
   148     NtQueryInformationProcessPtr =
   149         reinterpret_cast<NtQueryInformationProcessFunction>(GetProcAddress(
   150             ntdll_handle, "NtQueryInformationProcess"));
   151   }
   152   if (!NtQueryInformationProcessPtr)
   153     return false;
   154   // Ask for the process ID.
   155   PROCESS_BASIC_INFORMATION info;
   156   ULONG bytes_returned;
   157   NTSTATUS status = (*NtQueryInformationProcessPtr)(process,
   158                                                     ProcessBasicInformation,
   159                                                     &info, sizeof info,
   160                                                     &bytes_returned);
   161   if (!SUCCEEDED(status) || (bytes_returned != (sizeof info)))
   162     return false;
   164   *id = static_cast<DWORD>(info.UniqueProcessId);
   165   return true;
   166 }
   168 ProcessId GetProcId(ProcessHandle process) {
   169   // Get a handle to |process| that has PROCESS_QUERY_INFORMATION rights.
   170   HANDLE current_process = GetCurrentProcess();
   171   HANDLE process_with_query_rights;
   172   if (DuplicateHandle(current_process, process, current_process,
   173                       &process_with_query_rights, PROCESS_QUERY_INFORMATION,
   174                       false, 0)) {
   175     // Try to use GetProcessId(), if it exists.  Fall back on
   176     // NtQueryInformationProcess() otherwise (< Win XP SP1).
   177     DWORD id;
   178     bool success =
   179         GetProcIdViaGetProcessId(process_with_query_rights, &id) ||
   180         GetProcIdViaNtQueryInformationProcess(process_with_query_rights, &id);
   181     CloseHandle(process_with_query_rights);
   182     if (success)
   183       return id;
   184   }
   186   // We're screwed.
   187   NOTREACHED();
   188   return 0;
   189 }
   191 // from sandbox_policy_base.cc in a later version of the chromium ipc code...
   192 bool IsInheritableHandle(HANDLE handle) {
   193   if (!handle)
   194     return false;
   195   if (handle == INVALID_HANDLE_VALUE)
   196     return false;
   197   // File handles (FILE_TYPE_DISK) and pipe handles are known to be
   198   // inheritable.  Console handles (FILE_TYPE_CHAR) are not
   199   // inheritable via PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
   200   DWORD handle_type = GetFileType(handle);
   201   return handle_type == FILE_TYPE_DISK || handle_type == FILE_TYPE_PIPE;
   202 }
   204 void LoadThreadAttributeFunctions() {
   205   HMODULE kernel32 = GetModuleHandle(L"kernel32.dll");
   206   InitializeProcThreadAttributeListPtr =
   207     reinterpret_cast<InitializeProcThreadAttributeListFn>
   208     (GetProcAddress(kernel32, "InitializeProcThreadAttributeList"));
   209   DeleteProcThreadAttributeListPtr =
   210     reinterpret_cast<DeleteProcThreadAttributeListFn>
   211     (GetProcAddress(kernel32, "DeleteProcThreadAttributeList"));
   212   UpdateProcThreadAttributePtr =
   213     reinterpret_cast<UpdateProcThreadAttributeFn>
   214     (GetProcAddress(kernel32, "UpdateProcThreadAttribute"));
   215 }
   217 // Creates and returns a "thread attribute list" to pass to the child process.
   218 // On return, is a pointer to a THREAD_ATTRIBUTE_LIST or NULL if either the
   219 // functions we need aren't available (eg, XP or earlier) or the functions we
   220 // need failed.
   221 // The result of this function must be passed to FreeThreadAttributeList.
   222 // Note that the pointer to the HANDLE array ends up embedded in the result of
   223 // this function and must stay alive until FreeThreadAttributeList is called,
   224 // hence it is passed in so the owner is the caller of this function.
   225 LPPROC_THREAD_ATTRIBUTE_LIST CreateThreadAttributeList(HANDLE *handlesToInherit,
   226                                                        int handleCount) {
   227   if (!InitializeProcThreadAttributeListPtr ||
   228       !DeleteProcThreadAttributeListPtr ||
   229       !UpdateProcThreadAttributePtr)
   230     LoadThreadAttributeFunctions();
   231   // shouldn't happen as we are only called for Vista+, but better safe than sorry...
   232   if (!InitializeProcThreadAttributeListPtr ||
   233       !DeleteProcThreadAttributeListPtr ||
   234       !UpdateProcThreadAttributePtr)
   235     return NULL;
   237   SIZE_T threadAttrSize;
   238   LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList;
   240   if (!(*InitializeProcThreadAttributeListPtr)(NULL, 1, 0, &threadAttrSize) &&
   241       GetLastError() != ERROR_INSUFFICIENT_BUFFER)
   242     goto fail;
   243   lpAttributeList = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>
   244                               (malloc(threadAttrSize));
   245   if (!lpAttributeList ||
   246       !(*InitializeProcThreadAttributeListPtr)(lpAttributeList, 1, 0, &threadAttrSize))
   247     goto fail;
   249   if (!(*UpdateProcThreadAttributePtr)(lpAttributeList,
   250                   0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
   251                   handlesToInherit,
   252                   sizeof(handlesToInherit[0]) * handleCount,
   253                   NULL, NULL)) {
   254     (*DeleteProcThreadAttributeListPtr)(lpAttributeList);
   255     goto fail;
   256   }
   257   return lpAttributeList;
   259 fail:
   260   if (lpAttributeList)
   261     free(lpAttributeList);
   262   return NULL;
   263 }
   265 // Frees the data returned by CreateThreadAttributeList.
   266 void FreeThreadAttributeList(LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList) {
   267   // must be impossible to get a NULL DeleteProcThreadAttributeListPtr, as
   268   // we already checked it existed when creating the data we are now freeing.
   269   (*DeleteProcThreadAttributeListPtr)(lpAttributeList);
   270   free(lpAttributeList);
   271 }
   273 bool LaunchApp(const std::wstring& cmdline,
   274                bool wait, bool start_hidden, ProcessHandle* process_handle) {
   276   // We want to inherit the std handles so dump() statements and assertion
   277   // messages in the child process can be seen - but we *do not* want to
   278   // blindly have all handles inherited.  Vista and later has a technique
   279   // where only specified handles are inherited - so we use this technique if
   280   // we can.  If that technique isn't available (or it fails), we just don't
   281   // inherit anything.  This means that dump() etc isn't going to be seen on
   282   // XP release builds, but that's OK (developers who really care can run a
   283   // debug build on XP, where the processes are marked as "console" apps, so
   284   // things work without these hoops)
   285   DWORD dwCreationFlags = 0;
   286   BOOL bInheritHandles = FALSE;
   287   // We use a STARTUPINFOEX, but if we can't do the thread attribute thing, we
   288   // just pass the size of a STARTUPINFO.
   289   STARTUPINFOEX startup_info_ex;
   290   ZeroMemory(&startup_info_ex, sizeof(startup_info_ex));
   291   STARTUPINFO &startup_info = startup_info_ex.StartupInfo;
   292   startup_info.cb = sizeof(startup_info);
   293   startup_info.dwFlags = STARTF_USESHOWWINDOW;
   294   startup_info.wShowWindow = start_hidden ? SW_HIDE : SW_SHOW;
   296   LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL;
   297   // Don't even bother trying pre-Vista...
   298   if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA) {
   299     // setup our handle array first - if we end up with no handles that can
   300     // be inherited we can avoid trying to do the ThreadAttributeList dance...
   301     HANDLE handlesToInherit[2];
   302     int handleCount = 0;
   303     HANDLE stdOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
   304     HANDLE stdErr = ::GetStdHandle(STD_ERROR_HANDLE);
   306     if (IsInheritableHandle(stdOut))
   307       handlesToInherit[handleCount++] = stdOut;
   308     if (stdErr != stdOut && IsInheritableHandle(stdErr))
   309       handlesToInherit[handleCount++] = stdErr;
   311     if (handleCount)
   312       lpAttributeList = CreateThreadAttributeList(handlesToInherit, handleCount);
   313   }
   315   if (lpAttributeList) {
   316     // it's safe to inherit handles, so arrange for that...
   317     startup_info.cb = sizeof(startup_info_ex);
   318     startup_info.dwFlags |= STARTF_USESTDHANDLES;
   319     startup_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
   320     startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE);
   321     startup_info.hStdInput = INVALID_HANDLE_VALUE;
   322     startup_info_ex.lpAttributeList = lpAttributeList;
   323     dwCreationFlags |= EXTENDED_STARTUPINFO_PRESENT;
   324     bInheritHandles = TRUE;
   325   }
   326   PROCESS_INFORMATION process_info;
   327   BOOL createdOK = CreateProcess(NULL,
   328                      const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL,
   329                      bInheritHandles, dwCreationFlags, NULL, NULL,
   330                      &startup_info, &process_info);
   331   if (lpAttributeList)
   332     FreeThreadAttributeList(lpAttributeList);
   333   if (!createdOK)
   334     return false;
   336   gProcessLog.print("==> process %d launched child process %d (%S)\n",
   337                     GetCurrentProcId(),
   338                     process_info.dwProcessId,
   339                     cmdline.c_str());
   341   // Handles must be closed or they will leak
   342   CloseHandle(process_info.hThread);
   344   if (wait)
   345     WaitForSingleObject(process_info.hProcess, INFINITE);
   347   // If the caller wants the process handle, we won't close it.
   348   if (process_handle) {
   349     *process_handle = process_info.hProcess;
   350   } else {
   351     CloseHandle(process_info.hProcess);
   352   }
   353   return true;
   354 }
   356 bool LaunchApp(const CommandLine& cl,
   357                bool wait, bool start_hidden, ProcessHandle* process_handle) {
   358   return LaunchApp(cl.command_line_string(), wait,
   359                    start_hidden, process_handle);
   360 }
   362 bool KillProcess(ProcessHandle process, int exit_code, bool wait) {
   363   bool result = (TerminateProcess(process, exit_code) != FALSE);
   364   if (result && wait) {
   365     // The process may not end immediately due to pending I/O
   366     if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000))
   367       DLOG(ERROR) << "Error waiting for process exit: " << GetLastError();
   368   } else if (!result) {
   369     DLOG(ERROR) << "Unable to terminate process: " << GetLastError();
   370   }
   371   return result;
   372 }
   374 bool DidProcessCrash(bool* child_exited, ProcessHandle handle) {
   375   DWORD exitcode = 0;
   377   if (child_exited)
   378     *child_exited = true;  // On Windows it an error to call this function if
   379                            // the child hasn't already exited.
   380   if (!::GetExitCodeProcess(handle, &exitcode)) {
   381     NOTREACHED();
   382     return false;
   383   }
   384   if (exitcode == STILL_ACTIVE) {
   385     // The process is likely not dead or it used 0x103 as exit code.
   386     NOTREACHED();
   387     return false;
   388   }
   390   // Warning, this is not generic code; it heavily depends on the way
   391   // the rest of the code kills a process.
   393   if (exitcode == PROCESS_END_NORMAL_TERMINATON ||
   394       exitcode == PROCESS_END_KILLED_BY_USER ||
   395       exitcode == PROCESS_END_PROCESS_WAS_HUNG ||
   396       exitcode == 0xC0000354 ||     // STATUS_DEBUGGER_INACTIVE.
   397       exitcode == 0xC000013A ||     // Control-C/end session.
   398       exitcode == 0x40010004) {     // Debugger terminated process/end session.
   399     return false;
   400   }
   402   return true;
   403 }
   405 void SetCurrentProcessPrivileges(ChildPrivileges privs) {
   407 }
   409 NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name,
   410                                            const ProcessFilter* filter)
   411     : started_iteration_(false),
   412       executable_name_(executable_name),
   413       filter_(filter) {
   414   snapshot_ = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
   415 }
   417 NamedProcessIterator::~NamedProcessIterator() {
   418   CloseHandle(snapshot_);
   419 }
   422 const ProcessEntry* NamedProcessIterator::NextProcessEntry() {
   423   bool result = false;
   424   do {
   425     result = CheckForNextProcess();
   426   } while (result && !IncludeEntry());
   428   if (result) {
   429     return &entry_;
   430   }
   432   return NULL;
   433 }
   435 bool NamedProcessIterator::CheckForNextProcess() {
   436   InitProcessEntry(&entry_);
   438   if (!started_iteration_) {
   439     started_iteration_ = true;
   440     return !!Process32First(snapshot_, &entry_);
   441   }
   443   return !!Process32Next(snapshot_, &entry_);
   444 }
   446 bool NamedProcessIterator::IncludeEntry() {
   447   return _wcsicmp(executable_name_.c_str(), entry_.szExeFile) == 0 &&
   448                   (!filter_ || filter_->Includes(entry_.th32ProcessID,
   449                                                  entry_.th32ParentProcessID));
   450 }
   452 void NamedProcessIterator::InitProcessEntry(ProcessEntry* entry) {
   453   memset(entry, 0, sizeof(*entry));
   454   entry->dwSize = sizeof(*entry);
   455 }
   457 ///////////////////////////////////////////////////////////////////////////////
   458 // ProcesMetrics
   460 ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process),
   461                                                         last_time_(0),
   462                                                         last_system_time_(0) {
   463   SYSTEM_INFO system_info;
   464   GetSystemInfo(&system_info);
   465   processor_count_ = system_info.dwNumberOfProcessors;
   466 }
   468 // static
   469 ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
   470   return new ProcessMetrics(process);
   471 }
   473 ProcessMetrics::~ProcessMetrics() { }
   475 static uint64_t FileTimeToUTC(const FILETIME& ftime) {
   476   LARGE_INTEGER li;
   477   li.LowPart = ftime.dwLowDateTime;
   478   li.HighPart = ftime.dwHighDateTime;
   479   return li.QuadPart;
   480 }
   482 int ProcessMetrics::GetCPUUsage() {
   483   FILETIME now;
   484   FILETIME creation_time;
   485   FILETIME exit_time;
   486   FILETIME kernel_time;
   487   FILETIME user_time;
   489   GetSystemTimeAsFileTime(&now);
   491   if (!GetProcessTimes(process_, &creation_time, &exit_time,
   492                        &kernel_time, &user_time)) {
   493     // We don't assert here because in some cases (such as in the Task Manager)
   494     // we may call this function on a process that has just exited but we have
   495     // not yet received the notification.
   496     return 0;
   497   }
   498   int64_t system_time = (FileTimeToUTC(kernel_time) + FileTimeToUTC(user_time)) /
   499                         processor_count_;
   500   int64_t time = FileTimeToUTC(now);
   502   if ((last_system_time_ == 0) || (last_time_ == 0)) {
   503     // First call, just set the last values.
   504     last_system_time_ = system_time;
   505     last_time_ = time;
   506     return 0;
   507   }
   509   int64_t system_time_delta = system_time - last_system_time_;
   510   int64_t time_delta = time - last_time_;
   511   DCHECK(time_delta != 0);
   512   if (time_delta == 0)
   513     return 0;
   515   // We add time_delta / 2 so the result is rounded.
   516   int cpu = static_cast<int>((system_time_delta * 100 + time_delta / 2) /
   517                              time_delta);
   519   last_system_time_ = system_time;
   520   last_time_ = time;
   522   return cpu;
   523 }
   525 }  // namespace base

mercurial