1 // Copyright (c) 2013 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 #include "base/process/kill.h"
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/logging.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/process/process_iterator.h"
15 #include "base/profiler/scoped_tracker.h"
16 #include "base/win/object_watcher.h"
22 // Exit codes with special meanings on Windows.
23 const DWORD kNormalTerminationExitCode
= 0;
24 const DWORD kDebuggerInactiveExitCode
= 0xC0000354;
25 const DWORD kKeyboardInterruptExitCode
= 0xC000013A;
26 const DWORD kDebuggerTerminatedExitCode
= 0x40010004;
28 // This exit code is used by the Windows task manager when it kills a
29 // process. It's value is obviously not that unique, and it's
30 // surprising to me that the task manager uses this value, but it
31 // seems to be common practice on Windows to test for it as an
32 // indication that the task manager has killed something if the
34 const DWORD kProcessKilledExitCode
= 1;
36 // Maximum amount of time (in milliseconds) to wait for the process to exit.
37 static const int kWaitInterval
= 2000;
39 class TimerExpiredTask
: public win::ObjectWatcher::Delegate
{
41 explicit TimerExpiredTask(ProcessHandle process
);
46 // MessageLoop::Watcher -----------------------------------------------------
47 virtual void OnObjectSignaled(HANDLE object
);
52 // The process that we are watching.
53 ProcessHandle process_
;
55 win::ObjectWatcher watcher_
;
57 DISALLOW_COPY_AND_ASSIGN(TimerExpiredTask
);
60 TimerExpiredTask::TimerExpiredTask(ProcessHandle process
) : process_(process
) {
61 watcher_
.StartWatching(process_
, this);
64 TimerExpiredTask::~TimerExpiredTask() {
66 DCHECK(!process_
) << "Make sure to close the handle.";
69 void TimerExpiredTask::TimedOut() {
74 void TimerExpiredTask::OnObjectSignaled(HANDLE object
) {
75 // TODO(vadimt): Remove ScopedTracker below once crbug.com/418183 is fixed.
76 tracked_objects::ScopedTracker
tracking_profile(
77 FROM_HERE_WITH_EXPLICIT_FUNCTION("TimerExpiredTask_OnObjectSignaled"));
79 CloseHandle(process_
);
83 void TimerExpiredTask::KillProcess() {
84 // Stop watching the process handle since we're killing it.
85 watcher_
.StopWatching();
87 // OK, time to get frisky. We don't actually care when the process
88 // terminates. We just care that it eventually terminates, and that's what
89 // TerminateProcess should do for us. Don't check for the result code since
90 // it fails quite often. This should be investigated eventually.
91 base::KillProcess(process_
, kProcessKilledExitCode
, false);
93 // Now, just cleanup as if the process exited normally.
94 OnObjectSignaled(process_
);
99 bool KillProcess(ProcessHandle process
, int exit_code
, bool wait
) {
100 bool result
= (TerminateProcess(process
, exit_code
) != FALSE
);
101 if (result
&& wait
) {
102 // The process may not end immediately due to pending I/O
103 if (WAIT_OBJECT_0
!= WaitForSingleObject(process
, 60 * 1000))
104 DPLOG(ERROR
) << "Error waiting for process exit";
105 } else if (!result
) {
106 DPLOG(ERROR
) << "Unable to terminate process";
111 // Attempts to kill the process identified by the given process
112 // entry structure, giving it the specified exit code.
113 // Returns true if this is successful, false otherwise.
114 bool KillProcessById(ProcessId process_id
, int exit_code
, bool wait
) {
115 HANDLE process
= OpenProcess(PROCESS_TERMINATE
| SYNCHRONIZE
,
116 FALSE
, // Don't inherit handle
119 DPLOG(ERROR
) << "Unable to open process " << process_id
;
122 bool ret
= KillProcess(process
, exit_code
, wait
);
123 CloseHandle(process
);
127 TerminationStatus
GetTerminationStatus(ProcessHandle handle
, int* exit_code
) {
128 DWORD tmp_exit_code
= 0;
130 if (!::GetExitCodeProcess(handle
, &tmp_exit_code
)) {
131 DPLOG(FATAL
) << "GetExitCodeProcess() failed";
133 // This really is a random number. We haven't received any
134 // information about the exit code, presumably because this
135 // process doesn't have permission to get the exit code, or
136 // because of some other cause for GetExitCodeProcess to fail
137 // (MSDN docs don't give the possible failure error codes for
138 // this function, so it could be anything). But we don't want
139 // to leave exit_code uninitialized, since that could cause
140 // random interpretations of the exit code. So we assume it
141 // terminated "normally" in this case.
142 *exit_code
= kNormalTerminationExitCode
;
144 // Assume the child has exited normally if we can't get the exit
146 return TERMINATION_STATUS_NORMAL_TERMINATION
;
148 if (tmp_exit_code
== STILL_ACTIVE
) {
149 DWORD wait_result
= WaitForSingleObject(handle
, 0);
150 if (wait_result
== WAIT_TIMEOUT
) {
152 *exit_code
= wait_result
;
153 return TERMINATION_STATUS_STILL_RUNNING
;
156 if (wait_result
== WAIT_FAILED
) {
157 DPLOG(ERROR
) << "WaitForSingleObject() failed";
159 DCHECK_EQ(WAIT_OBJECT_0
, wait_result
);
161 // Strange, the process used 0x103 (STILL_ACTIVE) as exit code.
165 return TERMINATION_STATUS_ABNORMAL_TERMINATION
;
169 *exit_code
= tmp_exit_code
;
171 switch (tmp_exit_code
) {
172 case kNormalTerminationExitCode
:
173 return TERMINATION_STATUS_NORMAL_TERMINATION
;
174 case kDebuggerInactiveExitCode
: // STATUS_DEBUGGER_INACTIVE.
175 case kKeyboardInterruptExitCode
: // Control-C/end session.
176 case kDebuggerTerminatedExitCode
: // Debugger terminated process.
177 case kProcessKilledExitCode
: // Task manager kill.
178 return TERMINATION_STATUS_PROCESS_WAS_KILLED
;
180 // All other exit codes indicate crashes.
181 return TERMINATION_STATUS_PROCESS_CRASHED
;
185 bool WaitForExitCode(ProcessHandle handle
, int* exit_code
) {
186 bool success
= WaitForExitCodeWithTimeout(
187 handle
, exit_code
, base::TimeDelta::FromMilliseconds(INFINITE
));
188 CloseProcessHandle(handle
);
192 bool WaitForExitCodeWithTimeout(ProcessHandle handle
,
194 base::TimeDelta timeout
) {
195 if (::WaitForSingleObject(
196 handle
, static_cast<DWORD
>(timeout
.InMilliseconds())) != WAIT_OBJECT_0
)
198 DWORD temp_code
; // Don't clobber out-parameters in case of failure.
199 if (!::GetExitCodeProcess(handle
, &temp_code
))
202 *exit_code
= temp_code
;
206 bool WaitForProcessesToExit(const FilePath::StringType
& executable_name
,
207 base::TimeDelta wait
,
208 const ProcessFilter
* filter
) {
210 DWORD start_time
= GetTickCount();
212 NamedProcessIterator
iter(executable_name
, filter
);
213 for (const ProcessEntry
* entry
= iter
.NextProcessEntry(); entry
;
214 entry
= iter
.NextProcessEntry()) {
215 DWORD remaining_wait
= static_cast<DWORD
>(std::max(
216 static_cast<int64
>(0),
217 wait
.InMilliseconds() - (GetTickCount() - start_time
)));
218 HANDLE process
= OpenProcess(SYNCHRONIZE
,
220 entry
->th32ProcessID
);
221 DWORD wait_result
= WaitForSingleObject(process
, remaining_wait
);
222 CloseHandle(process
);
223 result
&= (wait_result
== WAIT_OBJECT_0
);
229 bool WaitForSingleProcess(ProcessHandle handle
, base::TimeDelta wait
) {
231 return WaitForExitCodeWithTimeout(handle
, &exit_code
, wait
) && exit_code
== 0;
234 bool CleanupProcesses(const FilePath::StringType
& executable_name
,
235 base::TimeDelta wait
,
237 const ProcessFilter
* filter
) {
238 if (WaitForProcessesToExit(executable_name
, wait
, filter
))
240 KillProcesses(executable_name
, exit_code
, filter
);
244 void EnsureProcessTerminated(ProcessHandle process
) {
245 DCHECK(process
!= GetCurrentProcess());
247 // If already signaled, then we are done!
248 if (WaitForSingleObject(process
, 0) == WAIT_OBJECT_0
) {
249 CloseHandle(process
);
253 MessageLoop::current()->PostDelayedTask(
255 base::Bind(&TimerExpiredTask::TimedOut
,
256 base::Owned(new TimerExpiredTask(process
))),
257 base::TimeDelta::FromMilliseconds(kWaitInterval
));