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

Side by Side Diff: third_party/crashpad/crashpad/tools/mac/catch_exception_tool.cc

Issue 1505213004: Copy Crashpad into the Chrome tree instead of importing it via DEPS (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address review comments, update README.chromium Created 5 years 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
(Empty)
1 // Copyright 2014 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (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
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <getopt.h>
16 #include <libgen.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <unistd.h>
20
21 #include <algorithm>
22 #include <string>
23 #include <vector>
24
25 #include "base/files/scoped_file.h"
26 #include "base/logging.h"
27 #include "base/mac/mach_logging.h"
28 #include "tools/tool_support.h"
29 #include "util/mach/exc_server_variants.h"
30 #include "util/mach/exception_behaviors.h"
31 #include "util/mach/exception_types.h"
32 #include "util/mach/mach_extensions.h"
33 #include "util/mach/mach_message.h"
34 #include "util/mach/mach_message_server.h"
35 #include "util/mach/symbolic_constants_mach.h"
36 #include "util/posix/symbolic_constants_posix.h"
37 #include "util/stdlib/string_number_conversion.h"
38
39 namespace crashpad {
40 namespace {
41
42 struct Options {
43 std::string file_path;
44 std::string mach_service;
45 FILE* file;
46 int timeout_secs;
47 bool has_timeout;
48 MachMessageServer::Persistent persistent;
49 };
50
51 class ExceptionServer : public UniversalMachExcServer::Interface {
52 public:
53 ExceptionServer(const Options& options,
54 const std::string& me,
55 int* exceptions_handled)
56 : UniversalMachExcServer::Interface(),
57 options_(options),
58 me_(me),
59 exceptions_handled_(exceptions_handled) {}
60
61 // UniversalMachExcServer::Interface:
62 virtual kern_return_t CatchMachException(
63 exception_behavior_t behavior,
64 exception_handler_t exception_port,
65 thread_t thread,
66 task_t task,
67 exception_type_t exception,
68 const mach_exception_data_type_t* code,
69 mach_msg_type_number_t code_count,
70 thread_state_flavor_t* flavor,
71 ConstThreadState old_state,
72 mach_msg_type_number_t old_state_count,
73 thread_state_t new_state,
74 mach_msg_type_number_t* new_state_count,
75 const mach_msg_trailer_t* trailer,
76 bool* destroy_complex_request) override {
77 *destroy_complex_request = true;
78 ++*exceptions_handled_;
79
80 fprintf(options_.file,
81 "%s: behavior %s",
82 me_.c_str(),
83 ExceptionBehaviorToString(
84 behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str());
85
86 kern_return_t kr;
87 if (ExceptionBehaviorHasIdentity(behavior)) {
88 // It’s not possible to call pid_for_task() once EXC_CORPSE_NOTIFY has
89 // been generated. It is possible to obtain the process ID by mapping the
90 // corpse kcdata area from the task’s address space at code[0] (size
91 // code[1]) and locating TASK_CRASHINFO_PID within that area. This area
92 // also includes TASK_CRASHINFO_CRASHED_THREADID which could be used
93 // instead of thread_info() below, and TASK_CRASHINFO_EXCEPTION_CODES
94 // which could be used to recover the exception codes passed to the
95 // EXC_CRASH handler. None of this is currently done because corpses are a
96 // new 10.11-only feature. See 10.11 <corpses/task_corpse.h> and
97 // <kern/kern_cdata.h>.
98 if (exception != EXC_CORPSE_NOTIFY) {
99 pid_t pid;
100 kr = pid_for_task(task, &pid);
101 if (kr != KERN_SUCCESS) {
102 fprintf(options_.file, "\n");
103 fflush(options_.file);
104 MACH_LOG(ERROR, kr) << "pid_for_task";
105 return KERN_FAILURE;
106 }
107 fprintf(options_.file, ", pid %d", pid);
108 }
109
110 thread_identifier_info identifier_info;
111 mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
112 kr = thread_info(thread,
113 THREAD_IDENTIFIER_INFO,
114 reinterpret_cast<thread_info_t>(&identifier_info),
115 &count);
116 if (kr != KERN_SUCCESS) {
117 fprintf(options_.file, "\n");
118 fflush(options_.file);
119 MACH_LOG(ERROR, kr) << "thread_info";
120 return KERN_FAILURE;
121 }
122 fprintf(options_.file, ", thread %lld", identifier_info.thread_id);
123 }
124
125 fprintf(
126 options_.file,
127 ", exception %s, codes[%d]",
128 ExceptionToString(exception, kUseFullName | kUnknownIsNumeric).c_str(),
129 code_count);
130
131 for (size_t index = 0; index < code_count; ++index) {
132 fprintf(options_.file,
133 "%s %#llx",
134 index != 0 ? "," : "",
135 code[index]);
136 }
137
138 if (exception == EXC_CRASH) {
139 mach_exception_code_t original_code_0;
140 int signal;
141 exception_type_t original_exception =
142 ExcCrashRecoverOriginalException(code[0], &original_code_0, &signal);
143 fprintf(options_.file,
144 ", original exception %s, original code[0] %lld, signal %s",
145 ExceptionToString(original_exception,
146 kUseFullName | kUnknownIsNumeric).c_str(),
147 original_code_0,
148 SignalToString(signal, kUseFullName | kUnknownIsNumeric).c_str());
149 }
150
151 if (ExceptionBehaviorHasState(behavior)) {
152 std::string flavor_string =
153 ThreadStateFlavorToString(*flavor, kUseFullName | kUnknownIsNumeric);
154 fprintf(options_.file,
155 ", flavor %s, old_state_count %d",
156 flavor_string.c_str(),
157 old_state_count);
158 }
159
160 fprintf(options_.file, "\n");
161 fflush(options_.file);
162
163 if (exception != EXC_CRASH && exception != kMachExceptionSimulated) {
164 // Find another handler.
165 return KERN_FAILURE;
166 }
167
168 ExcServerCopyState(
169 behavior, old_state, old_state_count, new_state, new_state_count);
170
171 return ExcServerSuccessfulReturnValue(exception, behavior, false);
172 }
173
174 private:
175 const Options& options_;
176 const std::string& me_;
177 int* exceptions_handled_;
178 };
179
180 void Usage(const std::string& me) {
181 fprintf(stderr,
182 "Usage: %s -m SERVICE [OPTION]...\n"
183 "Catch Mach exceptions and display information about them.\n"
184 "\n"
185 " -f, --file=FILE append information to FILE instead of stdout\n"
186 " -m, --mach-service=SERVICE register SERVICE with the bootstrap server\n"
187 " -p, --persistent continue processing exceptions after the first\n"
188 " -t, --timeout=TIMEOUT run for a maximum of TIMEOUT seconds\n"
189 " --help display this help and exit\n"
190 " --version output version information and exit\n",
191 me.c_str());
192 ToolSupport::UsageTail(me);
193 }
194
195 int CatchExceptionToolMain(int argc, char* argv[]) {
196 const std::string me(basename(argv[0]));
197
198 enum OptionFlags {
199 // “Short” (single-character) options.
200 kOptionFile = 'f',
201 kOptionMachService = 'm',
202 kOptionPersistent = 'p',
203 kOptionTimeout = 't',
204
205 // Long options without short equivalents.
206 kOptionLastChar = 255,
207
208 // Standard options.
209 kOptionHelp = -2,
210 kOptionVersion = -3,
211 };
212
213 Options options = {};
214
215 const option long_options[] = {
216 {"file", required_argument, nullptr, kOptionFile},
217 {"mach-service", required_argument, nullptr, kOptionMachService},
218 {"persistent", no_argument, nullptr, kOptionPersistent},
219 {"timeout", required_argument, nullptr, kOptionTimeout},
220 {"help", no_argument, nullptr, kOptionHelp},
221 {"version", no_argument, nullptr, kOptionVersion},
222 {nullptr, 0, nullptr, 0},
223 };
224
225 int opt;
226 while ((opt = getopt_long(argc, argv, "f:m:pt:", long_options, nullptr)) !=
227 -1) {
228 switch (opt) {
229 case kOptionFile:
230 options.file_path = optarg;
231 break;
232 case kOptionMachService:
233 options.mach_service = optarg;
234 break;
235 case kOptionPersistent:
236 options.persistent = MachMessageServer::kPersistent;
237 break;
238 case kOptionTimeout:
239 if (!StringToNumber(optarg, &options.timeout_secs) ||
240 options.timeout_secs < 0) {
241 ToolSupport::UsageHint(me, "-t requires a zero or positive TIMEOUT");
242 return EXIT_FAILURE;
243 }
244 options.has_timeout = true;
245 break;
246 case kOptionHelp:
247 Usage(me);
248 return EXIT_SUCCESS;
249 case kOptionVersion:
250 ToolSupport::Version(me);
251 return EXIT_SUCCESS;
252 default:
253 ToolSupport::UsageHint(me, nullptr);
254 return EXIT_FAILURE;
255 }
256 }
257 argc -= optind;
258 argv += optind;
259
260 if (options.mach_service.empty()) {
261 ToolSupport::UsageHint(me, "-m is required");
262 return EXIT_FAILURE;
263 }
264
265 base::mac::ScopedMachReceiveRight
266 service_port(BootstrapCheckIn(options.mach_service));
267 if (service_port == kMachPortNull) {
268 return EXIT_FAILURE;
269 }
270
271 base::ScopedFILE file_owner;
272 if (options.file_path.empty()) {
273 options.file = stdout;
274 } else {
275 file_owner.reset(fopen(options.file_path.c_str(), "a"));
276 if (!file_owner.get()) {
277 PLOG(ERROR) << "fopen " << options.file_path;
278 return EXIT_FAILURE;
279 }
280 options.file = file_owner.get();
281 }
282
283 int exceptions_handled = 0;
284 ExceptionServer exception_server(options, me, &exceptions_handled);
285 UniversalMachExcServer universal_mach_exc_server(&exception_server);
286
287 // Assume that if persistent mode has been requested, it’s desirable to ignore
288 // large messages and keep running.
289 MachMessageServer::ReceiveLarge receive_large =
290 (options.persistent == MachMessageServer::kPersistent)
291 ? MachMessageServer::kReceiveLargeIgnore
292 : MachMessageServer::kReceiveLargeError;
293
294 mach_msg_timeout_t timeout_ms;
295 if (!options.has_timeout) {
296 timeout_ms = kMachMessageTimeoutWaitIndefinitely;
297 } else if (options.timeout_secs == 0) {
298 timeout_ms = kMachMessageTimeoutNonblocking;
299 } else {
300 timeout_ms = options.timeout_secs * 1000;
301 }
302
303 mach_msg_return_t mr = MachMessageServer::Run(&universal_mach_exc_server,
304 service_port.get(),
305 MACH_MSG_OPTION_NONE,
306 options.persistent,
307 receive_large,
308 timeout_ms);
309 if (mr == MACH_RCV_TIMED_OUT && options.has_timeout && options.persistent &&
310 exceptions_handled) {
311 // This is not an error: when exiting on timeout during persistent
312 // processing and at least one exception was handled, it’s considered a
313 // success.
314 } else if (mr != MACH_MSG_SUCCESS) {
315 MACH_LOG(ERROR, mr) << "MachMessageServer::Run";
316 return EXIT_FAILURE;
317 }
318
319 return EXIT_SUCCESS;
320 }
321
322 } // namespace
323 } // namespace crashpad
324
325 int main(int argc, char* argv[]) {
326 return crashpad::CatchExceptionToolMain(argc, argv);
327 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698