OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/process_util.h" | 5 #include "base/process_util.h" |
6 | 6 |
7 #include <fcntl.h> | 7 #include <fcntl.h> |
8 #include <io.h> | 8 #include <io.h> |
9 #include <windows.h> | 9 #include <windows.h> |
10 #include <userenv.h> | 10 #include <userenv.h> |
(...skipping 12 matching lines...) Expand all Loading... |
23 // userenv.dll is required for CreateEnvironmentBlock(). | 23 // userenv.dll is required for CreateEnvironmentBlock(). |
24 #pragma comment(lib, "userenv.lib") | 24 #pragma comment(lib, "userenv.lib") |
25 | 25 |
26 namespace base { | 26 namespace base { |
27 | 27 |
28 namespace { | 28 namespace { |
29 | 29 |
30 // System pagesize. This value remains constant on x86/64 architectures. | 30 // System pagesize. This value remains constant on x86/64 architectures. |
31 const int PAGESIZE_KB = 4; | 31 const int PAGESIZE_KB = 4; |
32 | 32 |
| 33 // Exit codes with special meanings on Windows. |
| 34 const DWORD kNormalTerminationExitCode = 0; |
| 35 const DWORD kDebuggerInactiveExitCode = 0xC0000354; |
| 36 const DWORD kKeyboardInterruptExitCode = 0xC000013A; |
| 37 const DWORD kDebuggerTerminatedExitCode = 0x40010004; |
| 38 |
| 39 // This exit code is used by the Windows task manager when it kills a |
| 40 // process. It's value is obviously not that unique, and it's |
| 41 // surprising to me that the task manager uses this value, but it |
| 42 // seems to be common practice on Windows to test for it as an |
| 43 // indication that the task manager has killed something if the |
| 44 // process goes away. |
| 45 const DWORD kProcessKilledExitCode = 1; |
| 46 |
33 // HeapSetInformation function pointer. | 47 // HeapSetInformation function pointer. |
34 typedef BOOL (WINAPI* HeapSetFn)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T); | 48 typedef BOOL (WINAPI* HeapSetFn)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T); |
35 | 49 |
36 // Previous unhandled filter. Will be called if not NULL when we intercept an | 50 // Previous unhandled filter. Will be called if not NULL when we intercept an |
37 // exception. Only used in unit tests. | 51 // exception. Only used in unit tests. |
38 LPTOP_LEVEL_EXCEPTION_FILTER g_previous_filter = NULL; | 52 LPTOP_LEVEL_EXCEPTION_FILTER g_previous_filter = NULL; |
39 | 53 |
40 // Prints the exception call stack. | 54 // Prints the exception call stack. |
41 // This is the unit tests exception filter. | 55 // This is the unit tests exception filter. |
42 long WINAPI StackDumpExceptionFilter(EXCEPTION_POINTERS* info) { | 56 long WINAPI StackDumpExceptionFilter(EXCEPTION_POINTERS* info) { |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
98 | 112 |
99 if (result == INVALID_HANDLE_VALUE) | 113 if (result == INVALID_HANDLE_VALUE) |
100 return false; | 114 return false; |
101 | 115 |
102 *handle = result; | 116 *handle = result; |
103 return true; | 117 return true; |
104 } | 118 } |
105 | 119 |
106 bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) { | 120 bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) { |
107 ProcessHandle result = OpenProcess(PROCESS_DUP_HANDLE | | 121 ProcessHandle result = OpenProcess(PROCESS_DUP_HANDLE | |
108 PROCESS_TERMINATE | | 122 PROCESS_TERMINATE | |
109 PROCESS_QUERY_INFORMATION | | 123 PROCESS_QUERY_INFORMATION | |
110 PROCESS_VM_READ | | 124 PROCESS_VM_READ | |
111 SYNCHRONIZE, | 125 SYNCHRONIZE, |
112 FALSE, pid); | 126 FALSE, pid); |
113 | 127 |
114 if (result == INVALID_HANDLE_VALUE) | 128 if (result == INVALID_HANDLE_VALUE) |
115 return false; | 129 return false; |
116 | 130 |
117 *handle = result; | 131 *handle = result; |
118 return true; | 132 return true; |
119 } | 133 } |
120 | 134 |
121 bool OpenProcessHandleWithAccess(ProcessId pid, | 135 bool OpenProcessHandleWithAccess(ProcessId pid, |
(...skipping 265 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
387 if (result && wait) { | 401 if (result && wait) { |
388 // The process may not end immediately due to pending I/O | 402 // The process may not end immediately due to pending I/O |
389 if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000)) | 403 if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000)) |
390 DLOG(ERROR) << "Error waiting for process exit: " << GetLastError(); | 404 DLOG(ERROR) << "Error waiting for process exit: " << GetLastError(); |
391 } else if (!result) { | 405 } else if (!result) { |
392 DLOG(ERROR) << "Unable to terminate process: " << GetLastError(); | 406 DLOG(ERROR) << "Unable to terminate process: " << GetLastError(); |
393 } | 407 } |
394 return result; | 408 return result; |
395 } | 409 } |
396 | 410 |
397 bool DidProcessCrash(bool* child_exited, ProcessHandle handle) { | 411 TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) { |
398 DWORD exitcode = 0; | 412 DWORD tmp_exit_code = 0; |
399 | 413 |
400 if (!::GetExitCodeProcess(handle, &exitcode)) { | 414 if (!::GetExitCodeProcess(handle, &tmp_exit_code)) { |
401 NOTREACHED(); | 415 NOTREACHED(); |
402 // Assume the child has exited. | 416 if (exit_code) { |
403 if (child_exited) | 417 // This really is a random number. We haven't received any |
404 *child_exited = true; | 418 // information about the exit code, presumably because this |
405 return false; | 419 // process doesn't have permission to get the exit code, or |
| 420 // because of some other cause for GetExitCodeProcess to fail |
| 421 // (MSDN docs don't give the possible failure error codes for |
| 422 // this function, so it could be anything). But we don't want |
| 423 // to leave exit_code uninitialized, since that could cause |
| 424 // random interpretations of the exit code. So we assume it |
| 425 // terminated "normally" in this case. |
| 426 *exit_code = kNormalTerminationExitCode; |
| 427 } |
| 428 // Assume the child has exited normally if we can't get the exit |
| 429 // code. |
| 430 return TERMINATION_STATUS_NORMAL_TERMINATION; |
406 } | 431 } |
407 if (exitcode == STILL_ACTIVE) { | 432 if (tmp_exit_code == STILL_ACTIVE) { |
408 DWORD wait_result = WaitForSingleObject(handle, 0); | 433 DWORD wait_result = WaitForSingleObject(handle, 0); |
409 if (wait_result == WAIT_TIMEOUT) { | 434 if (wait_result == WAIT_TIMEOUT) { |
410 if (child_exited) | 435 if (exit_code) |
411 *child_exited = false; | 436 *exit_code = wait_result; |
412 return false; | 437 return TERMINATION_STATUS_STILL_RUNNING; |
413 } | 438 } |
414 | 439 |
415 DCHECK_EQ(WAIT_OBJECT_0, wait_result); | 440 DCHECK_EQ(WAIT_OBJECT_0, wait_result); |
416 | 441 |
417 // Strange, the process used 0x103 (STILL_ACTIVE) as exit code. | 442 // Strange, the process used 0x103 (STILL_ACTIVE) as exit code. |
418 NOTREACHED(); | 443 NOTREACHED(); |
419 | 444 |
420 return false; | 445 return TERMINATION_STATUS_ABNORMAL_TERMINATION; |
421 } | 446 } |
422 | 447 |
423 // We're sure the child has exited. | 448 if (exit_code) |
424 if (child_exited) | 449 *exit_code = tmp_exit_code; |
425 *child_exited = true; | |
426 | 450 |
427 // Warning, this is not generic code; it heavily depends on the way | 451 switch (tmp_exit_code) { |
428 // the rest of the code kills a process. | 452 case kNormalTerminationExitCode: |
429 | 453 return TERMINATION_STATUS_NORMAL_TERMINATION; |
430 if (exitcode == PROCESS_END_NORMAL_TERMINATION || | 454 case kDebuggerInactiveExitCode: // STATUS_DEBUGGER_INACTIVE. |
431 exitcode == PROCESS_END_KILLED_BY_USER || | 455 case kKeyboardInterruptExitCode: // Control-C/end session. |
432 exitcode == PROCESS_END_PROCESS_WAS_HUNG || | 456 case kDebuggerTerminatedExitCode: // Debugger terminated process. |
433 exitcode == 0xC0000354 || // STATUS_DEBUGGER_INACTIVE. | 457 case kProcessKilledExitCode: // Task manager kill. |
434 exitcode == 0xC000013A || // Control-C/end session. | 458 return TERMINATION_STATUS_PROCESS_WAS_KILLED; |
435 exitcode == 0x40010004) { // Debugger terminated process/end session. | 459 default: |
436 return false; | 460 // All other exit codes indicate crashes. |
| 461 return TERMINATION_STATUS_PROCESS_CRASHED; |
437 } | 462 } |
438 | |
439 // All other exit codes indicate crashes. | |
440 return true; | |
441 } | 463 } |
442 | 464 |
443 bool WaitForExitCode(ProcessHandle handle, int* exit_code) { | 465 bool WaitForExitCode(ProcessHandle handle, int* exit_code) { |
444 bool success = WaitForExitCodeWithTimeout(handle, exit_code, INFINITE); | 466 bool success = WaitForExitCodeWithTimeout(handle, exit_code, INFINITE); |
445 if (!success) | 467 if (!success) |
446 CloseProcessHandle(handle); | 468 CloseProcessHandle(handle); |
447 return success; | 469 return success; |
448 } | 470 } |
449 | 471 |
450 bool WaitForExitCodeWithTimeout(ProcessHandle handle, int* exit_code, | 472 bool WaitForExitCodeWithTimeout(ProcessHandle handle, int* exit_code, |
(...skipping 436 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
887 | 909 |
888 PERFORMANCE_INFORMATION info; | 910 PERFORMANCE_INFORMATION info; |
889 if (!InternalGetPerformanceInfo(&info, sizeof(info))) { | 911 if (!InternalGetPerformanceInfo(&info, sizeof(info))) { |
890 LOG(ERROR) << "Failed to fetch internal performance info."; | 912 LOG(ERROR) << "Failed to fetch internal performance info."; |
891 return 0; | 913 return 0; |
892 } | 914 } |
893 return (info.CommitTotal * system_info.dwPageSize) / 1024; | 915 return (info.CommitTotal * system_info.dwPageSize) / 1024; |
894 } | 916 } |
895 | 917 |
896 } // namespace base | 918 } // namespace base |
OLD | NEW |