OLD | NEW |
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 <windows.h> | 18 #include <windows.h> |
18 | 19 |
19 #include "base/atomicops.h" | 20 #include "base/atomicops.h" |
20 #include "base/logging.h" | 21 #include "base/logging.h" |
21 #include "base/strings/string16.h" | 22 #include "base/strings/string16.h" |
22 #include "base/strings/utf_string_conversions.h" | 23 #include "base/strings/utf_string_conversions.h" |
23 #include "client/crashpad_info.h" | |
24 #include "client/registration_protocol_win.h" | |
25 #include "util/file/file_io.h" | 24 #include "util/file/file_io.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 // Time to wait for the handler to create a dump. This is tricky to figure out. | |
30 const DWORD kMillisecondsUntilTerminate = 5000; | |
31 | 29 |
32 // This is the exit code that the process will return to the system once the | 30 // This handle is never closed. |
33 // crash has been handled by Crashpad. We don't want to clash with the | 31 HANDLE g_signal_exception = INVALID_HANDLE_VALUE; |
34 // application-defined exit codes but we don't know them so we use one that is | |
35 // unlikely to be used. | |
36 const UINT kCrashExitCode = 0xffff7001; | |
37 | 32 |
38 // These two handles to events are leaked. | 33 // Where we store the exception information that the crash handler reads. |
39 HANDLE g_signal_exception = nullptr; | 34 crashpad::ExceptionInformation g_exception_information; |
40 HANDLE g_wait_termination = nullptr; | |
41 | |
42 // Tracks whether a thread has already entered UnhandledExceptionHandler. | |
43 base::subtle::AtomicWord g_have_crashed; | |
44 | 35 |
45 LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) { | 36 LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) { |
| 37 // Tracks whether a thread has already entered UnhandledExceptionHandler. |
| 38 static base::subtle::AtomicWord have_crashed; |
| 39 |
46 // This is a per-process handler. While this handler is being invoked, other | 40 // This is a per-process handler. While this handler is being invoked, other |
47 // threads are still executing as usual, so multiple threads could enter at | 41 // threads are still executing as usual, so multiple threads could enter at |
48 // the same time. Because we're in a crashing state, we shouldn't be doing | 42 // the same time. Because we're in a crashing state, we shouldn't be doing |
49 // anything that might cause allocations, call into kernel mode, etc. So, we | 43 // anything that might cause allocations, call into kernel mode, etc. So, we |
50 // don't want to take a critical section here to avoid simultaneous access to | 44 // don't want to take a critical section here to avoid simultaneous access to |
51 // the global exception pointers in CrashpadInfo. Because the crash handler | 45 // the global exception pointers in ExceptionInformation. Because the crash |
52 // will record all threads, it's fine to simply have the second and subsequent | 46 // handler will record all threads, it's fine to simply have the second and |
53 // entrants block here. They will soon be suspended by the crash handler, and | 47 // subsequent entrants block here. They will soon be suspended by the crash |
54 // then the entire process will be terminated below. This means that we won't | 48 // handler, and then the entire process will be terminated below. This means |
55 // save the exception pointers from the second and further crashes, but | 49 // that we won't save the exception pointers from the second and further |
56 // contention here is very unlikely, and we'll still have a stack that's | 50 // crashes, but contention here is very unlikely, and we'll still have a stack |
57 // blocked at this location. | 51 // that's blocked at this location. |
58 if (base::subtle::Barrier_AtomicIncrement(&g_have_crashed, 1) > 1) { | 52 if (base::subtle::Barrier_AtomicIncrement(&have_crashed, 1) > 1) { |
59 SleepEx(INFINITE, false); | 53 SleepEx(INFINITE, false); |
60 } | 54 } |
61 | 55 |
62 // Otherwise, we're the first thread, so record the exception pointer and | 56 // Otherwise, we're the first thread, so record the exception pointer and |
63 // signal the crash handler. | 57 // signal the crash handler. |
64 crashpad::CrashpadInfo::GetCrashpadInfo()->set_thread_id( | 58 g_exception_information.thread_id = GetCurrentThreadId(); |
65 GetCurrentThreadId()); | 59 g_exception_information.exception_pointers = |
66 crashpad::CrashpadInfo::GetCrashpadInfo()->set_exception_pointers( | 60 reinterpret_cast<crashpad::WinVMAddress>(exception_pointers); |
67 exception_pointers); | |
68 DWORD rv = SignalObjectAndWait(g_signal_exception, | |
69 g_wait_termination, | |
70 kMillisecondsUntilTerminate, | |
71 false); | |
72 if (rv != WAIT_OBJECT_0) { | |
73 // Something went wrong. It is likely that a dump has not been created. | |
74 if (rv == WAIT_TIMEOUT) { | |
75 LOG(WARNING) << "SignalObjectAndWait timed out"; | |
76 } else { | |
77 PLOG(WARNING) << "SignalObjectAndWait error"; | |
78 } | |
79 } | |
80 // We don't want to generate more exceptions, so we take the fast route. | |
81 TerminateProcess(GetCurrentProcess(), kCrashExitCode); | |
82 return 0L; | |
83 } | |
84 | 61 |
85 // Returns a pipe handle connected to the RegistrationServer. | 62 // Now signal the crash server, which will take a dump and then terminate us |
86 crashpad::ScopedFileHANDLE Connect(const base::string16& pipe_name) { | 63 // when it's complete. |
87 crashpad::ScopedFileHANDLE pipe; | 64 SetEvent(g_signal_exception); |
88 const int kMaxTries = 5; | |
89 for (int tries = 0; tries < kMaxTries; ++tries) { | |
90 pipe.reset(CreateFile(pipe_name.c_str(), | |
91 GENERIC_READ | GENERIC_WRITE, | |
92 0, | |
93 nullptr, | |
94 OPEN_EXISTING, | |
95 SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS, | |
96 nullptr)); | |
97 if (pipe.is_valid()) | |
98 break; | |
99 | 65 |
100 // If busy, wait 60s before retrying. | 66 // Time to wait for the handler to create a dump. |
101 if (GetLastError() != ERROR_PIPE_BUSY) { | 67 const DWORD kMillisecondsUntilTerminate = 60 * 1000; |
102 PLOG(ERROR) << "CreateFile pipe connection"; | |
103 return crashpad::ScopedFileHANDLE(); | |
104 } else if (!WaitNamedPipe(pipe_name.c_str(), 60000)) { | |
105 PLOG(ERROR) << "WaitNamedPipe"; | |
106 } | |
107 } | |
108 | 68 |
109 if (!pipe.is_valid()) | 69 // Sleep for a while to allow it to process us. Eventually, we terminate |
110 return crashpad::ScopedFileHANDLE(); | 70 // ourselves in case the crash server is gone, so that we don't leave zombies |
| 71 // around. This would ideally never happen. |
| 72 // TODO(scottmg): Re-add the "reply" event here, for implementing |
| 73 // DumpWithoutCrashing. |
| 74 Sleep(kMillisecondsUntilTerminate); |
111 | 75 |
112 DWORD mode = PIPE_READMODE_MESSAGE; | 76 LOG(ERROR) << "crash server did not respond, self-terminating"; |
113 if (!SetNamedPipeHandleState(pipe.get(), | |
114 &mode, | |
115 nullptr, // Don't set maximum bytes. | |
116 nullptr)) { // Don't set maximum time. | |
117 PLOG(ERROR) << "SetNamedPipeHandleState"; | |
118 return crashpad::ScopedFileHANDLE(); | |
119 } | |
120 | 77 |
121 return pipe.Pass(); | 78 const UINT kCrashExitCodeNoDump = 0xffff7001; |
| 79 TerminateProcess(GetCurrentProcess(), kCrashExitCodeNoDump); |
| 80 |
| 81 return EXCEPTION_CONTINUE_SEARCH; |
122 } | 82 } |
123 | 83 |
124 } // namespace | 84 } // namespace |
125 | 85 |
126 namespace crashpad { | 86 namespace crashpad { |
127 | 87 |
128 CrashpadClient::CrashpadClient() { | 88 CrashpadClient::CrashpadClient() { |
129 } | 89 } |
130 | 90 |
131 CrashpadClient::~CrashpadClient() { | 91 CrashpadClient::~CrashpadClient() { |
132 } | 92 } |
133 | 93 |
134 bool CrashpadClient::StartHandler( | 94 bool CrashpadClient::StartHandler( |
135 const base::FilePath& handler, | 95 const base::FilePath& handler, |
136 const base::FilePath& database, | 96 const base::FilePath& database, |
137 const std::string& url, | 97 const std::string& url, |
138 const std::map<std::string, std::string>& annotations, | 98 const std::map<std::string, std::string>& annotations, |
139 const std::vector<std::string>& arguments) { | 99 const std::vector<std::string>& arguments) { |
| 100 LOG(FATAL) << "SetHandler should be used on Windows"; |
140 return false; | 101 return false; |
141 } | 102 } |
142 | 103 |
143 bool CrashpadClient::SetHandler(const std::string& ipc_port) { | 104 bool CrashpadClient::SetHandler(const std::string& ipc_port) { |
144 RegistrationRequest request = {0}; | 105 ClientToServerMessage message; |
145 request.client_process_id = GetCurrentProcessId(); | 106 memset(&message, 0, sizeof(message)); |
146 request.crashpad_info_address = | 107 message.type = ClientToServerMessage::kRegister; |
147 reinterpret_cast<WinVMAddress>(CrashpadInfo::GetCrashpadInfo()); | 108 message.registration.client_process_id = GetCurrentProcessId(); |
| 109 message.registration.exception_information = |
| 110 reinterpret_cast<WinVMAddress>(&g_exception_information); |
148 | 111 |
149 RegistrationResponse response = {0}; | 112 ServerToClientMessage response = {0}; |
150 | 113 |
151 ScopedFileHANDLE pipe = Connect(base::UTF8ToUTF16(ipc_port)); | 114 if (!SendToCrashHandlerServer( |
152 if (!pipe.is_valid()) | 115 base::UTF8ToUTF16(ipc_port), message, &response)) { |
153 return false; | 116 return false; |
154 bool result = LoggingWriteFile(pipe.get(), &request, sizeof(request)) && | 117 } |
155 LoggingReadFile(pipe.get(), &response, sizeof(response)); | |
156 if (!result) | |
157 return result; | |
158 | 118 |
159 // The server returns these already duplicated to be valid in this process. | 119 // The server returns these already duplicated to be valid in this process. |
160 g_signal_exception = reinterpret_cast<HANDLE>(response.request_report_event); | 120 g_signal_exception = |
161 g_wait_termination = | 121 reinterpret_cast<HANDLE>(response.registration.request_report_event); |
162 reinterpret_cast<HANDLE>(response.report_complete_event); | |
163 return true; | 122 return true; |
164 } | 123 } |
165 | 124 |
166 bool CrashpadClient::UseHandler() { | 125 bool CrashpadClient::UseHandler() { |
167 if (!g_signal_exception) | 126 if (g_signal_exception == INVALID_HANDLE_VALUE) |
168 return false; | |
169 if (!g_wait_termination) | |
170 return false; | 127 return false; |
171 // In theory we could store the previous handler but it is not clear what | 128 // In theory we could store the previous handler but it is not clear what |
172 // use we have for it. | 129 // use we have for it. |
173 SetUnhandledExceptionFilter(&UnhandledExceptionHandler); | 130 SetUnhandledExceptionFilter(&UnhandledExceptionHandler); |
174 return true; | 131 return true; |
175 } | 132 } |
176 | 133 |
177 } // namespace crashpad | 134 } // namespace crashpad |
OLD | NEW |