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

Side by Side Diff: tools/catch_exception_tool.cc

Issue 579443005: Add catch_exception_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 <getopt.h>
16 #include <libgen.h>
17 #include <servers/bootstrap.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <unistd.h>
21
22 #include <algorithm>
23 #include <string>
24 #include <vector>
25
26 #include "base/files/scoped_file.h"
27 #include "base/logging.h"
28 #include "base/mac/mach_logging.h"
29 #include "tools/tool_support.h"
30 #include "util/mach/exc_server_variants.h"
31 #include "util/mach/exception_behaviors.h"
32 #include "util/mach/mach_extensions.h"
33 #include "util/mach/mach_message_server.h"
34 #include "util/mach/symbolic_constants_mach.h"
35 #include "util/posix/symbolic_constants_posix.h"
36 #include "util/stdlib/string_number_conversion.h"
37
38 namespace {
39
40 using namespace crashpad;
41
42 struct Options {
43 std::string file_path;
44 std::string mach_service;
45 FILE* file;
46 int timeout_secs;
47 MachMessageServer::Nonblocking nonblocking;
48 MachMessageServer::Persistent persistent;
49 };
50
51 class ExceptionServer : public UniversalMachExcServer {
52 public:
53 ExceptionServer(const Options& options,
54 const std::string& me,
55 int* exceptions_handled)
56 : UniversalMachExcServer(),
57 options_(options),
58 me_(me),
59 exceptions_handled_(exceptions_handled) {}
60
61 virtual kern_return_t CatchMachException(
62 exception_behavior_t behavior,
63 exception_handler_t exception_port,
64 thread_t thread,
65 task_t task,
66 exception_type_t exception,
67 const mach_exception_data_type_t* code,
68 mach_msg_type_number_t code_count,
69 thread_state_flavor_t* flavor,
70 const natural_t* old_state,
71 mach_msg_type_number_t old_state_count,
72 thread_state_t new_state,
73 mach_msg_type_number_t* new_state_count,
74 bool* destroy_complex_request) override {
75 *destroy_complex_request = true;
76 ++*exceptions_handled_;
77
78 fprintf(options_.file,
79 "%s: behavior %s, ",
80 me_.c_str(),
81 ExceptionBehaviorToString(
82 behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str());
83
84 pid_t pid;
85 kern_return_t kr;
86 if (ExceptionBehaviorHasIdentity(behavior)) {
87 kr = pid_for_task(task, &pid);
88 if (kr != KERN_SUCCESS) {
89 MACH_LOG(ERROR, kr) << "pid_for_task";
90 return KERN_FAILURE;
91 }
92 fprintf(options_.file, "pid %d, ", pid);
93 } else {
94 pid = -1;
95 }
96
97 if (ExceptionBehaviorHasState(behavior)) {
98 // If this is a state-carrying exception, make new_state something valid.
99 memcpy(
100 new_state,
101 old_state,
102 std::min(old_state_count, *new_state_count) * sizeof(old_state[0]));
103 *new_state_count = old_state_count;
104 }
105
106 fprintf(
107 options_.file,
108 "exception %s, codes[%d] ",
109 ExceptionToString(exception, kUseFullName | kUnknownIsNumeric).c_str(),
110 code_count);
111
112 for (size_t index = 0; index < code_count; ++index) {
113 fprintf(options_.file,
114 "%#llx%s",
115 code[index],
116 index != code_count - 1 ? ", " : "");
117 }
118
119 if (exception == EXC_CRASH) {
120 // 10.9.4 xnu-2422.110.17/bsd/kern/kern_exit.c proc_prepareexit() sets
121 // code[0] based on the signal value, original exception type, and low 20
122 // bits of the original code[0] before calling
123 // xnu-2422.110.17/osfmk/kern/exception.c task_exception_notify() to raise
124 // an EXC_CRASH.
125 int sig = (code[0] >> 24) & 0xff;
Robert Sesek 2014/09/17 16:28:03 You've masked the code this way before. Would it b
126 int orig_exception = (code[0] >> 20) & 0xf;
127 int orig_code_0 = code[0] & 0xfffff;
128 fprintf(options_.file,
129 ", original exception %s, original code0 %d, signal %s",
130 ExceptionToString(orig_exception,
131 kUseFullName | kUnknownIsNumeric).c_str(),
132 orig_code_0,
133 SignalToString(sig, kUseFullName | kUnknownIsNumeric).c_str());
134 }
135
136 fprintf(options_.file, "\n");
137 fflush(options_.file);
138
139 if (exception != EXC_CRASH && exception != kMachExceptionSimulated) {
140 // Find another handler.
141 return KERN_FAILURE;
142 }
143
144 return ExcServerSuccessfulReturnValue(behavior, false);
145 }
146
147 private:
148 const Options& options_;
149 const std::string& me_;
150 int* exceptions_handled_;
151 };
152
153 void Usage(const std::string& me) {
154 fprintf(stderr,
155 "Usage: %s -m SERVICE [OPTION]...\n"
156 "Catch Mach exceptions and display information about them.\n"
157 "\n"
158 " -f, --file=FILE append information to FILE instead of stdout\n"
159 " -m, --mach_service=SERVICE register SERVICE with the bootstrap server\n"
160 " -n, --nonblocking don't block waiting for an exception to occur\n"
161 " -p, --persistent continue processing exceptions after the first\n"
162 " -t, --timeout=TIMEOUT run for a maximum of TIMEOUT seconds\n"
163 " --help display this help and exit\n"
164 " --version output version information and exit\n",
165 me.c_str());
166 ToolSupport::UsageTail(me);
167 }
168
169 } // namespace
170
171 int main(int argc, char* argv[]) {
172 const std::string me(basename(argv[0]));
173
174 enum OptionFlags {
175 // “Short” (single-character) options.
176 kOptionFile = 'f',
177 kOptionMachService = 'm',
178 kOptionNonblocking = 'n',
179 kOptionPersistent = 'p',
180 kOptionTimeout = 't',
181
182 // Long options without short equivalents.
183 kOptionLastChar = 255,
184
185 // Standard options.
186 kOptionHelp = -2,
187 kOptionVersion = -3,
188 };
189
190 Options options = {};
191
192 const struct option long_options[] = {
193 {"file", required_argument, NULL, kOptionFile},
194 {"mach_service", required_argument, NULL, kOptionMachService},
195 {"nonblocking", no_argument, NULL, kOptionNonblocking},
196 {"persistent", no_argument, NULL, kOptionPersistent},
197 {"timeout", required_argument, NULL, kOptionTimeout},
198 {"help", no_argument, NULL, kOptionHelp},
199 {"version", no_argument, NULL, kOptionVersion},
200 {NULL, 0, NULL, 0},
201 };
202
203 int opt;
204 while ((opt = getopt_long(argc, argv, "f:m:npt:", long_options, NULL)) !=
205 -1) {
206 switch (opt) {
207 case kOptionFile:
208 options.file_path = optarg;
209 break;
210 case kOptionMachService:
211 options.mach_service = optarg;
212 break;
213 case kOptionNonblocking:
214 options.nonblocking = MachMessageServer::kNonblocking;
215 break;
216 case kOptionPersistent:
217 options.persistent = MachMessageServer::kPersistent;
218 break;
219 case kOptionTimeout:
220 if (!StringToNumber(optarg, &options.timeout_secs) ||
221 options.timeout_secs <= 0) {
222 ToolSupport::UsageHint(me, "-t requires a positive TIMEOUT");
223 return EXIT_FAILURE;
224 }
225 break;
226 case kOptionHelp:
227 Usage(me);
228 return EXIT_SUCCESS;
229 case kOptionVersion:
230 ToolSupport::Version(me);
231 return EXIT_SUCCESS;
232 default:
233 ToolSupport::UsageHint(me, NULL);
234 return EXIT_FAILURE;
235 }
236 }
237 argc -= optind;
238 argv += optind;
239
240 if (options.mach_service.empty()) {
241 ToolSupport::UsageHint(me, "-m is required");
242 return EXIT_FAILURE;
243 }
244
245 mach_port_t service_port;
246 kern_return_t kr = bootstrap_check_in(
247 bootstrap_port, options.mach_service.c_str(), &service_port);
248 if (kr != BOOTSTRAP_SUCCESS) {
249 BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_check_in " << options.mach_service;
250 return EXIT_FAILURE;
251 }
252
253 base::ScopedFILE file_owner;
254 if (options.file_path.empty()) {
255 options.file = stdout;
256 } else {
257 file_owner.reset(fopen(options.file_path.c_str(), "a"));
258 if (!file_owner.get()) {
259 PLOG(ERROR) << "fopen " << options.file_path;
260 return EXIT_FAILURE;
261 }
262 options.file = file_owner.get();
263 }
264
265 int exceptions_handled = 0;
266 ExceptionServer exception_server(options, me, &exceptions_handled);
267
268 mach_msg_timeout_t timeout_ms = options.timeout_secs
269 ? options.timeout_secs * 1000
270 : MACH_MSG_TIMEOUT_NONE;
271
272 mach_msg_return_t mr = MachMessageServer::Run(&exception_server,
273 service_port,
274 MACH_MSG_OPTION_NONE,
275 options.persistent,
276 options.nonblocking,
277 timeout_ms);
278 if (mr == MACH_RCV_TIMED_OUT && options.timeout_secs && options.persistent &&
279 exceptions_handled) {
280 // This is not an error: when exiting on timeout during persistent
281 // processing and at least one exception was handled, it’s considered a
282 // success.
283 } else if (mr != MACH_MSG_SUCCESS) {
284 MACH_LOG(ERROR, mr) << "MachMessageServer::Run";
285 return EXIT_FAILURE;
286 }
287
288 return EXIT_SUCCESS;
289 }
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