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

Side by Side Diff: client/crashpad_client_win.cc

Issue 1356383002: win: Implement CRASHPAD_SIMULATE_CRASH() (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: . Created 5 years, 3 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
OLDNEW
1 // Copyright 2015 The Crashpad Authors. All rights reserved. 1 // Copyright 2015 The Crashpad Authors. All rights reserved.
2 // 2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License. 4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at 5 // You may obtain a copy of the License at
6 // 6 //
7 // http://www.apache.org/licenses/LICENSE-2.0 7 // http://www.apache.org/licenses/LICENSE-2.0
8 // 8 //
9 // Unless required by applicable law or agreed to in writing, software 9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, 10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and 12 // See the License for the specific language governing permissions and
13 // limitations under the License. 13 // limitations under the License.
14 14
15 #include "client/crashpad_client.h" 15 #include "client/crashpad_client.h"
16 16
17 #include <string.h> 17 #include <string.h>
18 #include <windows.h> 18 #include <windows.h>
19 19
20 #include "base/atomicops.h" 20 #include "base/atomicops.h"
21 #include "base/logging.h" 21 #include "base/logging.h"
22 #include "base/strings/string16.h" 22 #include "base/strings/string16.h"
23 #include "base/strings/utf_string_conversions.h" 23 #include "base/strings/utf_string_conversions.h"
24 #include "util/file/file_io.h" 24 #include "util/file/file_io.h"
25 #include "util/win/registration_protocol_win.h" 25 #include "util/win/registration_protocol_win.h"
26 #include "util/win/scoped_handle.h" 26 #include "util/win/scoped_handle.h"
27 27
28 namespace { 28 namespace {
29 29
30 // This handle is never closed. 30 // These handles are never closed.
31 HANDLE g_signal_exception = INVALID_HANDLE_VALUE; 31 HANDLE g_signal_exception = INVALID_HANDLE_VALUE;
32 HANDLE g_dump_completed = INVALID_HANDLE_VALUE;
32 33
33 // Where we store the exception information that the crash handler reads. 34 // Where we store the exception information that the crash handler reads.
34 crashpad::ExceptionInformation g_exception_information; 35 crashpad::ExceptionInformation g_exception_information;
35 36
37 // Tracks whether a thread has already entered UnhandledExceptionHandler() or
38 // DumpWithoutCrash().
39 base::subtle::AtomicWord g_capturing_dump;
40
36 LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) { 41 LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
37 // Tracks whether a thread has already entered UnhandledExceptionHandler.
38 static base::subtle::AtomicWord have_crashed;
39
40 // This is a per-process handler. While this handler is being invoked, other 42 // This is a per-process handler. While this handler is being invoked, other
41 // threads are still executing as usual, so multiple threads could enter at 43 // threads are still executing as usual, so multiple threads could enter at
42 // the same time. Because we're in a crashing state, we shouldn't be doing 44 // the same time. Because we're in a crashing state, we shouldn't be doing
43 // anything that might cause allocations, call into kernel mode, etc. So, we 45 // anything that might cause allocations, call into kernel mode, etc. So, we
44 // don't want to take a critical section here to avoid simultaneous access to 46 // don't want to take a critical section here to avoid simultaneous access to
45 // the global exception pointers in ExceptionInformation. Because the crash 47 // the global exception pointers in ExceptionInformation. Because the crash
46 // handler will record all threads, it's fine to simply have the second and 48 // handler will record all threads, it's fine to simply have the second and
47 // subsequent entrants block here. They will soon be suspended by the crash 49 // subsequent entrants block here. They will soon be suspended by the crash
48 // handler, and then the entire process will be terminated below. This means 50 // handler, and then the entire process will be terminated below. This means
49 // that we won't save the exception pointers from the second and further 51 // that we won't save the exception pointers from the second and further
50 // crashes, but contention here is very unlikely, and we'll still have a stack 52 // crashes, but contention here is very unlikely, and we'll still have a stack
51 // that's blocked at this location. 53 // that's blocked at this location.
52 if (base::subtle::Barrier_AtomicIncrement(&have_crashed, 1) > 1) { 54 if (base::subtle::Barrier_AtomicIncrement(&g_capturing_dump, 1) > 1) {
Mark Mentovai 2015/09/23 20:00:52 Maybe it’d be nice to not have an in-process non-c
scottmg 2015/09/23 22:03:40 Yeah, that would be better. Ideally it might be ju
scottmg 2015/09/24 19:16:52 Restructured.
53 SleepEx(INFINITE, false); 55 SleepEx(INFINITE, false);
Mark Mentovai 2015/09/23 20:00:52 But this line is a bigger problem. g_capturing_dum
scottmg 2015/09/24 19:16:52 This problem is gone now, only the crashing path u
54 } 56 }
55 57
56 // Otherwise, we're the first thread, so record the exception pointer and 58 // Otherwise, we're the first thread, so record the exception pointer and
57 // signal the crash handler. 59 // signal the crash handler.
58 g_exception_information.thread_id = GetCurrentThreadId(); 60 g_exception_information.thread_id = GetCurrentThreadId();
59 g_exception_information.exception_pointers = 61 g_exception_information.exception_pointers =
60 reinterpret_cast<crashpad::WinVMAddress>(exception_pointers); 62 reinterpret_cast<crashpad::WinVMAddress>(exception_pointers);
61 63
62 // Now signal the crash server, which will take a dump and then terminate us 64 // Now signal the crash server, which will take a dump and then signal back on
63 // when it's complete. 65 // the other event when it's done.
64 SetEvent(g_signal_exception); 66 SetEvent(g_signal_exception);
65 67
66 // Time to wait for the handler to create a dump. 68 // Time to wait for the handler to create a dump.
67 const DWORD kMillisecondsUntilTerminate = 60 * 1000; 69 const DWORD kMillisecondsUntilTerminate = 60 * 1000;
68 70
69 // Sleep for a while to allow it to process us. Eventually, we terminate 71 // Once the server has told us it's finished, terminate. If
70 // ourselves in case the crash server is gone, so that we don't leave zombies 72 // WaitForSingleObject() times out or fails in some other way, our only
71 // around. This would ideally never happen. 73 // recourse is still to kill ourselves, but we use a different exit code than
72 // TODO(scottmg): Re-add the "reply" event here, for implementing 74 // if we terminated via a normal path.
73 // DumpWithoutCrashing. 75 DWORD result =
74 Sleep(kMillisecondsUntilTerminate); 76 WaitForSingleObject(g_dump_completed, kMillisecondsUntilTerminate);
75 77
76 LOG(ERROR) << "crash server did not respond, self-terminating"; 78 const unsigned int kCrashExitCodeNoDump = 0xffff7001;
77 79 TerminateProcess(GetCurrentProcess(),
78 const UINT kCrashExitCodeNoDump = 0xffff7001; 80 result == WAIT_OBJECT_0
79 TerminateProcess(GetCurrentProcess(), kCrashExitCodeNoDump); 81 ? exception_pointers->ExceptionRecord->ExceptionCode
82 : kCrashExitCodeNoDump);
80 83
81 return EXCEPTION_CONTINUE_SEARCH; 84 return EXCEPTION_CONTINUE_SEARCH;
82 } 85 }
83 86
84 } // namespace 87 } // namespace
85 88
86 namespace crashpad { 89 namespace crashpad {
87 90
88 CrashpadClient::CrashpadClient() { 91 CrashpadClient::CrashpadClient() {
89 } 92 }
(...skipping 23 matching lines...) Expand all
113 ServerToClientMessage response = {0}; 116 ServerToClientMessage response = {0};
114 117
115 if (!SendToCrashHandlerServer( 118 if (!SendToCrashHandlerServer(
116 base::UTF8ToUTF16(ipc_port), message, &response)) { 119 base::UTF8ToUTF16(ipc_port), message, &response)) {
117 return false; 120 return false;
118 } 121 }
119 122
120 // The server returns these already duplicated to be valid in this process. 123 // The server returns these already duplicated to be valid in this process.
121 g_signal_exception = reinterpret_cast<HANDLE>( 124 g_signal_exception = reinterpret_cast<HANDLE>(
122 static_cast<uintptr_t>(response.registration.request_report_event)); 125 static_cast<uintptr_t>(response.registration.request_report_event));
126 g_dump_completed = reinterpret_cast<HANDLE>(
127 static_cast<uintptr_t>(response.registration.report_completed_event));
123 return true; 128 return true;
124 } 129 }
125 130
126 bool CrashpadClient::UseHandler() { 131 bool CrashpadClient::UseHandler() {
127 if (g_signal_exception == INVALID_HANDLE_VALUE) 132 if (g_signal_exception == INVALID_HANDLE_VALUE ||
133 g_dump_completed == INVALID_HANDLE_VALUE) {
128 return false; 134 return false;
135 }
129 // In theory we could store the previous handler but it is not clear what 136 // In theory we could store the previous handler but it is not clear what
130 // use we have for it. 137 // use we have for it.
131 SetUnhandledExceptionFilter(&UnhandledExceptionHandler); 138 SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
132 return true; 139 return true;
133 } 140 }
134 141
142 // static
143 bool CrashpadClient::DumpWithoutCrash() {
144 if (g_signal_exception == INVALID_HANDLE_VALUE ||
145 g_dump_completed == INVALID_HANDLE_VALUE) {
146 LOG(ERROR) << "not registered with a server";
147 return false;
148 }
149
150 if (base::subtle::Barrier_AtomicIncrement(&g_capturing_dump, 1) > 1) {
151 // Someone else is either writing or has crashed, simply abort here.
Mark Mentovai 2015/09/23 20:00:52 Decrement to undo what you just did, since there’s
152 LOG(WARNING) << "already writing";
153 return false;
154 }
155
156 // Create a fake EXCEPTION_POINTERS (with a null EXCEPTION_RECORD), so that
Mark Mentovai 2015/09/23 20:00:52 Expand on the thing about the “null EXCEPTION_RECO
scottmg 2015/09/24 19:16:52 Done.
157 // the handler has a context to start from.
158 CONTEXT context;
159 // TODO(scottmg): Replace with our custom version. See:
160 // https://code.google.com/p/crashpad/issues/detail?id=53.
161 RtlCaptureContext(&context);
Mark Mentovai 2015/09/23 20:00:52 We’ll actually want to capture the context from th
scottmg 2015/09/24 19:16:52 I guess as we discovered, this happens to be what
Mark Mentovai 2015/09/24 19:26:05 Yeah, I already have an x86 CaptureContext for Win
scottmg 2015/09/24 20:15:22 OK, I can just land it slightly borked for now.
162 EXCEPTION_POINTERS exception_pointers = {0};
Mark Mentovai 2015/09/23 22:20:38 P.S. On Mac, we have our own made-up exception ID
scottmg 2015/09/24 19:16:52 My thinking was that we don't really want it to ha
Mark Mentovai 2015/09/24 19:26:05 Do we at least wind up with a minidump with a MINI
scottmg 2015/09/24 20:15:22 Oh, you're right. I had only run the (in-memory) t
163 exception_pointers.ContextRecord = &context;
164
165 g_exception_information.thread_id = GetCurrentThreadId();
Mark Mentovai 2015/09/23 20:00:52 This through WaitForSingleObject() looks duplicate
166 g_exception_information.exception_pointers =
167 reinterpret_cast<crashpad::WinVMAddress>(&exception_pointers);
168
169 // Now signal the crash server, which will take a dump and then signal back on
170 // the other event when it's done.
171 SetEvent(g_signal_exception);
172
173 // Wait until the server tells us it's finished.
174 DWORD result = WaitForSingleObject(g_dump_completed, INFINITE);
175 PLOG_IF(ERROR, result != WAIT_OBJECT_0) << "WaitForSingleObject";
176
177 base::subtle::Barrier_AtomicIncrement(&g_capturing_dump, -1);
178 return true;
179 }
180
181
Mark Mentovai 2015/09/23 20:00:52 Extra blank line.
scottmg 2015/09/24 19:16:52 Done.
135 } // namespace crashpad 182 } // namespace crashpad
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698