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

Side by Side Diff: tools/exception_port_tool.cc

Issue 575033004: Add exception_port_tool (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Created 6 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
« no previous file with comments | « no previous file | tools/tools.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 <errno.h>
16 #include <getopt.h>
17 #include <libgen.h>
18 #include <mach/mach.h>
19 #include <servers/bootstrap.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24
25 #include <string>
26 #include <vector>
27
28 #include "base/basictypes.h"
29 #include "base/mac/mach_logging.h"
30 #include "base/mac/scoped_mach_port.h"
31 #include "base/strings/stringprintf.h"
32 #include "tools/tool_support.h"
33 #include "util/mach/exception_ports.h"
34 #include "util/mach/mach_extensions.h"
35 #include "util/mach/symbolic_constants_mach.h"
36 #include "util/stdlib/string_number_conversion.h"
37
38 namespace {
39
40 using namespace crashpad;
41
42 struct ExceptionHandlerDescription {
43 ExceptionPorts::TargetType target_type;
44 exception_mask_t mask;
45 exception_behavior_t behavior;
46 thread_state_flavor_t flavor;
47 std::string handler;
48 };
49
50 const char kHandlerNull[] = "NULL";
51 const char kHandlerBootstrapColon[] = "bootstrap:";
52
53 // Populates |description| based on a textual representation in
54 // |handler_string_ro|, returning true on success and false on failure (parse
55 // error). The --help string describes the format of |handler_string_ro|.
56 // Briefly, it is a comma-separated string that allows the members of
57 // |description| to be specified as "field=value". Values for "target" can be
58 // "host", "task", or "thread"; values for "handler" are of the form
59 // "bootstrap:service_name" where service_name will be looked up with the
60 // bootstrap server; and values for the other fields are interpreted by
61 // SymbolicConstantsMach.
62 bool ParseHandlerString(const char* handler_string_ro,
63 ExceptionHandlerDescription* description) {
64 const char kTargetEquals[] = "target=";
65 const char kMaskEquals[] = "mask=";
66 const char kBehaviorEquals[] = "behavior=";
67 const char kFlavorEquals[] = "flavor=";
68 const char kHandlerEquals[] = "handler=";
69
70 std::string handler_string(handler_string_ro);
71 char* handler_string_c = &handler_string[0];
72
73 char* token;
74 while ((token = strsep(&handler_string_c, ",")) != NULL) {
75 if (strncmp(token, kTargetEquals, strlen(kTargetEquals)) == 0) {
76 const char* value = token + strlen(kTargetEquals);
77 if (strcmp(value, "host") == 0) {
78 description->target_type = ExceptionPorts::kTargetTypeHost;
79 } else if (strcmp(value, "task") == 0) {
80 description->target_type = ExceptionPorts::kTargetTypeTask;
81 } else if (strcmp(value, "thread") == 0) {
82 description->target_type = ExceptionPorts::kTargetTypeThread;
83 }
Robert Sesek 2014/09/18 14:43:48 else { return false; } ?
84 } else if (strncmp(token, kMaskEquals, strlen(kMaskEquals)) == 0) {
85 const char* value = token + strlen(kMaskEquals);
86 if (!StringToExceptionMask(
87 value,
88 kAllowFullName | kAllowShortName | kAllowNumber | kAllowOr,
89 &description->mask)) {
90 return false;
91 }
92 } else if (strncmp(token, kBehaviorEquals, strlen(kBehaviorEquals)) == 0) {
93 const char* value = token + strlen(kBehaviorEquals);
94 if (!StringToExceptionBehavior(
95 value,
96 kAllowFullName | kAllowShortName | kAllowNumber,
97 &description->behavior)) {
98 return false;
99 }
100 } else if (strncmp(token, kFlavorEquals, strlen(kFlavorEquals)) == 0) {
101 const char* value = token + strlen(kFlavorEquals);
102 if (!StringToThreadStateFlavor(
103 value,
104 kAllowFullName | kAllowShortName | kAllowNumber,
105 &description->flavor)) {
106 return false;
107 }
108 } else if (strncmp(token, kHandlerEquals, strlen(kHandlerEquals)) == 0) {
109 const char* value = token + strlen(kHandlerEquals);
110 if (strcmp(value, kHandlerNull) != 0 &&
111 strncmp(value,
112 kHandlerBootstrapColon,
113 strlen(kHandlerBootstrapColon)) != 0) {
114 return false;
115 }
116 description->handler = std::string(value);
117 } else {
118 return false;
119 }
120 }
121
122 return true;
123 }
124
125 // ShowExceptionPorts() shows handlers as numeric mach_port_t values, which are
126 // opaque and meaningless on their own. ShowBootstrapService() can be used to
127 // look up a service with the bootstrap server by name and show its mach_port_t
128 // value, which can then be associated with handlers shown by
129 // ShowExceptionPorts().
130 void ShowBootstrapService(const std::string& service_name) {
131 mach_port_t service_port;
Robert Sesek 2014/09/18 14:43:47 This send right is leaked.
132 kern_return_t kr = bootstrap_look_up(
133 bootstrap_port, const_cast<char*>(service_name.c_str()), &service_port);
134 if (kr != BOOTSTRAP_SUCCESS) {
135 BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up " << service_name;
136 return;
137 }
138
139 printf("service %s %#x\n", service_name.c_str(), service_port);
140 }
141
142 // Prints information about all exception ports known for |exception_ports|. If
143 // |numeric| is true, all information is printed in numeric form, otherwise, it
144 // will be converted to symbolic constants where possible by
145 // SymbolicConstantsMach. If |is_new| is true, information will be presented as
146 // “new exception ports”, indicating that they show the state of the exception
147 // ports after SetExceptionPort() has been called.
148 void ShowExceptionPorts(const ExceptionPorts& exception_ports,
149 bool numeric,
150 bool is_new) {
151 const char* target_name = exception_ports.TargetTypeName();
152
153 std::vector<ExceptionPorts::ExceptionHandler> handlers;
154 if (!exception_ports.GetExceptionPorts(ExcMaskAll() | EXC_MASK_CRASH,
155 &handlers)) {
156 return;
157 }
158
159 const char* age_name = is_new ? "new " : "";
160
161 if (handlers.size() == 0) {
162 printf("no %s%s exception ports\n", age_name, target_name);
163 }
164
165 for (size_t port_index = 0; port_index < handlers.size(); ++port_index) {
166 if (numeric) {
167 printf(
168 "%s%s exception port %zu, mask %#x, port %#x, "
169 "behavior %#x, flavor %u\n",
170 age_name,
171 target_name,
172 port_index,
173 handlers[port_index].mask,
174 handlers[port_index].port,
175 handlers[port_index].behavior,
176 handlers[port_index].flavor);
177 } else {
178 std::string mask_string = ExceptionMaskToString(
179 handlers[port_index].mask, kUseShortName | kUnknownIsEmpty | kUseOr);
180 if (mask_string.empty()) {
181 mask_string.assign("?");
182 }
183
184 std::string behavior_string = ExceptionBehaviorToString(
185 handlers[port_index].behavior, kUseShortName | kUnknownIsEmpty);
186 if (behavior_string.empty()) {
187 behavior_string.assign("?");
188 }
189
190 std::string flavor_string = ThreadStateFlavorToString(
191 handlers[port_index].flavor, kUseShortName | kUnknownIsEmpty);
192 if (flavor_string.empty()) {
193 flavor_string.assign("?");
194 }
195
196 printf(
197 "%s%s exception port %zu, mask %#x (%s), port %#x, "
198 "behavior %#x (%s), flavor %u (%s)\n",
199 age_name,
200 target_name,
201 port_index,
202 handlers[port_index].mask,
203 mask_string.c_str(),
204 handlers[port_index].port,
205 handlers[port_index].behavior,
206 behavior_string.c_str(),
207 handlers[port_index].flavor,
208 flavor_string.c_str());
209 }
210 }
211 }
212
213 // Sets the exception port for |target_port|, a send right to a thread, task, or
214 // host port, to |description|, which identifies what type of port |target_port|
215 // is and describes an exception port to be set. Returns true on success.
216 //
217 // This function may be called more than once if setting different handlers is
218 // desired.
219 bool SetExceptionPort(const ExceptionHandlerDescription* description,
220 mach_port_t target_port) {
221 mach_port_t service_port = MACH_PORT_NULL;
Robert Sesek 2014/09/18 14:43:48 Also leaked.
222 kern_return_t kr;
223 if (description->handler.compare(
224 0, strlen(kHandlerBootstrapColon), kHandlerBootstrapColon) == 0) {
225 const char* service_name =
226 description->handler.c_str() + strlen(kHandlerBootstrapColon);
227 kr = bootstrap_look_up(
228 bootstrap_port, const_cast<char*>(service_name), &service_port);
229 if (kr != BOOTSTRAP_SUCCESS) {
230 BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up " << service_name;
231 return false;
232 }
233 } else if (description->handler != kHandlerNull) {
234 return false;
235 }
236
237 ExceptionPorts exception_ports(description->target_type, target_port);
238 if (!exception_ports.SetExceptionPort(description->mask,
239 service_port,
240 description->behavior,
241 description->flavor)) {
242 return false;
243 }
244
245 return true;
246 }
247
248 void Usage(const std::string& me) {
249 fprintf(stderr,
250 "Usage: %s [OPTION]... [COMMAND [ARG]...]\n"
251 "View and change Mach exception ports, and run COMMAND if supplied.\n"
252 "\n"
253 " -s, --set_handler=DESCRIPTION set an exception port to DESCRIPTION, see belo w\n"
254 " --show_bootstrap=SERVICE look up and display a service registered with\ n"
255 " the bootstrap server\n"
256 " -p, --pid=PID operate on PID instead of the current task\n"
Robert Sesek 2014/09/18 14:43:47 "(requires root)" ?
257 " -h, --show_host display original host exception ports\n"
258 " -t, --show_task display original task exception ports\n"
259 " --show_thread display original thread exception ports\n"
260 " -H, --show_new_host display modified host exception ports\n"
261 " -T, --show_new_task display modified task exception ports\n"
262 " --show_new_thread display modified thread exception ports\n"
263 " -n, --numeric display values numerically, not symbolically\n "
264 " --help display this help and exit\n"
265 " --version output version information and exit\n"
266 "\n"
267 "Any operations on host exception ports require superuser permissions.\n"
268 "\n"
269 "DESCRIPTION is formatted as a comma-separated sequence of tokens, where each\n"
270 "token consists of a key and value separated by an equals sign. Available keys:\ n"
271 " target which target's exception ports to set: host, task, or target\n"
272 " mask the mask of exception types to handle: CRASH, ALL, or others\n"
273 " behavior the specific exception handler routine to call: DEFAULT, STATE,\n"
274 " or STATE_IDENTITY, possibly with MACH_EXCEPTION_CODES.\n"
275 " flavor the thread state flavor passed to the handler: architecture-specifi c\n"
276 " handler the exception handler: NULL or bootstrap:SERVICE, indicating that\n "
277 " the handler should be looked up with the bootstrap server\n"
278 "The default DESCRIPTION is\n"
279 " target=task,mask=CRASH,behavior=DEFAULT|MACH,flavor=NONE,handler=NULL\n",
280 me.c_str());
281 ToolSupport::UsageTail(me);
282 }
283
284 } // namespace
285
286 int main(int argc, char* argv[]) {
287 const std::string me(basename(argv[0]));
288
289 enum ExitCode {
290 kExitSuccess = EXIT_SUCCESS,
291
292 // To differentiate this tool’s errors from errors in the programs it execs,
293 // use a high exit code for ordinary failures instead of EXIT_FAILURE. This
294 // is the same rationale for using the distinct exit codes for exec
295 // failures.
296 kExitFailure = 125,
297
298 // Like env, use exit code 126 if the program was found but could not be
299 // invoked, and 127 if it could not be found.
300 // http://pubs.opengroup.org/onlinepubs/9699919799/utilities/env.html
301 kExitExecFailure = 126,
302 kExitExecENOENT = 127,
303 };
304
305 enum OptionFlags {
306 // “Short” (single-character) options.
307 kOptionSetPort = 's',
308 kOptionPid = 'p',
309 kOptionShowHost = 'h',
310 kOptionShowTask = 't',
311 kOptionShowNewHost = 'H',
312 kOptionShowNewTask = 'T',
313 kOptionNumeric = 'n',
314
315 // Long options without short equivalents.
316 kOptionLastChar = 255,
317 kOptionShowBootstrap,
318 kOptionShowThread,
319 kOptionShowNewThread,
320
321 // Standard options.
322 kOptionHelp = -2,
323 kOptionVersion = -3,
324 };
325
326 struct {
327 std::vector<const char*> show_bootstrap;
328 std::vector<ExceptionHandlerDescription> set_handler;
329 int pid;
Robert Sesek 2014/09/18 14:43:47 Not pid_t?
330 mach_port_t alternate_task;
331 bool show_host;
332 bool show_task;
333 bool show_thread;
334 bool show_new_host;
335 bool show_new_task;
336 bool show_new_thread;
337 bool numeric;
338 } options = {};
339
340 const struct option long_options[] = {
341 {"set_handler", required_argument, NULL, kOptionSetPort},
342 {"show_bootstrap", required_argument, NULL, kOptionShowBootstrap},
343 {"pid", required_argument, NULL, kOptionPid},
344 {"show_host", no_argument, NULL, kOptionShowHost},
345 {"show_task", no_argument, NULL, kOptionShowTask},
346 {"show_thread", no_argument, NULL, kOptionShowThread},
347 {"show_new_host", no_argument, NULL, kOptionShowNewHost},
348 {"show_new_task", no_argument, NULL, kOptionShowNewTask},
349 {"show_new_thread", no_argument, NULL, kOptionShowNewThread},
350 {"numeric", no_argument, NULL, kOptionNumeric},
351 {"help", no_argument, NULL, kOptionHelp},
352 {"version", no_argument, NULL, kOptionVersion},
353 {NULL, 0, NULL, 0},
354 };
355
356 int opt;
357 while ((opt = getopt_long(argc, argv, "+s:p:htHTn", long_options, NULL)) !=
358 -1) {
359 switch (opt) {
360 case kOptionSetPort: {
361 options.set_handler.push_back({});
362 ExceptionHandlerDescription* description = &options.set_handler.back();
363 description->target_type = ExceptionPorts::kTargetTypeTask;
364 description->mask = EXC_MASK_CRASH;
365 description->behavior = EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES;
366 description->flavor = THREAD_STATE_NONE;
367 description->handler = "NULL";
368 if (!ParseHandlerString(optarg, description)) {
369 fprintf(stderr,
370 "%s: invalid exception handler: %s\n",
371 me.c_str(),
372 optarg);
373 return kExitFailure;
374 }
375 break;
376 }
377 case kOptionShowBootstrap:
378 options.show_bootstrap.push_back(optarg);
379 break;
380 case kOptionPid:
381 if (!StringToNumber(optarg, &options.pid)) {
382 fprintf(stderr, "%s: invalid pid: %s\n", me.c_str(), optarg);
383 return kExitFailure;
384 }
385 break;
386 case kOptionShowHost:
387 options.show_host = true;
388 break;
389 case kOptionShowTask:
390 options.show_task = true;
391 break;
392 case kOptionShowThread:
393 options.show_thread = true;
394 break;
395 case kOptionShowNewHost:
396 options.show_new_host = true;
397 break;
398 case kOptionShowNewTask:
399 options.show_new_task = true;
400 break;
401 case kOptionShowNewThread:
402 options.show_new_thread = true;
403 break;
404 case kOptionNumeric:
405 options.numeric = true;
406 break;
407 case kOptionHelp:
408 Usage(me);
409 return kExitSuccess;
410 case kOptionVersion:
411 ToolSupport::Version(me);
412 return kExitSuccess;
413 default:
414 ToolSupport::UsageHint(me, NULL);
415 return kExitFailure;
416 }
417 }
418 argc -= optind;
419 argv += optind;
420
421 if (options.show_bootstrap.empty() && !options.show_host &&
422 !options.show_task && !options.show_thread &&
423 options.set_handler.empty() && argc == 0) {
424 ToolSupport::UsageHint(me, "nothing to do");
425 return kExitFailure;
426 }
427
428 base::mac::ScopedMachSendRight alternate_task_owner;
429 if (options.pid) {
430 if (argc) {
431 ToolSupport::UsageHint(me, "cannot combine -p with COMMAND");
432 return kExitFailure;
433 }
434
435 // This is only expected to work as root.
436 kern_return_t kr =
437 task_for_pid(mach_task_self(), options.pid, &options.alternate_task);
438 if (kr != KERN_SUCCESS) {
439 MACH_LOG(ERROR, kr) << "task_for_pid";
440 return kExitFailure;
441 }
442 alternate_task_owner.reset(options.alternate_task);
443 }
444
445 // Show bootstrap services requested.
446 for (const char* service : options.show_bootstrap) {
447 ShowBootstrapService(service);
448 }
449
450 // Show the original exception ports.
451 if (options.show_host) {
452 ShowExceptionPorts(
453 ExceptionPorts(ExceptionPorts::kTargetTypeHost, MACH_PORT_NULL),
454 options.numeric,
455 false);
456 }
457 if (options.show_task) {
458 ShowExceptionPorts(
459 ExceptionPorts(ExceptionPorts::kTargetTypeTask, options.alternate_task),
460 options.numeric,
461 false);
462 }
463 if (options.show_thread) {
464 ShowExceptionPorts(
465 ExceptionPorts(ExceptionPorts::kTargetTypeThread, MACH_PORT_NULL),
466 options.numeric,
467 false);
468 }
469
470 if (!options.set_handler.empty()) {
471 // Set new exception handlers.
472 for (ExceptionHandlerDescription description : options.set_handler) {
473 if (!SetExceptionPort(
474 &description,
475 description.target_type == ExceptionPorts::kTargetTypeTask
476 ? options.alternate_task
477 : MACH_PORT_NULL)) {
478 return kExitFailure;
479 }
480 }
481
482 // Show changed exception ports.
483 if (options.show_new_host) {
484 ShowExceptionPorts(
485 ExceptionPorts(ExceptionPorts::kTargetTypeHost, MACH_PORT_NULL),
486 options.numeric,
487 true);
488 }
489 if (options.show_new_task) {
490 ShowExceptionPorts(ExceptionPorts(ExceptionPorts::kTargetTypeTask,
491 options.alternate_task),
492 options.numeric,
493 true);
494 }
495 if (options.show_new_thread) {
496 ShowExceptionPorts(
497 ExceptionPorts(ExceptionPorts::kTargetTypeThread, MACH_PORT_NULL),
498 options.numeric,
499 true);
500 }
501 }
502
503 if (argc) {
504 // Using the remaining arguments, start a new program with the new set of
505 // exception ports in effect.
506 execvp(argv[0], argv);
507 PLOG(ERROR) << "execvp " << argv[0];
508 return errno == ENOENT ? kExitExecENOENT : kExitExecFailure;
509 }
510
511 return kExitSuccess;
512 }
OLDNEW
« no previous file with comments | « no previous file | tools/tools.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698