1 // Copyright (c) 2011 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. 4 5 #include "base/process_util.h" 6 7 #include <fcntl.h> 8 #include <io.h> 9 #include <windows.h> 10 #include <userenv.h> 11 #include <psapi.h> 12 13 #include <ios> 14 15 #include "base/command_line.h" 16 #include "base/debug/stack_trace.h" 17 #include "base/logging.h" 18 #include "base/memory/scoped_ptr.h" 19 #include "base/metrics/histogram.h" 20 #include "base/sys_info.h" 21 #include "base/win/scoped_handle.h" 22 #include "base/win/windows_version.h" 23 24 // userenv.dll is required for CreateEnvironmentBlock(). 25 #pragma comment(lib, "userenv.lib") 26 27 namespace base { 28 29 namespace { 30 31 // System pagesize. This value remains constant on x86/64 architectures. 32 const int PAGESIZE_KB = 4; 33 34 // Exit codes with special meanings on Windows. 35 const DWORD kNormalTerminationExitCode = 0; 36 const DWORD kDebuggerInactiveExitCode = 0xC0000354; 37 const DWORD kKeyboardInterruptExitCode = 0xC000013A; 38 const DWORD kDebuggerTerminatedExitCode = 0x40010004; 39 40 // This exit code is used by the Windows task manager when it kills a 41 // process. It's value is obviously not that unique, and it's 42 // surprising to me that the task manager uses this value, but it 43 // seems to be common practice on Windows to test for it as an 44 // indication that the task manager has killed something if the 45 // process goes away. 46 const DWORD kProcessKilledExitCode = 1; 47 48 // HeapSetInformation function pointer. 49 typedef BOOL (WINAPI* HeapSetFn)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T); 50 51 // Previous unhandled filter. Will be called if not NULL when we intercept an 52 // exception. Only used in unit tests. 53 LPTOP_LEVEL_EXCEPTION_FILTER g_previous_filter = NULL; 54 55 // Prints the exception call stack. 56 // This is the unit tests exception filter. 57 long WINAPI StackDumpExceptionFilter(EXCEPTION_POINTERS* info) { 58 debug::StackTrace(info).PrintBacktrace(); 59 if (g_previous_filter) 60 return g_previous_filter(info); 61 return EXCEPTION_CONTINUE_SEARCH; 62 } 63 64 // Connects back to a console if available. 65 void AttachToConsole() { 66 if (!AttachConsole(ATTACH_PARENT_PROCESS)) { 67 unsigned int result = GetLastError(); 68 // Was probably already attached. 69 if (result == ERROR_ACCESS_DENIED) 70 return; 71 72 if (result == ERROR_INVALID_HANDLE || result == ERROR_INVALID_HANDLE) { 73 // TODO(maruel): Walk up the process chain if deemed necessary. 74 } 75 // Continue even if the function call fails. 76 AllocConsole(); 77 } 78 // http://support.microsoft.com/kb/105305 79 int raw_out = _open_osfhandle( 80 reinterpret_cast<intptr_t>(GetStdHandle(STD_OUTPUT_HANDLE)), _O_TEXT); 81 *stdout = *_fdopen(raw_out, "w"); 82 setvbuf(stdout, NULL, _IONBF, 0); 83 84 int raw_err = _open_osfhandle( 85 reinterpret_cast<intptr_t>(GetStdHandle(STD_ERROR_HANDLE)), _O_TEXT); 86 *stderr = *_fdopen(raw_err, "w"); 87 setvbuf(stderr, NULL, _IONBF, 0); 88 89 int raw_in = _open_osfhandle( 90 reinterpret_cast<intptr_t>(GetStdHandle(STD_INPUT_HANDLE)), _O_TEXT); 91 *stdin = *_fdopen(raw_in, "r"); 92 setvbuf(stdin, NULL, _IONBF, 0); 93 // Fix all cout, wcout, cin, wcin, cerr, wcerr, clog and wclog. 94 std::ios::sync_with_stdio(); 95 } 96 97 } // namespace 98 99 ProcessId GetCurrentProcId() { 100 return ::GetCurrentProcessId(); 101 } 102 103 ProcessHandle GetCurrentProcessHandle() { 104 return ::GetCurrentProcess(); 105 } 106 107 bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) { 108 // We try to limit privileges granted to the handle. If you need this 109 // for test code, consider using OpenPrivilegedProcessHandle instead of 110 // adding more privileges here. 111 ProcessHandle result = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_TERMINATE, 112 FALSE, pid); 113 114 if (result == INVALID_HANDLE_VALUE) 115 return false; 116 117 *handle = result; 118 return true; 119 } 120 121 bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) { 122 ProcessHandle result = OpenProcess(PROCESS_DUP_HANDLE | 123 PROCESS_TERMINATE | 124 PROCESS_QUERY_INFORMATION | 125 PROCESS_VM_READ | 126 SYNCHRONIZE, 127 FALSE, pid); 128 129 if (result == INVALID_HANDLE_VALUE) 130 return false; 131 132 *handle = result; 133 return true; 134 } 135 136 bool OpenProcessHandleWithAccess(ProcessId pid, 137 uint32 access_flags, 138 ProcessHandle* handle) { 139 ProcessHandle result = OpenProcess(access_flags, FALSE, pid); 140 141 if (result == INVALID_HANDLE_VALUE) 142 return false; 143 144 *handle = result; 145 return true; 146 } 147 148 void CloseProcessHandle(ProcessHandle process) { 149 CloseHandle(process); 150 } 151 152 ProcessId GetProcId(ProcessHandle process) { 153 // Get a handle to |process| that has PROCESS_QUERY_INFORMATION rights. 154 HANDLE current_process = GetCurrentProcess(); 155 HANDLE process_with_query_rights; 156 if (DuplicateHandle(current_process, process, current_process, 157 &process_with_query_rights, PROCESS_QUERY_INFORMATION, 158 false, 0)) { 159 DWORD id = GetProcessId(process_with_query_rights); 160 CloseHandle(process_with_query_rights); 161 return id; 162 } 163 164 // We're screwed. 165 NOTREACHED(); 166 return 0; 167 } 168 169 bool GetProcessIntegrityLevel(ProcessHandle process, IntegrityLevel *level) { 170 if (!level) 171 return false; 172 173 if (win::GetVersion() < base::win::VERSION_VISTA) 174 return false; 175 176 HANDLE process_token; 177 if (!OpenProcessToken(process, TOKEN_QUERY | TOKEN_QUERY_SOURCE, 178 &process_token)) 179 return false; 180 181 win::ScopedHandle scoped_process_token(process_token); 182 183 DWORD token_info_length = 0; 184 if (GetTokenInformation(process_token, TokenIntegrityLevel, NULL, 0, 185 &token_info_length) || 186 GetLastError() != ERROR_INSUFFICIENT_BUFFER) 187 return false; 188 189 scoped_array<char> token_label_bytes(new char[token_info_length]); 190 if (!token_label_bytes.get()) 191 return false; 192 193 TOKEN_MANDATORY_LABEL* token_label = 194 reinterpret_cast<TOKEN_MANDATORY_LABEL*>(token_label_bytes.get()); 195 if (!token_label) 196 return false; 197 198 if (!GetTokenInformation(process_token, TokenIntegrityLevel, token_label, 199 token_info_length, &token_info_length)) 200 return false; 201 202 DWORD integrity_level = *GetSidSubAuthority(token_label->Label.Sid, 203 (DWORD)(UCHAR)(*GetSidSubAuthorityCount(token_label->Label.Sid)-1)); 204 205 if (integrity_level < SECURITY_MANDATORY_MEDIUM_RID) { 206 *level = LOW_INTEGRITY; 207 } else if (integrity_level >= SECURITY_MANDATORY_MEDIUM_RID && 208 integrity_level < SECURITY_MANDATORY_HIGH_RID) { 209 *level = MEDIUM_INTEGRITY; 210 } else if (integrity_level >= SECURITY_MANDATORY_HIGH_RID) { 211 *level = HIGH_INTEGRITY; 212 } else { 213 NOTREACHED(); 214 return false; 215 } 216 217 return true; 218 } 219 220 bool LaunchAppImpl(const std::wstring& cmdline, 221 bool wait, bool start_hidden, bool inherit_handles, 222 ProcessHandle* process_handle) { 223 STARTUPINFO startup_info = {0}; 224 startup_info.cb = sizeof(startup_info); 225 startup_info.dwFlags = STARTF_USESHOWWINDOW; 226 startup_info.wShowWindow = start_hidden ? SW_HIDE : SW_SHOW; 227 PROCESS_INFORMATION process_info; 228 if (!CreateProcess(NULL, 229 const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL, 230 inherit_handles, 0, NULL, NULL, 231 &startup_info, &process_info)) 232 return false; 233 234 // Handles must be closed or they will leak 235 CloseHandle(process_info.hThread); 236 237 if (wait) 238 WaitForSingleObject(process_info.hProcess, INFINITE); 239 240 // If the caller wants the process handle, we won't close it. 241 if (process_handle) { 242 *process_handle = process_info.hProcess; 243 } else { 244 CloseHandle(process_info.hProcess); 245 } 246 return true; 247 } 248 249 bool LaunchApp(const std::wstring& cmdline, 250 bool wait, bool start_hidden, ProcessHandle* process_handle) { 251 return LaunchAppImpl(cmdline, wait, start_hidden, false, process_handle); 252 } 253 254 bool LaunchAppWithHandleInheritance( 255 const std::wstring& cmdline, bool wait, bool start_hidden, 256 ProcessHandle* process_handle) { 257 return LaunchAppImpl(cmdline, wait, start_hidden, true, process_handle); 258 } 259 260 bool LaunchAppAsUser(UserTokenHandle token, const std::wstring& cmdline, 261 bool start_hidden, ProcessHandle* process_handle) { 262 return LaunchAppAsUser(token, cmdline, start_hidden, process_handle, 263 false, false); 264 } 265 266 bool LaunchAppAsUser(UserTokenHandle token, const std::wstring& cmdline, 267 bool start_hidden, ProcessHandle* process_handle, 268 bool empty_desktop_name, bool inherit_handles) { 269 STARTUPINFO startup_info = {0}; 270 startup_info.cb = sizeof(startup_info); 271 if (empty_desktop_name) 272 startup_info.lpDesktop = L""; 273 PROCESS_INFORMATION process_info; 274 if (start_hidden) { 275 startup_info.dwFlags = STARTF_USESHOWWINDOW; 276 startup_info.wShowWindow = SW_HIDE; 277 } 278 DWORD flags = CREATE_UNICODE_ENVIRONMENT; 279 void* enviroment_block = NULL; 280 281 if (!CreateEnvironmentBlock(&enviroment_block, token, FALSE)) 282 return false; 283 284 BOOL launched = 285 CreateProcessAsUser(token, NULL, const_cast<wchar_t*>(cmdline.c_str()), 286 NULL, NULL, inherit_handles, flags, enviroment_block, 287 NULL, &startup_info, &process_info); 288 289 DestroyEnvironmentBlock(enviroment_block); 290 291 if (!launched) 292 return false; 293 294 CloseHandle(process_info.hThread); 295 296 if (process_handle) { 297 *process_handle = process_info.hProcess; 298 } else { 299 CloseHandle(process_info.hProcess); 300 } 301 return true; 302 } 303 304 bool LaunchApp(const CommandLine& cl, 305 bool wait, bool start_hidden, ProcessHandle* process_handle) { 306 return LaunchAppImpl(cl.command_line_string(), wait, 307 start_hidden, false, process_handle); 308 } 309 310 // Attempts to kill the process identified by the given process 311 // entry structure, giving it the specified exit code. 312 // Returns true if this is successful, false otherwise. 313 bool KillProcessById(ProcessId process_id, int exit_code, bool wait) { 314 HANDLE process = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE, 315 FALSE, // Don't inherit handle 316 process_id); 317 if (!process) { 318 DLOG(ERROR) << "Unable to open process " << process_id << " : " 319 << GetLastError(); 320 return false; 321 } 322 bool ret = KillProcess(process, exit_code, wait); 323 CloseHandle(process); 324 return ret; 325 } 326 327 bool GetAppOutput(const CommandLine& cl, std::string* output) { 328 HANDLE out_read = NULL; 329 HANDLE out_write = NULL; 330 331 SECURITY_ATTRIBUTES sa_attr; 332 // Set the bInheritHandle flag so pipe handles are inherited. 333 sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES); 334 sa_attr.bInheritHandle = TRUE; 335 sa_attr.lpSecurityDescriptor = NULL; 336 337 // Create the pipe for the child process's STDOUT. 338 if (!CreatePipe(&out_read, &out_write, &sa_attr, 0)) { 339 NOTREACHED() << "Failed to create pipe"; 340 return false; 341 } 342 343 // Ensure we don't leak the handles. 344 win::ScopedHandle scoped_out_read(out_read); 345 win::ScopedHandle scoped_out_write(out_write); 346 347 // Ensure the read handle to the pipe for STDOUT is not inherited. 348 if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) { 349 NOTREACHED() << "Failed to disabled pipe inheritance"; 350 return false; 351 } 352 353 // Now create the child process 354 PROCESS_INFORMATION proc_info = { 0 }; 355 STARTUPINFO start_info = { 0 }; 356 357 start_info.cb = sizeof(STARTUPINFO); 358 start_info.hStdOutput = out_write; 359 // Keep the normal stdin and stderr. 360 start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); 361 start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); 362 start_info.dwFlags |= STARTF_USESTDHANDLES; 363 364 // Create the child process. 365 if (!CreateProcess(NULL, 366 const_cast<wchar_t*>(cl.command_line_string().c_str()), 367 NULL, NULL, 368 TRUE, // Handles are inherited. 369 0, NULL, NULL, &start_info, &proc_info)) { 370 NOTREACHED() << "Failed to start process"; 371 return false; 372 } 373 374 // We don't need the thread handle, close it now. 375 CloseHandle(proc_info.hThread); 376 377 // Close our writing end of pipe now. Otherwise later read would not be able 378 // to detect end of child's output. 379 scoped_out_write.Close(); 380 381 // Read output from the child process's pipe for STDOUT 382 const int kBufferSize = 1024; 383 char buffer[kBufferSize]; 384 385 for (;;) { 386 DWORD bytes_read = 0; 387 BOOL success = ReadFile(out_read, buffer, kBufferSize, &bytes_read, NULL); 388 if (!success || bytes_read == 0) 389 break; 390 output->append(buffer, bytes_read); 391 } 392 393 // Let's wait for the process to finish. 394 WaitForSingleObject(proc_info.hProcess, INFINITE); 395 CloseHandle(proc_info.hProcess); 396 397 return true; 398 } 399 400 bool KillProcess(ProcessHandle process, int exit_code, bool wait) { 401 bool result = (TerminateProcess(process, exit_code) != FALSE); 402 if (result && wait) { 403 // The process may not end immediately due to pending I/O 404 if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000)) 405 DLOG(ERROR) << "Error waiting for process exit: " << GetLastError(); 406 } else if (!result) { 407 DLOG(ERROR) << "Unable to terminate process: " << GetLastError(); 408 } 409 return result; 410 } 411 412 TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) { 413 DWORD tmp_exit_code = 0; 414 415 if (!::GetExitCodeProcess(handle, &tmp_exit_code)) { 416 NOTREACHED(); 417 if (exit_code) { 418 // This really is a random number. We haven't received any 419 // information about the exit code, presumably because this 420 // process doesn't have permission to get the exit code, or 421 // because of some other cause for GetExitCodeProcess to fail 422 // (MSDN docs don't give the possible failure error codes for 423 // this function, so it could be anything). But we don't want 424 // to leave exit_code uninitialized, since that could cause 425 // random interpretations of the exit code. So we assume it 426 // terminated "normally" in this case. 427 *exit_code = kNormalTerminationExitCode; 428 } 429 // Assume the child has exited normally if we can't get the exit 430 // code. 431 return TERMINATION_STATUS_NORMAL_TERMINATION; 432 } 433 if (tmp_exit_code == STILL_ACTIVE) { 434 DWORD wait_result = WaitForSingleObject(handle, 0); 435 if (wait_result == WAIT_TIMEOUT) { 436 if (exit_code) 437 *exit_code = wait_result; 438 return TERMINATION_STATUS_STILL_RUNNING; 439 } 440 441 DCHECK_EQ(WAIT_OBJECT_0, wait_result); 442 443 // Strange, the process used 0x103 (STILL_ACTIVE) as exit code. 444 NOTREACHED(); 445 446 return TERMINATION_STATUS_ABNORMAL_TERMINATION; 447 } 448 449 if (exit_code) 450 *exit_code = tmp_exit_code; 451 452 switch (tmp_exit_code) { 453 case kNormalTerminationExitCode: 454 return TERMINATION_STATUS_NORMAL_TERMINATION; 455 case kDebuggerInactiveExitCode: // STATUS_DEBUGGER_INACTIVE. 456 case kKeyboardInterruptExitCode: // Control-C/end session. 457 case kDebuggerTerminatedExitCode: // Debugger terminated process. 458 case kProcessKilledExitCode: // Task manager kill. 459 return TERMINATION_STATUS_PROCESS_WAS_KILLED; 460 default: 461 // All other exit codes indicate crashes. 462 return TERMINATION_STATUS_PROCESS_CRASHED; 463 } 464 } 465 466 bool WaitForExitCode(ProcessHandle handle, int* exit_code) { 467 bool success = WaitForExitCodeWithTimeout(handle, exit_code, INFINITE); 468 CloseProcessHandle(handle); 469 return success; 470 } 471 472 bool WaitForExitCodeWithTimeout(ProcessHandle handle, int* exit_code, 473 int64 timeout_milliseconds) { 474 if (::WaitForSingleObject(handle, timeout_milliseconds) != WAIT_OBJECT_0) 475 return false; 476 DWORD temp_code; // Don't clobber out-parameters in case of failure. 477 if (!::GetExitCodeProcess(handle, &temp_code)) 478 return false; 479 480 *exit_code = temp_code; 481 return true; 482 } 483 484 ProcessIterator::ProcessIterator(const ProcessFilter* filter) 485 : started_iteration_(false), 486 filter_(filter) { 487 snapshot_ = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 488 } 489 490 ProcessIterator::~ProcessIterator() { 491 CloseHandle(snapshot_); 492 } 493 494 bool ProcessIterator::CheckForNextProcess() { 495 InitProcessEntry(&entry_); 496 497 if (!started_iteration_) { 498 started_iteration_ = true; 499 return !!Process32First(snapshot_, &entry_); 500 } 501 502 return !!Process32Next(snapshot_, &entry_); 503 } 504 505 void ProcessIterator::InitProcessEntry(ProcessEntry* entry) { 506 memset(entry, 0, sizeof(*entry)); 507 entry->dwSize = sizeof(*entry); 508 } 509 510 bool NamedProcessIterator::IncludeEntry() { 511 // Case insensitive. 512 return _wcsicmp(executable_name_.c_str(), entry().exe_file()) == 0 && 513 ProcessIterator::IncludeEntry(); 514 } 515 516 bool WaitForProcessesToExit(const std::wstring& executable_name, 517 int64 wait_milliseconds, 518 const ProcessFilter* filter) { 519 const ProcessEntry* entry; 520 bool result = true; 521 DWORD start_time = GetTickCount(); 522 523 NamedProcessIterator iter(executable_name, filter); 524 while (entry = iter.NextProcessEntry()) { 525 DWORD remaining_wait = 526 std::max<int64>(0, wait_milliseconds - (GetTickCount() - start_time)); 527 HANDLE process = OpenProcess(SYNCHRONIZE, 528 FALSE, 529 entry->th32ProcessID); 530 DWORD wait_result = WaitForSingleObject(process, remaining_wait); 531 CloseHandle(process); 532 result = result && (wait_result == WAIT_OBJECT_0); 533 } 534 535 return result; 536 } 537 538 bool WaitForSingleProcess(ProcessHandle handle, int64 wait_milliseconds) { 539 bool retval = WaitForSingleObject(handle, wait_milliseconds) == WAIT_OBJECT_0; 540 return retval; 541 } 542 543 bool CleanupProcesses(const std::wstring& executable_name, 544 int64 wait_milliseconds, 545 int exit_code, 546 const ProcessFilter* filter) { 547 bool exited_cleanly = WaitForProcessesToExit(executable_name, 548 wait_milliseconds, 549 filter); 550 if (!exited_cleanly) 551 KillProcesses(executable_name, exit_code, filter); 552 return exited_cleanly; 553 } 554 555 /////////////////////////////////////////////////////////////////////////////// 556 // ProcesMetrics 557 558 ProcessMetrics::ProcessMetrics(ProcessHandle process) 559 : process_(process), 560 processor_count_(base::SysInfo::NumberOfProcessors()), 561 last_time_(0), 562 last_system_time_(0) { 563 } 564 565 // static 566 ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) { 567 return new ProcessMetrics(process); 568 } 569 570 ProcessMetrics::~ProcessMetrics() { } 571 572 size_t ProcessMetrics::GetPagefileUsage() const { 573 PROCESS_MEMORY_COUNTERS pmc; 574 if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) { 575 return pmc.PagefileUsage; 576 } 577 return 0; 578 } 579 580 // Returns the peak space allocated for the pagefile, in bytes. 581 size_t ProcessMetrics::GetPeakPagefileUsage() const { 582 PROCESS_MEMORY_COUNTERS pmc; 583 if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) { 584 return pmc.PeakPagefileUsage; 585 } 586 return 0; 587 } 588 589 // Returns the current working set size, in bytes. 590 size_t ProcessMetrics::GetWorkingSetSize() const { 591 PROCESS_MEMORY_COUNTERS pmc; 592 if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) { 593 return pmc.WorkingSetSize; 594 } 595 return 0; 596 } 597 598 // Returns the peak working set size, in bytes. 599 size_t ProcessMetrics::GetPeakWorkingSetSize() const { 600 PROCESS_MEMORY_COUNTERS pmc; 601 if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) { 602 return pmc.PeakWorkingSetSize; 603 } 604 return 0; 605 } 606 607 bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, 608 size_t* shared_bytes) { 609 // PROCESS_MEMORY_COUNTERS_EX is not supported until XP SP2. 610 // GetProcessMemoryInfo() will simply fail on prior OS. So the requested 611 // information is simply not available. Hence, we will return 0 on unsupported 612 // OSes. Unlike most Win32 API, we don't need to initialize the "cb" member. 613 PROCESS_MEMORY_COUNTERS_EX pmcx; 614 if (private_bytes && 615 GetProcessMemoryInfo(process_, 616 reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmcx), 617 sizeof(pmcx))) { 618 *private_bytes = pmcx.PrivateUsage; 619 } 620 621 if (shared_bytes) { 622 WorkingSetKBytes ws_usage; 623 if (!GetWorkingSetKBytes(&ws_usage)) 624 return false; 625 626 *shared_bytes = ws_usage.shared * 1024; 627 } 628 629 return true; 630 } 631 632 void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) const { 633 MEMORY_BASIC_INFORMATION mbi = {0}; 634 size_t committed_private = 0; 635 size_t committed_mapped = 0; 636 size_t committed_image = 0; 637 void* base_address = NULL; 638 while (VirtualQueryEx(process_, base_address, &mbi, sizeof(mbi)) == 639 sizeof(mbi)) { 640 if (mbi.State == MEM_COMMIT) { 641 if (mbi.Type == MEM_PRIVATE) { 642 committed_private += mbi.RegionSize; 643 } else if (mbi.Type == MEM_MAPPED) { 644 committed_mapped += mbi.RegionSize; 645 } else if (mbi.Type == MEM_IMAGE) { 646 committed_image += mbi.RegionSize; 647 } else { 648 NOTREACHED(); 649 } 650 } 651 void* new_base = (static_cast<BYTE*>(mbi.BaseAddress)) + mbi.RegionSize; 652 // Avoid infinite loop by weird MEMORY_BASIC_INFORMATION. 653 // If we query 64bit processes in a 32bit process, VirtualQueryEx() 654 // returns such data. 655 if (new_base <= base_address) { 656 usage->image = 0; 657 usage->mapped = 0; 658 usage->priv = 0; 659 return; 660 } 661 base_address = new_base; 662 } 663 usage->image = committed_image / 1024; 664 usage->mapped = committed_mapped / 1024; 665 usage->priv = committed_private / 1024; 666 } 667 668 bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { 669 size_t ws_private = 0; 670 size_t ws_shareable = 0; 671 size_t ws_shared = 0; 672 673 DCHECK(ws_usage); 674 memset(ws_usage, 0, sizeof(*ws_usage)); 675 676 DWORD number_of_entries = 4096; // Just a guess. 677 PSAPI_WORKING_SET_INFORMATION* buffer = NULL; 678 int retries = 5; 679 for (;;) { 680 DWORD buffer_size = sizeof(PSAPI_WORKING_SET_INFORMATION) + 681 (number_of_entries * sizeof(PSAPI_WORKING_SET_BLOCK)); 682 683 // if we can't expand the buffer, don't leak the previous 684 // contents or pass a NULL pointer to QueryWorkingSet 685 PSAPI_WORKING_SET_INFORMATION* new_buffer = 686 reinterpret_cast<PSAPI_WORKING_SET_INFORMATION*>( 687 realloc(buffer, buffer_size)); 688 if (!new_buffer) { 689 free(buffer); 690 return false; 691 } 692 buffer = new_buffer; 693 694 // Call the function once to get number of items 695 if (QueryWorkingSet(process_, buffer, buffer_size)) 696 break; // Success 697 698 if (GetLastError() != ERROR_BAD_LENGTH) { 699 free(buffer); 700 return false; 701 } 702 703 number_of_entries = static_cast<DWORD>(buffer->NumberOfEntries); 704 705 // Maybe some entries are being added right now. Increase the buffer to 706 // take that into account. 707 number_of_entries = static_cast<DWORD>(number_of_entries * 1.25); 708 709 if (--retries == 0) { 710 free(buffer); // If we're looping, eventually fail. 711 return false; 712 } 713 } 714 715 // On windows 2000 the function returns 1 even when the buffer is too small. 716 // The number of entries that we are going to parse is the minimum between the 717 // size we allocated and the real number of entries. 718 number_of_entries = 719 std::min(number_of_entries, static_cast<DWORD>(buffer->NumberOfEntries)); 720 for (unsigned int i = 0; i < number_of_entries; i++) { 721 if (buffer->WorkingSetInfo[i].Shared) { 722 ws_shareable++; 723 if (buffer->WorkingSetInfo[i].ShareCount > 1) 724 ws_shared++; 725 } else { 726 ws_private++; 727 } 728 } 729 730 ws_usage->priv = ws_private * PAGESIZE_KB; 731 ws_usage->shareable = ws_shareable * PAGESIZE_KB; 732 ws_usage->shared = ws_shared * PAGESIZE_KB; 733 free(buffer); 734 return true; 735 } 736 737 static uint64 FileTimeToUTC(const FILETIME& ftime) { 738 LARGE_INTEGER li; 739 li.LowPart = ftime.dwLowDateTime; 740 li.HighPart = ftime.dwHighDateTime; 741 return li.QuadPart; 742 } 743 744 double ProcessMetrics::GetCPUUsage() { 745 FILETIME now; 746 FILETIME creation_time; 747 FILETIME exit_time; 748 FILETIME kernel_time; 749 FILETIME user_time; 750 751 GetSystemTimeAsFileTime(&now); 752 753 if (!GetProcessTimes(process_, &creation_time, &exit_time, 754 &kernel_time, &user_time)) { 755 // We don't assert here because in some cases (such as in the Task Manager) 756 // we may call this function on a process that has just exited but we have 757 // not yet received the notification. 758 return 0; 759 } 760 int64 system_time = (FileTimeToUTC(kernel_time) + FileTimeToUTC(user_time)) / 761 processor_count_; 762 int64 time = FileTimeToUTC(now); 763 764 if ((last_system_time_ == 0) || (last_time_ == 0)) { 765 // First call, just set the last values. 766 last_system_time_ = system_time; 767 last_time_ = time; 768 return 0; 769 } 770 771 int64 system_time_delta = system_time - last_system_time_; 772 int64 time_delta = time - last_time_; 773 DCHECK(time_delta != 0); 774 if (time_delta == 0) 775 return 0; 776 777 // We add time_delta / 2 so the result is rounded. 778 int cpu = static_cast<int>((system_time_delta * 100 + time_delta / 2) / 779 time_delta); 780 781 last_system_time_ = system_time; 782 last_time_ = time; 783 784 return cpu; 785 } 786 787 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { 788 return GetProcessIoCounters(process_, io_counters) != FALSE; 789 } 790 791 bool ProcessMetrics::CalculateFreeMemory(FreeMBytes* free) const { 792 const SIZE_T kTopAddress = 0x7F000000; 793 const SIZE_T kMegabyte = 1024 * 1024; 794 SIZE_T accumulated = 0; 795 796 MEMORY_BASIC_INFORMATION largest = {0}; 797 UINT_PTR scan = 0; 798 while (scan < kTopAddress) { 799 MEMORY_BASIC_INFORMATION info; 800 if (!::VirtualQueryEx(process_, reinterpret_cast<void*>(scan), 801 &info, sizeof(info))) 802 return false; 803 if (info.State == MEM_FREE) { 804 accumulated += info.RegionSize; 805 UINT_PTR end = scan + info.RegionSize; 806 if (info.RegionSize > largest.RegionSize) 807 largest = info; 808 } 809 scan += info.RegionSize; 810 } 811 free->largest = largest.RegionSize / kMegabyte; 812 free->largest_ptr = largest.BaseAddress; 813 free->total = accumulated / kMegabyte; 814 return true; 815 } 816 817 bool EnableLowFragmentationHeap() { 818 HMODULE kernel32 = GetModuleHandle(L"kernel32.dll"); 819 HeapSetFn heap_set = reinterpret_cast<HeapSetFn>(GetProcAddress( 820 kernel32, 821 "HeapSetInformation")); 822 823 // On Windows 2000, the function is not exported. This is not a reason to 824 // fail. 825 if (!heap_set) 826 return true; 827 828 unsigned number_heaps = GetProcessHeaps(0, NULL); 829 if (!number_heaps) 830 return false; 831 832 // Gives us some extra space in the array in case a thread is creating heaps 833 // at the same time we're querying them. 834 static const int MARGIN = 8; 835 scoped_array<HANDLE> heaps(new HANDLE[number_heaps + MARGIN]); 836 number_heaps = GetProcessHeaps(number_heaps + MARGIN, heaps.get()); 837 if (!number_heaps) 838 return false; 839 840 for (unsigned i = 0; i < number_heaps; ++i) { 841 ULONG lfh_flag = 2; 842 // Don't bother with the result code. It may fails on heaps that have the 843 // HEAP_NO_SERIALIZE flag. This is expected and not a problem at all. 844 heap_set(heaps[i], 845 HeapCompatibilityInformation, 846 &lfh_flag, 847 sizeof(lfh_flag)); 848 } 849 return true; 850 } 851 852 void EnableTerminationOnHeapCorruption() { 853 // Ignore the result code. Supported on XP SP3 and Vista. 854 HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); 855 } 856 857 bool EnableInProcessStackDumping() { 858 // Add stack dumping support on exception on windows. Similar to OS_POSIX 859 // signal() handling in process_util_posix.cc. 860 g_previous_filter = SetUnhandledExceptionFilter(&StackDumpExceptionFilter); 861 AttachToConsole(); 862 return true; 863 } 864 865 void RaiseProcessToHighPriority() { 866 SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); 867 } 868 869 // GetPerformanceInfo is not available on WIN2K. So we'll 870 // load it on-the-fly. 871 const wchar_t kPsapiDllName[] = L"psapi.dll"; 872 typedef BOOL (WINAPI *GetPerformanceInfoFunction) ( 873 PPERFORMANCE_INFORMATION pPerformanceInformation, 874 DWORD cb); 875 876 // Beware of races if called concurrently from multiple threads. 877 static BOOL InternalGetPerformanceInfo( 878 PPERFORMANCE_INFORMATION pPerformanceInformation, DWORD cb) { 879 static GetPerformanceInfoFunction GetPerformanceInfo_func = NULL; 880 if (!GetPerformanceInfo_func) { 881 HMODULE psapi_dll = ::GetModuleHandle(kPsapiDllName); 882 if (psapi_dll) 883 GetPerformanceInfo_func = reinterpret_cast<GetPerformanceInfoFunction>( 884 GetProcAddress(psapi_dll, "GetPerformanceInfo")); 885 886 if (!GetPerformanceInfo_func) { 887 // The function could be loaded! 888 memset(pPerformanceInformation, 0, cb); 889 return FALSE; 890 } 891 } 892 return GetPerformanceInfo_func(pPerformanceInformation, cb); 893 } 894 895 size_t GetSystemCommitCharge() { 896 // Get the System Page Size. 897 SYSTEM_INFO system_info; 898 GetSystemInfo(&system_info); 899 900 PERFORMANCE_INFORMATION info; 901 if (!InternalGetPerformanceInfo(&info, sizeof(info))) { 902 LOG(ERROR) << "Failed to fetch internal performance info."; 903 return 0; 904 } 905 return (info.CommitTotal * system_info.dwPageSize) / 1024; 906 } 907 908 } // namespace base 909