1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ipc/chromium/src/base/process_util_win.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,525 @@ 1.4 +// Copyright (c) 2009 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +// We need extended process and thread attribute support 1.9 +#undef _WIN32_WINNT 1.10 +#define _WIN32_WINNT 0x0600 1.11 + 1.12 +#include "base/process_util.h" 1.13 + 1.14 +#include <windows.h> 1.15 +#include <winternl.h> 1.16 +#include <psapi.h> 1.17 + 1.18 +#include "base/debug_util.h" 1.19 +#include "base/histogram.h" 1.20 +#include "base/logging.h" 1.21 +#include "base/scoped_handle_win.h" 1.22 +#include "base/scoped_ptr.h" 1.23 +#include "base/win_util.h" 1.24 + 1.25 +#include <algorithm> 1.26 + 1.27 +namespace { 1.28 + 1.29 +// System pagesize. This value remains constant on x86/64 architectures. 1.30 +const int PAGESIZE_KB = 4; 1.31 + 1.32 +// HeapSetInformation function pointer. 1.33 +typedef BOOL (WINAPI* HeapSetFn)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T); 1.34 + 1.35 +typedef BOOL (WINAPI * InitializeProcThreadAttributeListFn)( 1.36 + LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, 1.37 + DWORD dwAttributeCount, 1.38 + DWORD dwFlags, 1.39 + PSIZE_T lpSize 1.40 +); 1.41 + 1.42 +typedef BOOL (WINAPI * DeleteProcThreadAttributeListFn)( 1.43 + LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList 1.44 +); 1.45 + 1.46 +typedef BOOL (WINAPI * UpdateProcThreadAttributeFn)( 1.47 + LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, 1.48 + DWORD dwFlags, 1.49 + DWORD_PTR Attribute, 1.50 + PVOID lpValue, 1.51 + SIZE_T cbSize, 1.52 + PVOID lpPreviousValue, 1.53 + PSIZE_T lpReturnSize 1.54 +); 1.55 + 1.56 +static InitializeProcThreadAttributeListFn InitializeProcThreadAttributeListPtr; 1.57 +static DeleteProcThreadAttributeListFn DeleteProcThreadAttributeListPtr; 1.58 +static UpdateProcThreadAttributeFn UpdateProcThreadAttributePtr; 1.59 + 1.60 +static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG"); 1.61 + 1.62 +} // namespace 1.63 + 1.64 +namespace base { 1.65 + 1.66 +ProcessId GetCurrentProcId() { 1.67 + return ::GetCurrentProcessId(); 1.68 +} 1.69 + 1.70 +ProcessHandle GetCurrentProcessHandle() { 1.71 + return ::GetCurrentProcess(); 1.72 +} 1.73 + 1.74 +bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) { 1.75 + // TODO(phajdan.jr): Take even more permissions out of this list. 1.76 + ProcessHandle result = OpenProcess(PROCESS_DUP_HANDLE | 1.77 + PROCESS_TERMINATE | 1.78 + PROCESS_QUERY_INFORMATION | 1.79 + SYNCHRONIZE, 1.80 + FALSE, pid); 1.81 + 1.82 + if (result == INVALID_HANDLE_VALUE) 1.83 + return false; 1.84 + 1.85 + *handle = result; 1.86 + return true; 1.87 +} 1.88 + 1.89 +bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) { 1.90 + ProcessHandle result = OpenProcess(PROCESS_DUP_HANDLE | 1.91 + PROCESS_TERMINATE | 1.92 + PROCESS_QUERY_INFORMATION | 1.93 + PROCESS_VM_READ | 1.94 + SYNCHRONIZE, 1.95 + FALSE, pid); 1.96 + 1.97 + if (result == INVALID_HANDLE_VALUE) 1.98 + return false; 1.99 + 1.100 + *handle = result; 1.101 + return true; 1.102 +} 1.103 + 1.104 +void CloseProcessHandle(ProcessHandle process) { 1.105 + // closing a handle twice on Windows can be catastrophic - after the first 1.106 + // close the handle value may be reused, so the second close will kill that 1.107 + // other new handle. 1.108 + BOOL ok = CloseHandle(process); 1.109 + DCHECK(ok); 1.110 +} 1.111 + 1.112 +// Helper for GetProcId() 1.113 +bool GetProcIdViaGetProcessId(ProcessHandle process, DWORD* id) { 1.114 + // Dynamically get a pointer to GetProcessId(). 1.115 + typedef DWORD (WINAPI *GetProcessIdFunction)(HANDLE); 1.116 + static GetProcessIdFunction GetProcessIdPtr = NULL; 1.117 + static bool initialize_get_process_id = true; 1.118 + if (initialize_get_process_id) { 1.119 + initialize_get_process_id = false; 1.120 + HMODULE kernel32_handle = GetModuleHandle(L"kernel32.dll"); 1.121 + if (!kernel32_handle) { 1.122 + NOTREACHED(); 1.123 + return false; 1.124 + } 1.125 + GetProcessIdPtr = reinterpret_cast<GetProcessIdFunction>(GetProcAddress( 1.126 + kernel32_handle, "GetProcessId")); 1.127 + } 1.128 + if (!GetProcessIdPtr) 1.129 + return false; 1.130 + // Ask for the process ID. 1.131 + *id = (*GetProcessIdPtr)(process); 1.132 + return true; 1.133 +} 1.134 + 1.135 +// Helper for GetProcId() 1.136 +bool GetProcIdViaNtQueryInformationProcess(ProcessHandle process, DWORD* id) { 1.137 + // Dynamically get a pointer to NtQueryInformationProcess(). 1.138 + typedef NTSTATUS (WINAPI *NtQueryInformationProcessFunction)( 1.139 + HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); 1.140 + static NtQueryInformationProcessFunction NtQueryInformationProcessPtr = NULL; 1.141 + static bool initialize_query_information_process = true; 1.142 + if (initialize_query_information_process) { 1.143 + initialize_query_information_process = false; 1.144 + // According to nsylvain, ntdll.dll is guaranteed to be loaded, even though 1.145 + // the Windows docs seem to imply that you should LoadLibrary() it. 1.146 + HMODULE ntdll_handle = GetModuleHandle(L"ntdll.dll"); 1.147 + if (!ntdll_handle) { 1.148 + NOTREACHED(); 1.149 + return false; 1.150 + } 1.151 + NtQueryInformationProcessPtr = 1.152 + reinterpret_cast<NtQueryInformationProcessFunction>(GetProcAddress( 1.153 + ntdll_handle, "NtQueryInformationProcess")); 1.154 + } 1.155 + if (!NtQueryInformationProcessPtr) 1.156 + return false; 1.157 + // Ask for the process ID. 1.158 + PROCESS_BASIC_INFORMATION info; 1.159 + ULONG bytes_returned; 1.160 + NTSTATUS status = (*NtQueryInformationProcessPtr)(process, 1.161 + ProcessBasicInformation, 1.162 + &info, sizeof info, 1.163 + &bytes_returned); 1.164 + if (!SUCCEEDED(status) || (bytes_returned != (sizeof info))) 1.165 + return false; 1.166 + 1.167 + *id = static_cast<DWORD>(info.UniqueProcessId); 1.168 + return true; 1.169 +} 1.170 + 1.171 +ProcessId GetProcId(ProcessHandle process) { 1.172 + // Get a handle to |process| that has PROCESS_QUERY_INFORMATION rights. 1.173 + HANDLE current_process = GetCurrentProcess(); 1.174 + HANDLE process_with_query_rights; 1.175 + if (DuplicateHandle(current_process, process, current_process, 1.176 + &process_with_query_rights, PROCESS_QUERY_INFORMATION, 1.177 + false, 0)) { 1.178 + // Try to use GetProcessId(), if it exists. Fall back on 1.179 + // NtQueryInformationProcess() otherwise (< Win XP SP1). 1.180 + DWORD id; 1.181 + bool success = 1.182 + GetProcIdViaGetProcessId(process_with_query_rights, &id) || 1.183 + GetProcIdViaNtQueryInformationProcess(process_with_query_rights, &id); 1.184 + CloseHandle(process_with_query_rights); 1.185 + if (success) 1.186 + return id; 1.187 + } 1.188 + 1.189 + // We're screwed. 1.190 + NOTREACHED(); 1.191 + return 0; 1.192 +} 1.193 + 1.194 +// from sandbox_policy_base.cc in a later version of the chromium ipc code... 1.195 +bool IsInheritableHandle(HANDLE handle) { 1.196 + if (!handle) 1.197 + return false; 1.198 + if (handle == INVALID_HANDLE_VALUE) 1.199 + return false; 1.200 + // File handles (FILE_TYPE_DISK) and pipe handles are known to be 1.201 + // inheritable. Console handles (FILE_TYPE_CHAR) are not 1.202 + // inheritable via PROC_THREAD_ATTRIBUTE_HANDLE_LIST. 1.203 + DWORD handle_type = GetFileType(handle); 1.204 + return handle_type == FILE_TYPE_DISK || handle_type == FILE_TYPE_PIPE; 1.205 +} 1.206 + 1.207 +void LoadThreadAttributeFunctions() { 1.208 + HMODULE kernel32 = GetModuleHandle(L"kernel32.dll"); 1.209 + InitializeProcThreadAttributeListPtr = 1.210 + reinterpret_cast<InitializeProcThreadAttributeListFn> 1.211 + (GetProcAddress(kernel32, "InitializeProcThreadAttributeList")); 1.212 + DeleteProcThreadAttributeListPtr = 1.213 + reinterpret_cast<DeleteProcThreadAttributeListFn> 1.214 + (GetProcAddress(kernel32, "DeleteProcThreadAttributeList")); 1.215 + UpdateProcThreadAttributePtr = 1.216 + reinterpret_cast<UpdateProcThreadAttributeFn> 1.217 + (GetProcAddress(kernel32, "UpdateProcThreadAttribute")); 1.218 +} 1.219 + 1.220 +// Creates and returns a "thread attribute list" to pass to the child process. 1.221 +// On return, is a pointer to a THREAD_ATTRIBUTE_LIST or NULL if either the 1.222 +// functions we need aren't available (eg, XP or earlier) or the functions we 1.223 +// need failed. 1.224 +// The result of this function must be passed to FreeThreadAttributeList. 1.225 +// Note that the pointer to the HANDLE array ends up embedded in the result of 1.226 +// this function and must stay alive until FreeThreadAttributeList is called, 1.227 +// hence it is passed in so the owner is the caller of this function. 1.228 +LPPROC_THREAD_ATTRIBUTE_LIST CreateThreadAttributeList(HANDLE *handlesToInherit, 1.229 + int handleCount) { 1.230 + if (!InitializeProcThreadAttributeListPtr || 1.231 + !DeleteProcThreadAttributeListPtr || 1.232 + !UpdateProcThreadAttributePtr) 1.233 + LoadThreadAttributeFunctions(); 1.234 + // shouldn't happen as we are only called for Vista+, but better safe than sorry... 1.235 + if (!InitializeProcThreadAttributeListPtr || 1.236 + !DeleteProcThreadAttributeListPtr || 1.237 + !UpdateProcThreadAttributePtr) 1.238 + return NULL; 1.239 + 1.240 + SIZE_T threadAttrSize; 1.241 + LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList; 1.242 + 1.243 + if (!(*InitializeProcThreadAttributeListPtr)(NULL, 1, 0, &threadAttrSize) && 1.244 + GetLastError() != ERROR_INSUFFICIENT_BUFFER) 1.245 + goto fail; 1.246 + lpAttributeList = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST> 1.247 + (malloc(threadAttrSize)); 1.248 + if (!lpAttributeList || 1.249 + !(*InitializeProcThreadAttributeListPtr)(lpAttributeList, 1, 0, &threadAttrSize)) 1.250 + goto fail; 1.251 + 1.252 + if (!(*UpdateProcThreadAttributePtr)(lpAttributeList, 1.253 + 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, 1.254 + handlesToInherit, 1.255 + sizeof(handlesToInherit[0]) * handleCount, 1.256 + NULL, NULL)) { 1.257 + (*DeleteProcThreadAttributeListPtr)(lpAttributeList); 1.258 + goto fail; 1.259 + } 1.260 + return lpAttributeList; 1.261 + 1.262 +fail: 1.263 + if (lpAttributeList) 1.264 + free(lpAttributeList); 1.265 + return NULL; 1.266 +} 1.267 + 1.268 +// Frees the data returned by CreateThreadAttributeList. 1.269 +void FreeThreadAttributeList(LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList) { 1.270 + // must be impossible to get a NULL DeleteProcThreadAttributeListPtr, as 1.271 + // we already checked it existed when creating the data we are now freeing. 1.272 + (*DeleteProcThreadAttributeListPtr)(lpAttributeList); 1.273 + free(lpAttributeList); 1.274 +} 1.275 + 1.276 +bool LaunchApp(const std::wstring& cmdline, 1.277 + bool wait, bool start_hidden, ProcessHandle* process_handle) { 1.278 + 1.279 + // We want to inherit the std handles so dump() statements and assertion 1.280 + // messages in the child process can be seen - but we *do not* want to 1.281 + // blindly have all handles inherited. Vista and later has a technique 1.282 + // where only specified handles are inherited - so we use this technique if 1.283 + // we can. If that technique isn't available (or it fails), we just don't 1.284 + // inherit anything. This means that dump() etc isn't going to be seen on 1.285 + // XP release builds, but that's OK (developers who really care can run a 1.286 + // debug build on XP, where the processes are marked as "console" apps, so 1.287 + // things work without these hoops) 1.288 + DWORD dwCreationFlags = 0; 1.289 + BOOL bInheritHandles = FALSE; 1.290 + // We use a STARTUPINFOEX, but if we can't do the thread attribute thing, we 1.291 + // just pass the size of a STARTUPINFO. 1.292 + STARTUPINFOEX startup_info_ex; 1.293 + ZeroMemory(&startup_info_ex, sizeof(startup_info_ex)); 1.294 + STARTUPINFO &startup_info = startup_info_ex.StartupInfo; 1.295 + startup_info.cb = sizeof(startup_info); 1.296 + startup_info.dwFlags = STARTF_USESHOWWINDOW; 1.297 + startup_info.wShowWindow = start_hidden ? SW_HIDE : SW_SHOW; 1.298 + 1.299 + LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL; 1.300 + // Don't even bother trying pre-Vista... 1.301 + if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA) { 1.302 + // setup our handle array first - if we end up with no handles that can 1.303 + // be inherited we can avoid trying to do the ThreadAttributeList dance... 1.304 + HANDLE handlesToInherit[2]; 1.305 + int handleCount = 0; 1.306 + HANDLE stdOut = ::GetStdHandle(STD_OUTPUT_HANDLE); 1.307 + HANDLE stdErr = ::GetStdHandle(STD_ERROR_HANDLE); 1.308 + 1.309 + if (IsInheritableHandle(stdOut)) 1.310 + handlesToInherit[handleCount++] = stdOut; 1.311 + if (stdErr != stdOut && IsInheritableHandle(stdErr)) 1.312 + handlesToInherit[handleCount++] = stdErr; 1.313 + 1.314 + if (handleCount) 1.315 + lpAttributeList = CreateThreadAttributeList(handlesToInherit, handleCount); 1.316 + } 1.317 + 1.318 + if (lpAttributeList) { 1.319 + // it's safe to inherit handles, so arrange for that... 1.320 + startup_info.cb = sizeof(startup_info_ex); 1.321 + startup_info.dwFlags |= STARTF_USESTDHANDLES; 1.322 + startup_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE); 1.323 + startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE); 1.324 + startup_info.hStdInput = INVALID_HANDLE_VALUE; 1.325 + startup_info_ex.lpAttributeList = lpAttributeList; 1.326 + dwCreationFlags |= EXTENDED_STARTUPINFO_PRESENT; 1.327 + bInheritHandles = TRUE; 1.328 + } 1.329 + PROCESS_INFORMATION process_info; 1.330 + BOOL createdOK = CreateProcess(NULL, 1.331 + const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL, 1.332 + bInheritHandles, dwCreationFlags, NULL, NULL, 1.333 + &startup_info, &process_info); 1.334 + if (lpAttributeList) 1.335 + FreeThreadAttributeList(lpAttributeList); 1.336 + if (!createdOK) 1.337 + return false; 1.338 + 1.339 + gProcessLog.print("==> process %d launched child process %d (%S)\n", 1.340 + GetCurrentProcId(), 1.341 + process_info.dwProcessId, 1.342 + cmdline.c_str()); 1.343 + 1.344 + // Handles must be closed or they will leak 1.345 + CloseHandle(process_info.hThread); 1.346 + 1.347 + if (wait) 1.348 + WaitForSingleObject(process_info.hProcess, INFINITE); 1.349 + 1.350 + // If the caller wants the process handle, we won't close it. 1.351 + if (process_handle) { 1.352 + *process_handle = process_info.hProcess; 1.353 + } else { 1.354 + CloseHandle(process_info.hProcess); 1.355 + } 1.356 + return true; 1.357 +} 1.358 + 1.359 +bool LaunchApp(const CommandLine& cl, 1.360 + bool wait, bool start_hidden, ProcessHandle* process_handle) { 1.361 + return LaunchApp(cl.command_line_string(), wait, 1.362 + start_hidden, process_handle); 1.363 +} 1.364 + 1.365 +bool KillProcess(ProcessHandle process, int exit_code, bool wait) { 1.366 + bool result = (TerminateProcess(process, exit_code) != FALSE); 1.367 + if (result && wait) { 1.368 + // The process may not end immediately due to pending I/O 1.369 + if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000)) 1.370 + DLOG(ERROR) << "Error waiting for process exit: " << GetLastError(); 1.371 + } else if (!result) { 1.372 + DLOG(ERROR) << "Unable to terminate process: " << GetLastError(); 1.373 + } 1.374 + return result; 1.375 +} 1.376 + 1.377 +bool DidProcessCrash(bool* child_exited, ProcessHandle handle) { 1.378 + DWORD exitcode = 0; 1.379 + 1.380 + if (child_exited) 1.381 + *child_exited = true; // On Windows it an error to call this function if 1.382 + // the child hasn't already exited. 1.383 + if (!::GetExitCodeProcess(handle, &exitcode)) { 1.384 + NOTREACHED(); 1.385 + return false; 1.386 + } 1.387 + if (exitcode == STILL_ACTIVE) { 1.388 + // The process is likely not dead or it used 0x103 as exit code. 1.389 + NOTREACHED(); 1.390 + return false; 1.391 + } 1.392 + 1.393 + // Warning, this is not generic code; it heavily depends on the way 1.394 + // the rest of the code kills a process. 1.395 + 1.396 + if (exitcode == PROCESS_END_NORMAL_TERMINATON || 1.397 + exitcode == PROCESS_END_KILLED_BY_USER || 1.398 + exitcode == PROCESS_END_PROCESS_WAS_HUNG || 1.399 + exitcode == 0xC0000354 || // STATUS_DEBUGGER_INACTIVE. 1.400 + exitcode == 0xC000013A || // Control-C/end session. 1.401 + exitcode == 0x40010004) { // Debugger terminated process/end session. 1.402 + return false; 1.403 + } 1.404 + 1.405 + return true; 1.406 +} 1.407 + 1.408 +void SetCurrentProcessPrivileges(ChildPrivileges privs) { 1.409 + 1.410 +} 1.411 + 1.412 +NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name, 1.413 + const ProcessFilter* filter) 1.414 + : started_iteration_(false), 1.415 + executable_name_(executable_name), 1.416 + filter_(filter) { 1.417 + snapshot_ = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 1.418 +} 1.419 + 1.420 +NamedProcessIterator::~NamedProcessIterator() { 1.421 + CloseHandle(snapshot_); 1.422 +} 1.423 + 1.424 + 1.425 +const ProcessEntry* NamedProcessIterator::NextProcessEntry() { 1.426 + bool result = false; 1.427 + do { 1.428 + result = CheckForNextProcess(); 1.429 + } while (result && !IncludeEntry()); 1.430 + 1.431 + if (result) { 1.432 + return &entry_; 1.433 + } 1.434 + 1.435 + return NULL; 1.436 +} 1.437 + 1.438 +bool NamedProcessIterator::CheckForNextProcess() { 1.439 + InitProcessEntry(&entry_); 1.440 + 1.441 + if (!started_iteration_) { 1.442 + started_iteration_ = true; 1.443 + return !!Process32First(snapshot_, &entry_); 1.444 + } 1.445 + 1.446 + return !!Process32Next(snapshot_, &entry_); 1.447 +} 1.448 + 1.449 +bool NamedProcessIterator::IncludeEntry() { 1.450 + return _wcsicmp(executable_name_.c_str(), entry_.szExeFile) == 0 && 1.451 + (!filter_ || filter_->Includes(entry_.th32ProcessID, 1.452 + entry_.th32ParentProcessID)); 1.453 +} 1.454 + 1.455 +void NamedProcessIterator::InitProcessEntry(ProcessEntry* entry) { 1.456 + memset(entry, 0, sizeof(*entry)); 1.457 + entry->dwSize = sizeof(*entry); 1.458 +} 1.459 + 1.460 +/////////////////////////////////////////////////////////////////////////////// 1.461 +// ProcesMetrics 1.462 + 1.463 +ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process), 1.464 + last_time_(0), 1.465 + last_system_time_(0) { 1.466 + SYSTEM_INFO system_info; 1.467 + GetSystemInfo(&system_info); 1.468 + processor_count_ = system_info.dwNumberOfProcessors; 1.469 +} 1.470 + 1.471 +// static 1.472 +ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) { 1.473 + return new ProcessMetrics(process); 1.474 +} 1.475 + 1.476 +ProcessMetrics::~ProcessMetrics() { } 1.477 + 1.478 +static uint64_t FileTimeToUTC(const FILETIME& ftime) { 1.479 + LARGE_INTEGER li; 1.480 + li.LowPart = ftime.dwLowDateTime; 1.481 + li.HighPart = ftime.dwHighDateTime; 1.482 + return li.QuadPart; 1.483 +} 1.484 + 1.485 +int ProcessMetrics::GetCPUUsage() { 1.486 + FILETIME now; 1.487 + FILETIME creation_time; 1.488 + FILETIME exit_time; 1.489 + FILETIME kernel_time; 1.490 + FILETIME user_time; 1.491 + 1.492 + GetSystemTimeAsFileTime(&now); 1.493 + 1.494 + if (!GetProcessTimes(process_, &creation_time, &exit_time, 1.495 + &kernel_time, &user_time)) { 1.496 + // We don't assert here because in some cases (such as in the Task Manager) 1.497 + // we may call this function on a process that has just exited but we have 1.498 + // not yet received the notification. 1.499 + return 0; 1.500 + } 1.501 + int64_t system_time = (FileTimeToUTC(kernel_time) + FileTimeToUTC(user_time)) / 1.502 + processor_count_; 1.503 + int64_t time = FileTimeToUTC(now); 1.504 + 1.505 + if ((last_system_time_ == 0) || (last_time_ == 0)) { 1.506 + // First call, just set the last values. 1.507 + last_system_time_ = system_time; 1.508 + last_time_ = time; 1.509 + return 0; 1.510 + } 1.511 + 1.512 + int64_t system_time_delta = system_time - last_system_time_; 1.513 + int64_t time_delta = time - last_time_; 1.514 + DCHECK(time_delta != 0); 1.515 + if (time_delta == 0) 1.516 + return 0; 1.517 + 1.518 + // We add time_delta / 2 so the result is rounded. 1.519 + int cpu = static_cast<int>((system_time_delta * 100 + time_delta / 2) / 1.520 + time_delta); 1.521 + 1.522 + last_system_time_ = system_time; 1.523 + last_time_ = time; 1.524 + 1.525 + return cpu; 1.526 +} 1.527 + 1.528 +} // namespace base