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 "handler/handler_main.h"
16
17 #include <errno.h>
18 #include <getopt.h>
19 #include <stdint.h>
20 #include <stdlib.h>
21 #include <sys/types.h>
22
23 #include <algorithm>
24 #include <map>
25 #include <memory>
26 #include <string>
27 #include <utility>
28 #include <vector>
29
30 #include "base/auto_reset.h"
31 #include "base/compiler_specific.h"
32 #include "base/files/file_path.h"
33 #include "base/files/scoped_file.h"
34 #include "base/logging.h"
35 #include "base/metrics/persistent_histogram_allocator.h"
36 #include "base/scoped_generic.h"
37 #include "base/strings/string_number_conversions.h"
38 #include "base/strings/stringprintf.h"
39 #include "base/strings/utf_string_conversions.h"
40 #include "build/build_config.h"
41 #include "client/crash_report_database.h"
42 #include "client/crashpad_client.h"
43 #include "client/crashpad_info.h"
44 #include "client/prune_crash_reports.h"
45 #include "client/simple_string_dictionary.h"
46 #include "handler/crash_report_upload_thread.h"
47 #include "handler/prune_crash_reports_thread.h"
48 #include "tools/tool_support.h"
49 #include "util/file/file_io.h"
50 #include "util/misc/address_types.h"
51 #include "util/misc/metrics.h"
52 #include "util/misc/paths.h"
53 #include "util/numeric/in_range_cast.h"
54 #include "util/stdlib/map_insert.h"
55 #include "util/stdlib/string_number_conversion.h"
56 #include "util/string/split_string.h"
57 #include "util/synchronization/semaphore.h"
58
59 #if defined(OS_CHROMEOS)
60 #include "handler/linux/cros_crash_report_exception_handler.h"
61 #endif
62
63 #if defined(OS_LINUX) || defined(OS_ANDROID)
64 #include <unistd.h>
65
66 #include "handler/linux/crash_report_exception_handler.h"
67 #include "handler/linux/exception_handler_server.h"
68 #include "util/posix/signals.h"
69 #elif defined(OS_MACOSX)
70 #include <libgen.h>
71 #include <signal.h>
72
73 #include "base/mac/scoped_mach_port.h"
74 #include "handler/mac/crash_report_exception_handler.h"
75 #include "handler/mac/exception_handler_server.h"
76 #include "handler/mac/file_limit_annotation.h"
77 #include "util/mach/child_port_handshake.h"
78 #include "util/mach/mach_extensions.h"
79 #include "util/posix/close_stdio.h"
80 #include "util/posix/signals.h"
81 #elif defined(OS_WIN)
82 #include <windows.h>
83
84 #include "handler/win/crash_report_exception_handler.h"
85 #include "util/win/exception_handler_server.h"
86 #include "util/win/handle.h"
87 #include "util/win/initial_client_data.h"
88 #include "util/win/session_end_watcher.h"
89 #elif defined(OS_FUCHSIA)
90 #include <zircon/process.h>
91 #include <zircon/processargs.h>
92
93 #include <lib/zx/channel.h>
94 #include <lib/zx/job.h>
95
96 #include "handler/fuchsia/crash_report_exception_handler.h"
97 #include "handler/fuchsia/exception_handler_server.h"
98 #elif defined(OS_LINUX)
99 #include "handler/linux/crash_report_exception_handler.h"
100 #include "handler/linux/exception_handler_server.h"
101 #endif // OS_MACOSX
102
103 namespace crashpad {
104
105 namespace {
106
Usage(const base::FilePath & me)107 void Usage(const base::FilePath& me) {
108 fprintf(stderr,
109 "Usage: %" PRFilePath " [OPTION]...\n"
110 "Crashpad's exception handler server.\n"
111 "\n"
112 " --annotation=KEY=VALUE set a process annotation in each crash report\n"
113 " --database=PATH store the crash report database at PATH\n"
114 #if defined(OS_MACOSX)
115 " --handshake-fd=FD establish communication with the client over FD\n"
116 #endif // OS_MACOSX
117 #if defined(OS_WIN)
118 " --initial-client-data=HANDLE_request_crash_dump,\n"
119 " HANDLE_request_non_crash_dump,\n"
120 " HANDLE_non_crash_dump_completed,\n"
121 " HANDLE_pipe,\n"
122 " HANDLE_client_process,\n"
123 " Address_crash_exception_information,\n"
124 " Address_non_crash_exception_information,\n"
125 " Address_debug_critical_section\n"
126 " use precreated data to register initial client\n"
127 #endif // OS_WIN
128 #if defined(OS_ANDROID) || defined(OS_LINUX)
129 " --initial-client-fd=FD a socket connected to a client.\n"
130 #endif // OS_ANDROID || OS_LINUX
131 #if defined(OS_MACOSX)
132 " --mach-service=SERVICE register SERVICE with the bootstrap server\n"
133 #endif // OS_MACOSX
134 " --metrics-dir=DIR store metrics files in DIR (only in Chromium)\n"
135 " --monitor-self run a second handler to catch crashes in the first\n"
136 " --monitor-self-annotation=KEY=VALUE\n"
137 " set a module annotation in the handler\n"
138 " --monitor-self-argument=ARGUMENT\n"
139 " provide additional arguments to the second handler\n"
140 " --no-identify-client-via-url\n"
141 " when uploading crash report, don't add\n"
142 " client-identifying arguments to URL\n"
143 " --no-periodic-tasks don't scan for new reports or prune the database\n"
144 " --no-rate-limit don't rate limit crash uploads\n"
145 " --no-upload-gzip don't use gzip compression when uploading\n"
146 #if defined(OS_ANDROID)
147 " --no-write-minidump-to-database\n"
148 " don't write minidump to database\n"
149 #endif // OS_ANDROID
150 #if defined(OS_WIN)
151 " --pipe-name=PIPE communicate with the client over PIPE\n"
152 #endif // OS_WIN
153 #if defined(OS_MACOSX)
154 " --reset-own-crash-exception-port-to-system-default\n"
155 " reset the server's exception handler to default\n"
156 #endif // OS_MACOSX
157 #if defined(OS_LINUX) || defined(OS_ANDROID)
158 " --sanitization-information=SANITIZATION_INFORMATION_ADDRESS\n"
159 " the address of a SanitizationInformation struct.\n"
160 " --shared-client-connection the file descriptor provided by\n"
161 " --initial-client-fd is shared among multiple\n"
162 " clients\n"
163 " --trace-parent-with-exception=EXCEPTION_INFORMATION_ADDRESS\n"
164 " request a dump for the handler's parent process\n"
165 #endif // OS_LINUX || OS_ANDROID
166 " --url=URL send crash reports to this Breakpad server URL,\n"
167 " only if uploads are enabled for the database\n"
168 #if defined(OS_CHROMEOS)
169 " --use-cros-crash-reporter\n"
170 " pass crash reports to /sbin/crash_reporter\n"
171 " instead of storing them in the database\n"
172 " --minidump-dir-for-tests=TEST_MINIDUMP_DIR\n"
173 " causes /sbin/crash_reporter to leave dumps in\n"
174 " this directory instead of the normal location\n"
175 " --always-allow-feedback\n"
176 " pass the --always_allow_feedback flag to\n"
177 " crash_reporter, thus skipping metrics consent\n"
178 " checks\n"
179 #endif // OS_CHROMEOS
180 #if defined(OS_ANDROID)
181 " --write-minidump-to-log write minidump to log\n"
182 #endif // OS_ANDROID
183 " --help display this help and exit\n"
184 " --version output version information and exit\n",
185 me.value().c_str());
186 ToolSupport::UsageTail(me);
187 }
188
189 struct Options {
190 std::map<std::string, std::string> annotations;
191 std::map<std::string, std::string> monitor_self_annotations;
192 std::string url;
193 base::FilePath database;
194 base::FilePath metrics_dir;
195 std::vector<std::string> monitor_self_arguments;
196 #if defined(OS_MACOSX)
197 std::string mach_service;
198 int handshake_fd;
199 bool reset_own_crash_exception_port_to_system_default;
200 #elif defined(OS_LINUX) || defined(OS_ANDROID)
201 VMAddress exception_information_address;
202 VMAddress sanitization_information_address;
203 int initial_client_fd;
204 bool shared_client_connection;
205 #if defined(OS_ANDROID)
206 bool write_minidump_to_log;
207 bool write_minidump_to_database;
208 #endif // OS_ANDROID
209 #elif defined(OS_WIN)
210 std::string pipe_name;
211 InitialClientData initial_client_data;
212 #endif // OS_MACOSX
213 bool identify_client_via_url;
214 bool monitor_self;
215 bool periodic_tasks;
216 bool rate_limit;
217 bool upload_gzip;
218 #if defined(OS_CHROMEOS)
219 bool use_cros_crash_reporter = false;
220 base::FilePath minidump_dir_for_tests;
221 bool always_allow_feedback = false;
222 #endif // OS_CHROMEOS
223 };
224
225 // Splits |key_value| on '=' and inserts the resulting key and value into |map|.
226 // If |key_value| has the wrong format, logs an error and returns false. If the
227 // key is already in the map, logs a warning, replaces the existing value, and
228 // returns true. If the key and value were inserted into the map, returns true.
229 // |argument| is used to give context to logged messages.
AddKeyValueToMap(std::map<std::string,std::string> * map,const std::string & key_value,const char * argument)230 bool AddKeyValueToMap(std::map<std::string, std::string>* map,
231 const std::string& key_value,
232 const char* argument) {
233 std::string key;
234 std::string value;
235 if (!SplitStringFirst(key_value, '=', &key, &value)) {
236 LOG(ERROR) << argument << " requires KEY=VALUE";
237 return false;
238 }
239
240 std::string old_value;
241 if (!MapInsertOrReplace(map, key, value, &old_value)) {
242 LOG(WARNING) << argument << " has duplicate key " << key
243 << ", discarding value " << old_value;
244 }
245 return true;
246 }
247
248 // Calls Metrics::HandlerLifetimeMilestone, but only on the first call. This is
249 // to prevent multiple exit events from inadvertently being recorded, which
250 // might happen if a crash occurs during destruction in what would otherwise be
251 // a normal exit, or if a CallMetricsRecordNormalExit object is destroyed after
252 // something else logs an exit event.
MetricsRecordExit(Metrics::LifetimeMilestone milestone)253 void MetricsRecordExit(Metrics::LifetimeMilestone milestone) {
254 static bool once = [](Metrics::LifetimeMilestone milestone) {
255 Metrics::HandlerLifetimeMilestone(milestone);
256 return true;
257 }(milestone);
258 ALLOW_UNUSED_LOCAL(once);
259 }
260
261 // Calls MetricsRecordExit() to record a failure, and returns EXIT_FAILURE for
262 // the convenience of callers in main() which can simply write “return
263 // ExitFailure();”.
ExitFailure()264 int ExitFailure() {
265 MetricsRecordExit(Metrics::LifetimeMilestone::kFailed);
266 return EXIT_FAILURE;
267 }
268
269 class CallMetricsRecordNormalExit {
270 public:
CallMetricsRecordNormalExit()271 CallMetricsRecordNormalExit() {}
~CallMetricsRecordNormalExit()272 ~CallMetricsRecordNormalExit() {
273 MetricsRecordExit(Metrics::LifetimeMilestone::kExitedNormally);
274 }
275
276 private:
277 DISALLOW_COPY_AND_ASSIGN(CallMetricsRecordNormalExit);
278 };
279
280 #if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_ANDROID)
281
HandleCrashSignal(int sig,siginfo_t * siginfo,void * context)282 void HandleCrashSignal(int sig, siginfo_t* siginfo, void* context) {
283 MetricsRecordExit(Metrics::LifetimeMilestone::kCrashed);
284
285 // Is siginfo->si_code useful? The only interesting values on macOS are 0 (not
286 // useful, signals generated asynchronously such as by kill() or raise()) and
287 // small positive numbers (useful, signal generated via a hardware fault). The
288 // standard specifies these other constants, and while xnu never uses them,
289 // they are intended to denote signals generated asynchronously and are
290 // included here. Additionally, existing practice on other systems
291 // (acknowledged by the standard) is for negative numbers to indicate that a
292 // signal was generated asynchronously. Although xnu does not do this, allow
293 // for the possibility for completeness.
294 bool si_code_valid = !(siginfo->si_code <= 0 ||
295 siginfo->si_code == SI_USER ||
296 siginfo->si_code == SI_QUEUE ||
297 siginfo->si_code == SI_TIMER ||
298 siginfo->si_code == SI_ASYNCIO ||
299 siginfo->si_code == SI_MESGQ);
300
301 // 0x5343 = 'SC', signifying “signal and code”, disambiguates from the schema
302 // used by ExceptionCodeForMetrics(). That system primarily uses Mach
303 // exception types and codes, which are not available to a POSIX signal
304 // handler. It does provide a way to encode only signal numbers, but does so
305 // with the understanding that certain “raw” signals would not be encountered
306 // without a Mach exception. Furthermore, it does not allow siginfo->si_code
307 // to be encoded, because that’s not available to Mach exception handlers. It
308 // would be a shame to lose that information available to a POSIX signal
309 // handler.
310 int metrics_code = 0x53430000 | (InRangeCast<uint8_t>(sig, 0xff) << 8);
311 if (si_code_valid) {
312 metrics_code |= InRangeCast<uint8_t>(siginfo->si_code, 0xff);
313 }
314 Metrics::HandlerCrashed(metrics_code);
315
316 Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
317 }
318
HandleTerminateSignal(int sig,siginfo_t * siginfo,void * context)319 void HandleTerminateSignal(int sig, siginfo_t* siginfo, void* context) {
320 MetricsRecordExit(Metrics::LifetimeMilestone::kTerminated);
321 Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
322 }
323
ReinstallCrashHandler()324 void ReinstallCrashHandler() {
325 // This is used to re-enable the metrics-recording crash handler after
326 // MonitorSelf() sets up a Crashpad exception handler. On macOS, the
327 // metrics-recording handler uses signals and the Crashpad handler uses Mach
328 // exceptions, so there’s nothing to re-enable.
329 // On Linux, the signal handler installed by StartHandler() restores the
330 // previously installed signal handler by default.
331 }
332
InstallCrashHandler()333 void InstallCrashHandler() {
334 Signals::InstallCrashHandlers(HandleCrashSignal, 0, nullptr);
335
336 // Not a crash handler, but close enough.
337 Signals::InstallTerminateHandlers(HandleTerminateSignal, 0, nullptr);
338 }
339
340 #if defined(OS_MACOSX)
341
342 struct ResetSIGTERMTraits {
InvalidValuecrashpad::__anon891c7b860111::ResetSIGTERMTraits343 static struct sigaction* InvalidValue() {
344 return nullptr;
345 }
346
Freecrashpad::__anon891c7b860111::ResetSIGTERMTraits347 static void Free(struct sigaction* sa) {
348 int rv = sigaction(SIGTERM, sa, nullptr);
349 PLOG_IF(ERROR, rv != 0) << "sigaction";
350 }
351 };
352 using ScopedResetSIGTERM =
353 base::ScopedGeneric<struct sigaction*, ResetSIGTERMTraits>;
354
355 ExceptionHandlerServer* g_exception_handler_server;
356
357 // This signal handler is only operative when being run from launchd.
HandleSIGTERM(int sig,siginfo_t * siginfo,void * context)358 void HandleSIGTERM(int sig, siginfo_t* siginfo, void* context) {
359 // Don’t call MetricsRecordExit(). This is part of the normal exit path when
360 // running from launchd.
361
362 DCHECK(g_exception_handler_server);
363 g_exception_handler_server->Stop();
364 }
365
366 #endif // OS_MACOSX
367
368 #elif defined(OS_WIN)
369
370 LONG(WINAPI* g_original_exception_filter)(EXCEPTION_POINTERS*) = nullptr;
371
UnhandledExceptionHandler(EXCEPTION_POINTERS * exception_pointers)372 LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
373 MetricsRecordExit(Metrics::LifetimeMilestone::kCrashed);
374 Metrics::HandlerCrashed(exception_pointers->ExceptionRecord->ExceptionCode);
375
376 if (g_original_exception_filter)
377 return g_original_exception_filter(exception_pointers);
378 else
379 return EXCEPTION_CONTINUE_SEARCH;
380 }
381
382 // Handles events like Control-C and Control-Break on a console.
ConsoleHandler(DWORD console_event)383 BOOL WINAPI ConsoleHandler(DWORD console_event) {
384 MetricsRecordExit(Metrics::LifetimeMilestone::kTerminated);
385 return false;
386 }
387
388 // Handles a WM_ENDSESSION message sent when the user session is ending.
389 class TerminateHandler final : public SessionEndWatcher {
390 public:
TerminateHandler()391 TerminateHandler() : SessionEndWatcher() {}
~TerminateHandler()392 ~TerminateHandler() override {}
393
394 private:
395 // SessionEndWatcher:
SessionEnding()396 void SessionEnding() override {
397 MetricsRecordExit(Metrics::LifetimeMilestone::kTerminated);
398 }
399
400 DISALLOW_COPY_AND_ASSIGN(TerminateHandler);
401 };
402
ReinstallCrashHandler()403 void ReinstallCrashHandler() {
404 // This is used to re-enable the metrics-recording crash handler after
405 // MonitorSelf() sets up a Crashpad exception handler. The Crashpad handler
406 // takes over the UnhandledExceptionFilter, so reinstall the metrics-recording
407 // one.
408 g_original_exception_filter =
409 SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
410 }
411
InstallCrashHandler()412 void InstallCrashHandler() {
413 ReinstallCrashHandler();
414
415 // These are termination handlers, not crash handlers, but that’s close
416 // enough. Note that destroying the TerminateHandler would wait for its thread
417 // to exit, which isn’t necessary or desirable.
418 SetConsoleCtrlHandler(ConsoleHandler, true);
419 static TerminateHandler* terminate_handler = new TerminateHandler();
420 ALLOW_UNUSED_LOCAL(terminate_handler);
421 }
422
423 #elif defined(OS_FUCHSIA)
424
InstallCrashHandler()425 void InstallCrashHandler() {
426 // There's nothing to do here. Crashes in this process will already be caught
427 // here because this handler process is in the same job that has had its
428 // exception port bound.
429
430 // TODO(scottmg): This should collect metrics on handler crashes, at a
431 // minimum. https://crashpad.chromium.org/bug/230.
432 }
433
ReinstallCrashHandler()434 void ReinstallCrashHandler() {
435 // TODO(scottmg): Fuchsia: https://crashpad.chromium.org/bug/196
436 NOTREACHED();
437 }
438
439 #endif // OS_MACOSX
440
MonitorSelf(const Options & options)441 void MonitorSelf(const Options& options) {
442 base::FilePath executable_path;
443 if (!Paths::Executable(&executable_path)) {
444 return;
445 }
446
447 if (std::find(options.monitor_self_arguments.begin(),
448 options.monitor_self_arguments.end(),
449 "--monitor-self") != options.monitor_self_arguments.end()) {
450 LOG(WARNING) << "--monitor-self-argument=--monitor-self is not supported";
451 return;
452 }
453 std::vector<std::string> extra_arguments(options.monitor_self_arguments);
454 if (!options.identify_client_via_url) {
455 extra_arguments.push_back("--no-identify-client-via-url");
456 }
457 extra_arguments.push_back("--no-periodic-tasks");
458 if (!options.rate_limit) {
459 extra_arguments.push_back("--no-rate-limit");
460 }
461 if (!options.upload_gzip) {
462 extra_arguments.push_back("--no-upload-gzip");
463 }
464 for (const auto& iterator : options.monitor_self_annotations) {
465 extra_arguments.push_back(
466 base::StringPrintf("--monitor-self-annotation=%s=%s",
467 iterator.first.c_str(),
468 iterator.second.c_str()));
469 }
470
471 // Don’t use options.metrics_dir. The current implementation only allows one
472 // instance of crashpad_handler to be writing metrics at a time, and it should
473 // be the primary instance.
474 CrashpadClient crashpad_client;
475 #if defined(OS_ANDROID)
476 if (!crashpad_client.StartHandlerAtCrash(executable_path,
477 options.database,
478 base::FilePath(),
479 options.url,
480 options.annotations,
481 extra_arguments)) {
482 return;
483 }
484 #else
485 if (!crashpad_client.StartHandler(executable_path,
486 options.database,
487 base::FilePath(),
488 options.url,
489 options.annotations,
490 extra_arguments,
491 true,
492 false)) {
493 return;
494 }
495 #endif
496
497 // Make sure that appropriate metrics will be recorded on crash before this
498 // process is terminated.
499 ReinstallCrashHandler();
500 }
501
502 class ScopedStoppable {
503 public:
504 ScopedStoppable() = default;
505
~ScopedStoppable()506 ~ScopedStoppable() {
507 if (stoppable_) {
508 stoppable_->Stop();
509 }
510 }
511
Reset(Stoppable * stoppable)512 void Reset(Stoppable* stoppable) { stoppable_.reset(stoppable); }
513
Get()514 Stoppable* Get() { return stoppable_.get(); }
515
516 private:
517 std::unique_ptr<Stoppable> stoppable_;
518
519 DISALLOW_COPY_AND_ASSIGN(ScopedStoppable);
520 };
521
522 } // namespace
523
HandlerMain(int argc,char * argv[],const UserStreamDataSources * user_stream_sources)524 int HandlerMain(int argc,
525 char* argv[],
526 const UserStreamDataSources* user_stream_sources) {
527 #if defined(OS_CHROMEOS)
528 if (freopen("/var/log/chrome/chrome", "a", stderr) == nullptr) {
529 PLOG(ERROR) << "Failed to redirect stderr to /var/log/chrome/chrome";
530 }
531 #endif
532
533 InstallCrashHandler();
534 CallMetricsRecordNormalExit metrics_record_normal_exit;
535
536 const base::FilePath argv0(
537 ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));
538 const base::FilePath me(argv0.BaseName());
539
540 enum OptionFlags {
541 // Long options without short equivalents.
542 kOptionLastChar = 255,
543 kOptionAnnotation,
544 kOptionDatabase,
545 #if defined(OS_MACOSX)
546 kOptionHandshakeFD,
547 #endif // OS_MACOSX
548 #if defined(OS_WIN)
549 kOptionInitialClientData,
550 #endif // OS_WIN
551 #if defined(OS_ANDROID) || defined(OS_LINUX)
552 kOptionInitialClientFD,
553 #endif // OS_ANDROID || OS_LINUX
554 #if defined(OS_MACOSX)
555 kOptionMachService,
556 #endif // OS_MACOSX
557 kOptionMetrics,
558 kOptionMonitorSelf,
559 kOptionMonitorSelfAnnotation,
560 kOptionMonitorSelfArgument,
561 kOptionNoIdentifyClientViaUrl,
562 kOptionNoPeriodicTasks,
563 kOptionNoRateLimit,
564 kOptionNoUploadGzip,
565 #if defined(OS_ANDROID)
566 kOptionNoWriteMinidumpToDatabase,
567 #endif // OS_ANDROID
568 #if defined(OS_WIN)
569 kOptionPipeName,
570 #endif // OS_WIN
571 #if defined(OS_MACOSX)
572 kOptionResetOwnCrashExceptionPortToSystemDefault,
573 #endif // OS_MACOSX
574 #if defined(OS_LINUX) || defined(OS_ANDROID)
575 kOptionSanitizationInformation,
576 kOptionSharedClientConnection,
577 kOptionTraceParentWithException,
578 #endif
579 kOptionURL,
580 #if defined(OS_CHROMEOS)
581 kOptionUseCrosCrashReporter,
582 kOptionMinidumpDirForTests,
583 kOptionAlwaysAllowFeedback,
584 #endif // OS_CHROMEOS
585 #if defined(OS_ANDROID)
586 kOptionWriteMinidumpToLog,
587 #endif // OS_ANDROID
588
589 // Standard options.
590 kOptionHelp = -2,
591 kOptionVersion = -3,
592 };
593
594 static constexpr option long_options[] = {
595 {"annotation", required_argument, nullptr, kOptionAnnotation},
596 {"database", required_argument, nullptr, kOptionDatabase},
597 #if defined(OS_MACOSX)
598 {"handshake-fd", required_argument, nullptr, kOptionHandshakeFD},
599 #endif // OS_MACOSX
600 #if defined(OS_WIN)
601 {"initial-client-data",
602 required_argument,
603 nullptr,
604 kOptionInitialClientData},
605 #endif // OS_MACOSX
606 #if defined(OS_ANDROID) || defined(OS_LINUX)
607 {"initial-client-fd", required_argument, nullptr, kOptionInitialClientFD},
608 #endif // OS_ANDROID || OS_LINUX
609 #if defined(OS_MACOSX)
610 {"mach-service", required_argument, nullptr, kOptionMachService},
611 #endif // OS_MACOSX
612 {"metrics-dir", required_argument, nullptr, kOptionMetrics},
613 {"monitor-self", no_argument, nullptr, kOptionMonitorSelf},
614 {"monitor-self-annotation",
615 required_argument,
616 nullptr,
617 kOptionMonitorSelfAnnotation},
618 {"monitor-self-argument",
619 required_argument,
620 nullptr,
621 kOptionMonitorSelfArgument},
622 {"no-identify-client-via-url",
623 no_argument,
624 nullptr,
625 kOptionNoIdentifyClientViaUrl},
626 {"no-periodic-tasks", no_argument, nullptr, kOptionNoPeriodicTasks},
627 {"no-rate-limit", no_argument, nullptr, kOptionNoRateLimit},
628 {"no-upload-gzip", no_argument, nullptr, kOptionNoUploadGzip},
629 #if defined(OS_ANDROID)
630 {"no-write-minidump-to-database",
631 no_argument,
632 nullptr,
633 kOptionNoWriteMinidumpToDatabase},
634 #endif // OS_ANDROID
635 #if defined(OS_WIN)
636 {"pipe-name", required_argument, nullptr, kOptionPipeName},
637 #endif // OS_WIN
638 #if defined(OS_MACOSX)
639 {"reset-own-crash-exception-port-to-system-default",
640 no_argument,
641 nullptr,
642 kOptionResetOwnCrashExceptionPortToSystemDefault},
643 #endif // OS_MACOSX
644 #if defined(OS_LINUX) || defined(OS_ANDROID)
645 {"sanitization-information",
646 required_argument,
647 nullptr,
648 kOptionSanitizationInformation},
649 {"shared-client-connection",
650 no_argument,
651 nullptr,
652 kOptionSharedClientConnection},
653 {"trace-parent-with-exception",
654 required_argument,
655 nullptr,
656 kOptionTraceParentWithException},
657 #endif // OS_LINUX || OS_ANDROID
658 {"url", required_argument, nullptr, kOptionURL},
659 #if defined(OS_CHROMEOS)
660 {"use-cros-crash-reporter",
661 no_argument,
662 nullptr,
663 kOptionUseCrosCrashReporter},
664 {"minidump-dir-for-tests",
665 required_argument,
666 nullptr,
667 kOptionMinidumpDirForTests},
668 {"always-allow-feedback",
669 no_argument,
670 nullptr,
671 kOptionAlwaysAllowFeedback},
672 #endif // OS_CHROMEOS
673 #if defined(OS_ANDROID)
674 {"write-minidump-to-log", no_argument, nullptr, kOptionWriteMinidumpToLog},
675 #endif // OS_ANDROID
676 {"help", no_argument, nullptr, kOptionHelp},
677 {"version", no_argument, nullptr, kOptionVersion},
678 {nullptr, 0, nullptr, 0},
679 };
680
681 Options options = {};
682 #if defined(OS_MACOSX)
683 options.handshake_fd = -1;
684 #endif
685 options.identify_client_via_url = true;
686 #if defined(OS_LINUX) || defined(OS_ANDROID)
687 options.initial_client_fd = kInvalidFileHandle;
688 #endif
689 options.periodic_tasks = true;
690 options.rate_limit = true;
691 options.upload_gzip = true;
692 #if defined(OS_ANDROID)
693 options.write_minidump_to_database = true;
694 #endif
695
696 int opt;
697 while ((opt = getopt_long(argc, argv, "", long_options, nullptr)) != -1) {
698 switch (opt) {
699 case kOptionAnnotation: {
700 if (!AddKeyValueToMap(&options.annotations, optarg, "--annotation")) {
701 return ExitFailure();
702 }
703 break;
704 }
705 case kOptionDatabase: {
706 options.database = base::FilePath(
707 ToolSupport::CommandLineArgumentToFilePathStringType(optarg));
708 break;
709 }
710 #if defined(OS_MACOSX)
711 case kOptionHandshakeFD: {
712 if (!StringToNumber(optarg, &options.handshake_fd) ||
713 options.handshake_fd < 0) {
714 ToolSupport::UsageHint(me,
715 "--handshake-fd requires a file descriptor");
716 return ExitFailure();
717 }
718 break;
719 }
720 case kOptionMachService: {
721 options.mach_service = optarg;
722 break;
723 }
724 #endif // OS_MACOSX
725 #if defined(OS_WIN)
726 case kOptionInitialClientData: {
727 if (!options.initial_client_data.InitializeFromString(optarg)) {
728 ToolSupport::UsageHint(
729 me, "failed to parse --initial-client-data");
730 return ExitFailure();
731 }
732 break;
733 }
734 #endif // OS_WIN
735 #if defined(OS_ANDROID) || defined(OS_LINUX)
736 case kOptionInitialClientFD: {
737 if (!base::StringToInt(optarg, &options.initial_client_fd)) {
738 ToolSupport::UsageHint(me, "failed to parse --initial-client-fd");
739 return ExitFailure();
740 }
741 break;
742 }
743 #endif // OS_ANDROID || OS_LINUX
744 case kOptionMetrics: {
745 options.metrics_dir = base::FilePath(
746 ToolSupport::CommandLineArgumentToFilePathStringType(optarg));
747 break;
748 }
749 case kOptionMonitorSelf: {
750 options.monitor_self = true;
751 break;
752 }
753 case kOptionMonitorSelfAnnotation: {
754 if (!AddKeyValueToMap(&options.monitor_self_annotations,
755 optarg,
756 "--monitor-self-annotation")) {
757 return ExitFailure();
758 }
759 break;
760 }
761 case kOptionMonitorSelfArgument: {
762 options.monitor_self_arguments.push_back(optarg);
763 break;
764 }
765 case kOptionNoIdentifyClientViaUrl: {
766 options.identify_client_via_url = false;
767 break;
768 }
769 case kOptionNoPeriodicTasks: {
770 options.periodic_tasks = false;
771 break;
772 }
773 case kOptionNoRateLimit: {
774 options.rate_limit = false;
775 break;
776 }
777 case kOptionNoUploadGzip: {
778 options.upload_gzip = false;
779 break;
780 }
781 #if defined(OS_ANDROID)
782 case kOptionNoWriteMinidumpToDatabase: {
783 options.write_minidump_to_database = false;
784 break;
785 }
786 #endif // OS_ANDROID
787 #if defined(OS_WIN)
788 case kOptionPipeName: {
789 options.pipe_name = optarg;
790 break;
791 }
792 #endif // OS_WIN
793 #if defined(OS_MACOSX)
794 case kOptionResetOwnCrashExceptionPortToSystemDefault: {
795 options.reset_own_crash_exception_port_to_system_default = true;
796 break;
797 }
798 #endif // OS_MACOSX
799 #if defined(OS_LINUX) || defined(OS_ANDROID)
800 case kOptionSanitizationInformation: {
801 if (!StringToNumber(optarg,
802 &options.sanitization_information_address)) {
803 ToolSupport::UsageHint(me,
804 "failed to parse --sanitization-information");
805 return ExitFailure();
806 }
807 break;
808 }
809 case kOptionSharedClientConnection: {
810 options.shared_client_connection = true;
811 break;
812 }
813 case kOptionTraceParentWithException: {
814 if (!StringToNumber(optarg, &options.exception_information_address)) {
815 ToolSupport::UsageHint(
816 me, "failed to parse --trace-parent-with-exception");
817 return ExitFailure();
818 }
819 break;
820 }
821 #endif // OS_LINUX || OS_ANDROID
822 case kOptionURL: {
823 options.url = optarg;
824 break;
825 }
826 #if defined(OS_CHROMEOS)
827 case kOptionUseCrosCrashReporter: {
828 options.use_cros_crash_reporter = true;
829 break;
830 }
831 case kOptionMinidumpDirForTests: {
832 options.minidump_dir_for_tests = base::FilePath(
833 ToolSupport::CommandLineArgumentToFilePathStringType(optarg));
834 break;
835 }
836 case kOptionAlwaysAllowFeedback: {
837 options.always_allow_feedback = true;
838 break;
839 }
840 #endif // OS_CHROMEOS
841 #if defined(OS_ANDROID)
842 case kOptionWriteMinidumpToLog: {
843 options.write_minidump_to_log = true;
844 break;
845 }
846 #endif // OS_ANDROID
847 case kOptionHelp: {
848 Usage(me);
849 MetricsRecordExit(Metrics::LifetimeMilestone::kExitedEarly);
850 return EXIT_SUCCESS;
851 }
852 case kOptionVersion: {
853 ToolSupport::Version(me);
854 MetricsRecordExit(Metrics::LifetimeMilestone::kExitedEarly);
855 return EXIT_SUCCESS;
856 }
857 default: {
858 ToolSupport::UsageHint(me, nullptr);
859 return ExitFailure();
860 }
861 }
862 }
863 argc -= optind;
864 argv += optind;
865
866 #if defined(OS_MACOSX)
867 if (options.handshake_fd < 0 && options.mach_service.empty()) {
868 ToolSupport::UsageHint(me, "--handshake-fd or --mach-service is required");
869 return ExitFailure();
870 }
871 if (options.handshake_fd >= 0 && !options.mach_service.empty()) {
872 ToolSupport::UsageHint(
873 me, "--handshake-fd and --mach-service are incompatible");
874 return ExitFailure();
875 }
876 #elif defined(OS_WIN)
877 if (!options.initial_client_data.IsValid() && options.pipe_name.empty()) {
878 ToolSupport::UsageHint(me,
879 "--initial-client-data or --pipe-name is required");
880 return ExitFailure();
881 }
882 if (options.initial_client_data.IsValid() && !options.pipe_name.empty()) {
883 ToolSupport::UsageHint(
884 me, "--initial-client-data and --pipe-name are incompatible");
885 return ExitFailure();
886 }
887 #elif defined(OS_LINUX) || defined(OS_ANDROID)
888 if (!options.exception_information_address &&
889 options.initial_client_fd == kInvalidFileHandle) {
890 ToolSupport::UsageHint(
891 me, "--trace-parent-with-exception or --initial-client-fd is required");
892 return ExitFailure();
893 }
894 if (options.sanitization_information_address &&
895 !options.exception_information_address) {
896 ToolSupport::UsageHint(
897 me,
898 "--sanitization_information requires --trace-parent-with-exception");
899 return ExitFailure();
900 }
901 if (options.shared_client_connection &&
902 options.initial_client_fd == kInvalidFileHandle) {
903 ToolSupport::UsageHint(
904 me, "--shared-client-connection requires --initial-client-fd");
905 return ExitFailure();
906 }
907 #if defined(OS_ANDROID)
908 if (!options.write_minidump_to_log && !options.write_minidump_to_database) {
909 ToolSupport::UsageHint(me,
910 "--no_write_minidump_to_database is required to use "
911 "with --write_minidump_to_log.");
912 ExitFailure();
913 }
914 #endif // OS_ANDROID
915 #endif // OS_MACOSX
916
917 if (options.database.empty()) {
918 ToolSupport::UsageHint(me, "--database is required");
919 return ExitFailure();
920 }
921
922 if (argc) {
923 ToolSupport::UsageHint(me, nullptr);
924 return ExitFailure();
925 }
926
927 #if defined(OS_MACOSX)
928 if (options.reset_own_crash_exception_port_to_system_default) {
929 CrashpadClient::UseSystemDefaultHandler();
930 }
931 #endif // OS_MACOSX
932
933 if (options.monitor_self) {
934 MonitorSelf(options);
935 }
936
937 if (!options.monitor_self_annotations.empty()) {
938 // Establish these annotations even if --monitor-self is not present, in
939 // case something such as generate_dump wants to try to access them later.
940 //
941 // If the handler is part of a multi-purpose executable, simple annotations
942 // may already be present for this module. If they are, use them.
943 CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();
944 SimpleStringDictionary* module_annotations =
945 crashpad_info->simple_annotations();
946 if (!module_annotations) {
947 module_annotations = new SimpleStringDictionary();
948 crashpad_info->set_simple_annotations(module_annotations);
949 }
950
951 for (const auto& iterator : options.monitor_self_annotations) {
952 module_annotations->SetKeyValue(iterator.first.c_str(),
953 iterator.second.c_str());
954 }
955 }
956
957 std::unique_ptr<CrashReportDatabase> database(
958 CrashReportDatabase::Initialize(options.database));
959 if (!database) {
960 return ExitFailure();
961 }
962
963 ScopedStoppable upload_thread;
964 if (!options.url.empty()) {
965 // TODO(scottmg): options.rate_limit should be removed when we have a
966 // configurable database setting to control upload limiting.
967 // See https://crashpad.chromium.org/bug/23.
968 CrashReportUploadThread::Options upload_thread_options;
969 upload_thread_options.identify_client_via_url =
970 options.identify_client_via_url;
971 upload_thread_options.rate_limit = options.rate_limit;
972 upload_thread_options.upload_gzip = options.upload_gzip;
973 upload_thread_options.watch_pending_reports = options.periodic_tasks;
974
975 upload_thread.Reset(new CrashReportUploadThread(
976 database.get(), options.url, upload_thread_options));
977 upload_thread.Get()->Start();
978 }
979
980 #if defined(OS_LINUX) || defined(OS_ANDROID)
981 std::unique_ptr<ExceptionHandlerServer::Delegate> exception_handler;
982 #else
983 std::unique_ptr<CrashReportExceptionHandler> exception_handler;
984 #endif
985
986 #if defined(OS_CHROMEOS)
987 if (options.use_cros_crash_reporter) {
988 auto cros_handler = std::make_unique<CrosCrashReportExceptionHandler>(
989 database.get(),
990 &options.annotations,
991 user_stream_sources);
992
993 if (!options.minidump_dir_for_tests.empty()) {
994 cros_handler->SetDumpDir(options.minidump_dir_for_tests);
995 }
996
997 if (options.always_allow_feedback) {
998 cros_handler->SetAlwaysAllowFeedback();
999 }
1000
1001 exception_handler = std::move(cros_handler);
1002 } else {
1003 exception_handler = std::make_unique<CrashReportExceptionHandler>(
1004 database.get(),
1005 static_cast<CrashReportUploadThread*>(upload_thread.Get()),
1006 &options.annotations,
1007 true,
1008 false,
1009 user_stream_sources);
1010 }
1011 #else
1012 exception_handler = std::make_unique<CrashReportExceptionHandler>(
1013 database.get(),
1014 static_cast<CrashReportUploadThread*>(upload_thread.Get()),
1015 &options.annotations,
1016 #if defined(OS_FUCHSIA)
1017 // TODO(scottmg): Process level file attachments, and for all platforms.
1018 nullptr,
1019 #endif
1020 #if defined(OS_ANDROID)
1021 options.write_minidump_to_database,
1022 options.write_minidump_to_log,
1023 #endif // OS_ANDROID
1024 #if defined(OS_LINUX)
1025 true,
1026 false,
1027 #endif // OS_LINUX
1028 user_stream_sources);
1029 #endif // OS_CHROMEOS
1030
1031 #if defined(OS_LINUX) || defined(OS_ANDROID)
1032 if (options.exception_information_address) {
1033 ExceptionHandlerProtocol::ClientInformation info;
1034 info.exception_information_address = options.exception_information_address;
1035 info.sanitization_information_address =
1036 options.sanitization_information_address;
1037 return exception_handler->HandleException(getppid(), geteuid(), info)
1038 ? EXIT_SUCCESS
1039 : ExitFailure();
1040 }
1041 #endif // OS_LINUX || OS_ANDROID
1042
1043 ScopedStoppable prune_thread;
1044 if (options.periodic_tasks) {
1045 prune_thread.Reset(new PruneCrashReportThread(
1046 database.get(), PruneCondition::GetDefault()));
1047 prune_thread.Get()->Start();
1048 }
1049
1050 #if defined(OS_MACOSX)
1051 if (options.mach_service.empty()) {
1052 // Don’t do this when being run by launchd. See launchd.plist(5).
1053 CloseStdinAndStdout();
1054 }
1055
1056 base::mac::ScopedMachReceiveRight receive_right;
1057
1058 if (options.handshake_fd >= 0) {
1059 receive_right.reset(
1060 ChildPortHandshake::RunServerForFD(
1061 base::ScopedFD(options.handshake_fd),
1062 ChildPortHandshake::PortRightType::kReceiveRight));
1063 } else if (!options.mach_service.empty()) {
1064 receive_right = BootstrapCheckIn(options.mach_service);
1065 }
1066
1067 if (!receive_right.is_valid()) {
1068 return ExitFailure();
1069 }
1070
1071 ExceptionHandlerServer exception_handler_server(
1072 std::move(receive_right), !options.mach_service.empty());
1073 base::AutoReset<ExceptionHandlerServer*> reset_g_exception_handler_server(
1074 &g_exception_handler_server, &exception_handler_server);
1075
1076 struct sigaction old_sigterm_action;
1077 ScopedResetSIGTERM reset_sigterm;
1078 if (!options.mach_service.empty()) {
1079 // When running from launchd, no no-senders notification could ever be
1080 // triggered, because launchd maintains a send right to the service. When
1081 // launchd wants the job to exit, it will send a SIGTERM. See
1082 // launchd.plist(5).
1083 //
1084 // Set up a SIGTERM handler that will call exception_handler_server.Stop().
1085 // This replaces the HandleTerminateSignal handler for SIGTERM.
1086 if (Signals::InstallHandler(
1087 SIGTERM, HandleSIGTERM, 0, &old_sigterm_action)) {
1088 reset_sigterm.reset(&old_sigterm_action);
1089 }
1090 }
1091
1092 RecordFileLimitAnnotation();
1093 #elif defined(OS_WIN)
1094 // Shut down as late as possible relative to programs we're watching.
1095 if (!SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY))
1096 PLOG(ERROR) << "SetProcessShutdownParameters";
1097
1098 ExceptionHandlerServer exception_handler_server(!options.pipe_name.empty());
1099
1100 if (!options.pipe_name.empty()) {
1101 exception_handler_server.SetPipeName(base::UTF8ToUTF16(options.pipe_name));
1102 }
1103 #elif defined(OS_FUCHSIA)
1104 // These handles are logically "moved" into these variables when retrieved by
1105 // zx_take_startup_handle(). Both are given to ExceptionHandlerServer which
1106 // owns them in this process. There is currently no "connect-later" mode on
1107 // Fuchsia, all the binding must be done by the client before starting
1108 // crashpad_handler.
1109 zx::job root_job(zx_take_startup_handle(PA_HND(PA_USER0, 0)));
1110 if (!root_job.is_valid()) {
1111 LOG(ERROR) << "no job handle passed in startup handle 0";
1112 return EXIT_FAILURE;
1113 }
1114
1115 zx::channel exception_channel(zx_take_startup_handle(PA_HND(PA_USER0, 1)));
1116 if (!exception_channel.is_valid()) {
1117 LOG(ERROR) << "no exception channel handle passed in startup handle 1";
1118 return EXIT_FAILURE;
1119 }
1120
1121 ExceptionHandlerServer exception_handler_server(std::move(root_job),
1122 std::move(exception_channel));
1123 #elif defined(OS_LINUX) || defined(OS_ANDROID)
1124 ExceptionHandlerServer exception_handler_server;
1125 #endif // OS_MACOSX
1126
1127 base::GlobalHistogramAllocator* histogram_allocator = nullptr;
1128 if (!options.metrics_dir.empty()) {
1129 static constexpr char kMetricsName[] = "CrashpadMetrics";
1130 constexpr size_t kMetricsFileSize = 1 << 20;
1131 if (base::GlobalHistogramAllocator::CreateWithActiveFileInDir(
1132 options.metrics_dir, kMetricsFileSize, 0, kMetricsName)) {
1133 histogram_allocator = base::GlobalHistogramAllocator::Get();
1134 histogram_allocator->CreateTrackingHistograms(kMetricsName);
1135 }
1136 }
1137
1138 Metrics::HandlerLifetimeMilestone(Metrics::LifetimeMilestone::kStarted);
1139
1140 #if defined(OS_WIN)
1141 if (options.initial_client_data.IsValid()) {
1142 exception_handler_server.InitializeWithInheritedDataForInitialClient(
1143 options.initial_client_data, exception_handler.get());
1144 }
1145 #elif defined(OS_LINUX) || defined(OS_ANDROID)
1146 if (options.initial_client_fd == kInvalidFileHandle ||
1147 !exception_handler_server.InitializeWithClient(
1148 ScopedFileHandle(options.initial_client_fd),
1149 options.shared_client_connection)) {
1150 return ExitFailure();
1151 }
1152 #endif // OS_WIN
1153
1154 exception_handler_server.Run(exception_handler.get());
1155
1156 return EXIT_SUCCESS;
1157 }
1158
1159 } // namespace crashpad
1160