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

Side by Side Diff: tools/catch_exception_tool.cc

Issue 804913003: tools: Move Mac-specific tools to tools/mac (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Created 6 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
« no previous file with comments | « tools/catch_exception_tool.ad ('k') | tools/exception_port_tool.ad » ('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.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 const natural_t* 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 pid_t pid;
89 kr = pid_for_task(task, &pid);
90 if (kr != KERN_SUCCESS) {
91 MACH_LOG(ERROR, kr) << "pid_for_task";
92 return KERN_FAILURE;
93 }
94 fprintf(options_.file, "pid %d, ", pid);
95
96 thread_identifier_info identifier_info;
97 mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
98 kr = thread_info(thread,
99 THREAD_IDENTIFIER_INFO,
100 reinterpret_cast<thread_info_t>(&identifier_info),
101 &count);
102 if (kr != KERN_SUCCESS) {
103 MACH_LOG(ERROR, kr) << "thread_info";
104 return KERN_FAILURE;
105 }
106 fprintf(options_.file, "thread %lld, ", identifier_info.thread_id);
107 }
108
109 fprintf(
110 options_.file,
111 "exception %s, codes[%d] ",
112 ExceptionToString(exception, kUseFullName | kUnknownIsNumeric).c_str(),
113 code_count);
114
115 for (size_t index = 0; index < code_count; ++index) {
116 fprintf(options_.file,
117 "%#llx%s",
118 code[index],
119 index != code_count - 1 ? ", " : "");
120 }
121
122 if (exception == EXC_CRASH) {
123 mach_exception_code_t original_code_0;
124 int signal;
125 exception_type_t original_exception =
126 ExcCrashRecoverOriginalException(code[0], &original_code_0, &signal);
127 fprintf(options_.file,
128 ", original exception %s, original code[0] %lld, signal %s",
129 ExceptionToString(original_exception,
130 kUseFullName | kUnknownIsNumeric).c_str(),
131 original_code_0,
132 SignalToString(signal, kUseFullName | kUnknownIsNumeric).c_str());
133 }
134
135 if (ExceptionBehaviorHasState(behavior)) {
136 // If this is a state-carrying exception, make new_state something valid.
137 memcpy(
138 new_state,
139 old_state,
140 std::min(old_state_count, *new_state_count) * sizeof(old_state[0]));
141 *new_state_count = old_state_count;
142
143 std::string flavor_string =
144 ThreadStateFlavorToString(*flavor, kUseFullName | kUnknownIsNumeric);
145 fprintf(options_.file,
146 ", flavor %s, old_state_count %d",
147 flavor_string.c_str(),
148 old_state_count);
149 }
150
151 fprintf(options_.file, "\n");
152 fflush(options_.file);
153
154 if (exception != EXC_CRASH && exception != kMachExceptionSimulated) {
155 // Find another handler.
156 return KERN_FAILURE;
157 }
158
159 return ExcServerSuccessfulReturnValue(behavior, false);
160 }
161
162 private:
163 const Options& options_;
164 const std::string& me_;
165 int* exceptions_handled_;
166 };
167
168 void Usage(const std::string& me) {
169 fprintf(stderr,
170 "Usage: %s -m SERVICE [OPTION]...\n"
171 "Catch Mach exceptions and display information about them.\n"
172 "\n"
173 " -f, --file=FILE append information to FILE instead of stdout\n"
174 " -m, --mach-service=SERVICE register SERVICE with the bootstrap server\n"
175 " -p, --persistent continue processing exceptions after the first\n"
176 " -t, --timeout=TIMEOUT run for a maximum of TIMEOUT seconds\n"
177 " --help display this help and exit\n"
178 " --version output version information and exit\n",
179 me.c_str());
180 ToolSupport::UsageTail(me);
181 }
182
183 int CatchExceptionToolMain(int argc, char* argv[]) {
184 const std::string me(basename(argv[0]));
185
186 enum OptionFlags {
187 // “Short” (single-character) options.
188 kOptionFile = 'f',
189 kOptionMachService = 'm',
190 kOptionPersistent = 'p',
191 kOptionTimeout = 't',
192
193 // Long options without short equivalents.
194 kOptionLastChar = 255,
195
196 // Standard options.
197 kOptionHelp = -2,
198 kOptionVersion = -3,
199 };
200
201 Options options = {};
202
203 const struct option long_options[] = {
204 {"file", required_argument, nullptr, kOptionFile},
205 {"mach-service", required_argument, nullptr, kOptionMachService},
206 {"persistent", no_argument, nullptr, kOptionPersistent},
207 {"timeout", required_argument, nullptr, kOptionTimeout},
208 {"help", no_argument, nullptr, kOptionHelp},
209 {"version", no_argument, nullptr, kOptionVersion},
210 {nullptr, 0, nullptr, 0},
211 };
212
213 int opt;
214 while ((opt = getopt_long(argc, argv, "f:m:pt:", long_options, nullptr)) !=
215 -1) {
216 switch (opt) {
217 case kOptionFile:
218 options.file_path = optarg;
219 break;
220 case kOptionMachService:
221 options.mach_service = optarg;
222 break;
223 case kOptionPersistent:
224 options.persistent = MachMessageServer::kPersistent;
225 break;
226 case kOptionTimeout:
227 if (!StringToNumber(optarg, &options.timeout_secs) ||
228 options.timeout_secs < 0) {
229 ToolSupport::UsageHint(me, "-t requires a zero or positive TIMEOUT");
230 return EXIT_FAILURE;
231 }
232 options.has_timeout = true;
233 break;
234 case kOptionHelp:
235 Usage(me);
236 return EXIT_SUCCESS;
237 case kOptionVersion:
238 ToolSupport::Version(me);
239 return EXIT_SUCCESS;
240 default:
241 ToolSupport::UsageHint(me, nullptr);
242 return EXIT_FAILURE;
243 }
244 }
245 argc -= optind;
246 argv += optind;
247
248 if (options.mach_service.empty()) {
249 ToolSupport::UsageHint(me, "-m is required");
250 return EXIT_FAILURE;
251 }
252
253 mach_port_t service_port;
254 kern_return_t kr = bootstrap_check_in(
255 bootstrap_port, options.mach_service.c_str(), &service_port);
256 if (kr != BOOTSTRAP_SUCCESS) {
257 BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_check_in " << options.mach_service;
258 return EXIT_FAILURE;
259 }
260
261 base::ScopedFILE file_owner;
262 if (options.file_path.empty()) {
263 options.file = stdout;
264 } else {
265 file_owner.reset(fopen(options.file_path.c_str(), "a"));
266 if (!file_owner.get()) {
267 PLOG(ERROR) << "fopen " << options.file_path;
268 return EXIT_FAILURE;
269 }
270 options.file = file_owner.get();
271 }
272
273 int exceptions_handled = 0;
274 ExceptionServer exception_server(options, me, &exceptions_handled);
275 UniversalMachExcServer universal_mach_exc_server(&exception_server);
276
277 // Assume that if persistent mode has been requested, it’s desirable to ignore
278 // large messages and keep running.
279 MachMessageServer::ReceiveLarge receive_large =
280 (options.persistent == MachMessageServer::kPersistent)
281 ? MachMessageServer::kReceiveLargeIgnore
282 : MachMessageServer::kReceiveLargeError;
283
284 mach_msg_timeout_t timeout_ms;
285 if (!options.has_timeout) {
286 timeout_ms = kMachMessageTimeoutWaitIndefinitely;
287 } else if (options.timeout_secs == 0) {
288 timeout_ms = kMachMessageTimeoutNonblocking;
289 } else {
290 timeout_ms = options.timeout_secs * 1000;
291 }
292
293 mach_msg_return_t mr = MachMessageServer::Run(&universal_mach_exc_server,
294 service_port,
295 MACH_MSG_OPTION_NONE,
296 options.persistent,
297 receive_large,
298 timeout_ms);
299 if (mr == MACH_RCV_TIMED_OUT && options.has_timeout && options.persistent &&
300 exceptions_handled) {
301 // This is not an error: when exiting on timeout during persistent
302 // processing and at least one exception was handled, it’s considered a
303 // success.
304 } else if (mr != MACH_MSG_SUCCESS) {
305 MACH_LOG(ERROR, mr) << "MachMessageServer::Run";
306 return EXIT_FAILURE;
307 }
308
309 return EXIT_SUCCESS;
310 }
311
312 } // namespace
313 } // namespace crashpad
314
315 int main(int argc, char* argv[]) {
316 return crashpad::CatchExceptionToolMain(argc, argv);
317 }
OLDNEW
« no previous file with comments | « tools/catch_exception_tool.ad ('k') | tools/exception_port_tool.ad » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698