Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(294)

Side by Side Diff: base/process_util_win.cc

Issue 18555002: Split out process killing functions from base/process_util.h into base/process/kill.h. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « base/process_util_posix.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 15 matching lines...) Expand all
26 #include "base/win/scoped_process_information.h" 26 #include "base/win/scoped_process_information.h"
27 #include "base/win/windows_version.h" 27 #include "base/win/windows_version.h"
28 28
29 // userenv.dll is required for CreateEnvironmentBlock(). 29 // userenv.dll is required for CreateEnvironmentBlock().
30 #pragma comment(lib, "userenv.lib") 30 #pragma comment(lib, "userenv.lib")
31 31
32 namespace base { 32 namespace base {
33 33
34 namespace { 34 namespace {
35 35
36 // Exit codes with special meanings on Windows.
37 const DWORD kNormalTerminationExitCode = 0;
38 const DWORD kDebuggerInactiveExitCode = 0xC0000354;
39 const DWORD kKeyboardInterruptExitCode = 0xC000013A;
40 const DWORD kDebuggerTerminatedExitCode = 0x40010004;
41
42 // Maximum amount of time (in milliseconds) to wait for the process to exit.
43 static const int kWaitInterval = 2000;
44
45 // This exit code is used by the Windows task manager when it kills a 36 // This exit code is used by the Windows task manager when it kills a
46 // process. It's value is obviously not that unique, and it's 37 // process. It's value is obviously not that unique, and it's
47 // surprising to me that the task manager uses this value, but it 38 // surprising to me that the task manager uses this value, but it
48 // seems to be common practice on Windows to test for it as an 39 // seems to be common practice on Windows to test for it as an
49 // indication that the task manager has killed something if the 40 // indication that the task manager has killed something if the
50 // process goes away. 41 // process goes away.
51 const DWORD kProcessKilledExitCode = 1; 42 const DWORD kProcessKilledExitCode = 1;
52 43
53 class TimerExpiredTask : public win::ObjectWatcher::Delegate {
54 public:
55 explicit TimerExpiredTask(ProcessHandle process);
56 ~TimerExpiredTask();
57
58 void TimedOut();
59
60 // MessageLoop::Watcher -----------------------------------------------------
61 virtual void OnObjectSignaled(HANDLE object);
62
63 private:
64 void KillProcess();
65
66 // The process that we are watching.
67 ProcessHandle process_;
68
69 win::ObjectWatcher watcher_;
70
71 DISALLOW_COPY_AND_ASSIGN(TimerExpiredTask);
72 };
73
74 TimerExpiredTask::TimerExpiredTask(ProcessHandle process) : process_(process) {
75 watcher_.StartWatching(process_, this);
76 }
77
78 TimerExpiredTask::~TimerExpiredTask() {
79 TimedOut();
80 DCHECK(!process_) << "Make sure to close the handle.";
81 }
82
83 void TimerExpiredTask::TimedOut() {
84 if (process_)
85 KillProcess();
86 }
87
88 void TimerExpiredTask::OnObjectSignaled(HANDLE object) {
89 CloseHandle(process_);
90 process_ = NULL;
91 }
92
93 void TimerExpiredTask::KillProcess() {
94 // Stop watching the process handle since we're killing it.
95 watcher_.StopWatching();
96
97 // OK, time to get frisky. We don't actually care when the process
98 // terminates. We just care that it eventually terminates, and that's what
99 // TerminateProcess should do for us. Don't check for the result code since
100 // it fails quite often. This should be investigated eventually.
101 base::KillProcess(process_, kProcessKilledExitCode, false);
102
103 // Now, just cleanup as if the process exited normally.
104 OnObjectSignaled(process_);
105 }
106
107 } // namespace 44 } // namespace
108 45
109 void RouteStdioToConsole() { 46 void RouteStdioToConsole() {
110 // Don't change anything if stdout or stderr already point to a 47 // Don't change anything if stdout or stderr already point to a
111 // valid stream. 48 // valid stream.
112 // 49 //
113 // If we are running under Buildbot or under Cygwin's default 50 // If we are running under Buildbot or under Cygwin's default
114 // terminal (mintty), stderr and stderr will be pipe handles. In 51 // terminal (mintty), stderr and stderr will be pipe handles. In
115 // that case, we don't want to open CONOUT$, because its output 52 // that case, we don't want to open CONOUT$, because its output
116 // likely does not go anywhere. 53 // likely does not go anywhere.
(...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after
372 JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {0}; 309 JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {0};
373 limit_info.BasicLimitInformation.LimitFlags = 310 limit_info.BasicLimitInformation.LimitFlags =
374 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; 311 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
375 return 0 != SetInformationJobObject( 312 return 0 != SetInformationJobObject(
376 job_object, 313 job_object,
377 JobObjectExtendedLimitInformation, 314 JobObjectExtendedLimitInformation,
378 &limit_info, 315 &limit_info,
379 sizeof(limit_info)); 316 sizeof(limit_info));
380 } 317 }
381 318
382 // Attempts to kill the process identified by the given process
383 // entry structure, giving it the specified exit code.
384 // Returns true if this is successful, false otherwise.
385 bool KillProcessById(ProcessId process_id, int exit_code, bool wait) {
386 HANDLE process = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE,
387 FALSE, // Don't inherit handle
388 process_id);
389 if (!process) {
390 DLOG_GETLASTERROR(ERROR) << "Unable to open process " << process_id;
391 return false;
392 }
393 bool ret = KillProcess(process, exit_code, wait);
394 CloseHandle(process);
395 return ret;
396 }
397
398 bool GetAppOutput(const CommandLine& cl, std::string* output) { 319 bool GetAppOutput(const CommandLine& cl, std::string* output) {
399 HANDLE out_read = NULL; 320 HANDLE out_read = NULL;
400 HANDLE out_write = NULL; 321 HANDLE out_write = NULL;
401 322
402 SECURITY_ATTRIBUTES sa_attr; 323 SECURITY_ATTRIBUTES sa_attr;
403 // Set the bInheritHandle flag so pipe handles are inherited. 324 // Set the bInheritHandle flag so pipe handles are inherited.
404 sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES); 325 sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
405 sa_attr.bInheritHandle = TRUE; 326 sa_attr.bInheritHandle = TRUE;
406 sa_attr.lpSecurityDescriptor = NULL; 327 sa_attr.lpSecurityDescriptor = NULL;
407 328
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
458 break; 379 break;
459 output->append(buffer, bytes_read); 380 output->append(buffer, bytes_read);
460 } 381 }
461 382
462 // Let's wait for the process to finish. 383 // Let's wait for the process to finish.
463 WaitForSingleObject(proc_info.process_handle(), INFINITE); 384 WaitForSingleObject(proc_info.process_handle(), INFINITE);
464 385
465 return true; 386 return true;
466 } 387 }
467 388
468 bool KillProcess(ProcessHandle process, int exit_code, bool wait) {
469 bool result = (TerminateProcess(process, exit_code) != FALSE);
470 if (result && wait) {
471 // The process may not end immediately due to pending I/O
472 if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000))
473 DLOG_GETLASTERROR(ERROR) << "Error waiting for process exit";
474 } else if (!result) {
475 DLOG_GETLASTERROR(ERROR) << "Unable to terminate process";
476 }
477 return result;
478 }
479
480 TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
481 DWORD tmp_exit_code = 0;
482
483 if (!::GetExitCodeProcess(handle, &tmp_exit_code)) {
484 DLOG_GETLASTERROR(FATAL) << "GetExitCodeProcess() failed";
485 if (exit_code) {
486 // This really is a random number. We haven't received any
487 // information about the exit code, presumably because this
488 // process doesn't have permission to get the exit code, or
489 // because of some other cause for GetExitCodeProcess to fail
490 // (MSDN docs don't give the possible failure error codes for
491 // this function, so it could be anything). But we don't want
492 // to leave exit_code uninitialized, since that could cause
493 // random interpretations of the exit code. So we assume it
494 // terminated "normally" in this case.
495 *exit_code = kNormalTerminationExitCode;
496 }
497 // Assume the child has exited normally if we can't get the exit
498 // code.
499 return TERMINATION_STATUS_NORMAL_TERMINATION;
500 }
501 if (tmp_exit_code == STILL_ACTIVE) {
502 DWORD wait_result = WaitForSingleObject(handle, 0);
503 if (wait_result == WAIT_TIMEOUT) {
504 if (exit_code)
505 *exit_code = wait_result;
506 return TERMINATION_STATUS_STILL_RUNNING;
507 }
508
509 if (wait_result == WAIT_FAILED) {
510 DLOG_GETLASTERROR(ERROR) << "WaitForSingleObject() failed";
511 } else {
512 DCHECK_EQ(WAIT_OBJECT_0, wait_result);
513
514 // Strange, the process used 0x103 (STILL_ACTIVE) as exit code.
515 NOTREACHED();
516 }
517
518 return TERMINATION_STATUS_ABNORMAL_TERMINATION;
519 }
520
521 if (exit_code)
522 *exit_code = tmp_exit_code;
523
524 switch (tmp_exit_code) {
525 case kNormalTerminationExitCode:
526 return TERMINATION_STATUS_NORMAL_TERMINATION;
527 case kDebuggerInactiveExitCode: // STATUS_DEBUGGER_INACTIVE.
528 case kKeyboardInterruptExitCode: // Control-C/end session.
529 case kDebuggerTerminatedExitCode: // Debugger terminated process.
530 case kProcessKilledExitCode: // Task manager kill.
531 return TERMINATION_STATUS_PROCESS_WAS_KILLED;
532 default:
533 // All other exit codes indicate crashes.
534 return TERMINATION_STATUS_PROCESS_CRASHED;
535 }
536 }
537
538 bool WaitForExitCode(ProcessHandle handle, int* exit_code) {
539 bool success = WaitForExitCodeWithTimeout(
540 handle, exit_code, base::TimeDelta::FromMilliseconds(INFINITE));
541 CloseProcessHandle(handle);
542 return success;
543 }
544
545 bool WaitForExitCodeWithTimeout(ProcessHandle handle, int* exit_code,
546 base::TimeDelta timeout) {
547 if (::WaitForSingleObject(handle, timeout.InMilliseconds()) != WAIT_OBJECT_0)
548 return false;
549 DWORD temp_code; // Don't clobber out-parameters in case of failure.
550 if (!::GetExitCodeProcess(handle, &temp_code))
551 return false;
552
553 *exit_code = temp_code;
554 return true;
555 }
556
557 bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
558 base::TimeDelta wait,
559 const ProcessFilter* filter) {
560 const ProcessEntry* entry;
561 bool result = true;
562 DWORD start_time = GetTickCount();
563
564 NamedProcessIterator iter(executable_name, filter);
565 while ((entry = iter.NextProcessEntry())) {
566 DWORD remaining_wait = std::max<int64>(
567 0, wait.InMilliseconds() - (GetTickCount() - start_time));
568 HANDLE process = OpenProcess(SYNCHRONIZE,
569 FALSE,
570 entry->th32ProcessID);
571 DWORD wait_result = WaitForSingleObject(process, remaining_wait);
572 CloseHandle(process);
573 result = result && (wait_result == WAIT_OBJECT_0);
574 }
575
576 return result;
577 }
578
579 bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) {
580 int exit_code;
581 if (!WaitForExitCodeWithTimeout(handle, &exit_code, wait))
582 return false;
583 return exit_code == 0;
584 }
585
586 bool CleanupProcesses(const FilePath::StringType& executable_name,
587 base::TimeDelta wait,
588 int exit_code,
589 const ProcessFilter* filter) {
590 bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter);
591 if (!exited_cleanly)
592 KillProcesses(executable_name, exit_code, filter);
593 return exited_cleanly;
594 }
595
596 void EnsureProcessTerminated(ProcessHandle process) {
597 DCHECK(process != GetCurrentProcess());
598
599 // If already signaled, then we are done!
600 if (WaitForSingleObject(process, 0) == WAIT_OBJECT_0) {
601 CloseHandle(process);
602 return;
603 }
604
605 MessageLoop::current()->PostDelayedTask(
606 FROM_HERE,
607 base::Bind(&TimerExpiredTask::TimedOut,
608 base::Owned(new TimerExpiredTask(process))),
609 base::TimeDelta::FromMilliseconds(kWaitInterval));
610 }
611
612 void RaiseProcessToHighPriority() { 389 void RaiseProcessToHighPriority() {
613 SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); 390 SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
614 } 391 }
615 392
616 } // namespace base 393 } // namespace base
OLDNEW
« no previous file with comments | « base/process_util_posix.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698