Allow the externalfile scheme to be whitelisted as an allowed scheme for component...
[chromium-blink-merge.git] / components / crash / app / breakpad_mac.mm
blobdeb7524d1655a04874765090ef4b235c0f273def
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import "components/crash/app/breakpad_mac.h"
7 #include <CoreFoundation/CoreFoundation.h>
8 #import <Foundation/Foundation.h>
10 #include "base/auto_reset.h"
11 #include "base/base_switches.h"
12 #import "base/basictypes.h"
13 #include "base/command_line.h"
14 #include "base/debug/crash_logging.h"
15 #include "base/debug/dump_without_crashing.h"
16 #include "base/files/file_path.h"
17 #include "base/files/file_util.h"
18 #import "base/logging.h"
19 #include "base/mac/bundle_locations.h"
20 #include "base/mac/foundation_util.h"
21 #include "base/mac/mac_util.h"
22 #include "base/mac/scoped_cftyperef.h"
23 #import "base/mac/scoped_nsautorelease_pool.h"
24 #include "base/strings/sys_string_conversions.h"
25 #include "base/threading/platform_thread.h"
26 #include "base/threading/thread_restrictions.h"
27 #import "breakpad/src/client/mac/Framework/Breakpad.h"
28 #include "components/crash/app/crash_reporter_client.h"
30 using crash_reporter::GetCrashReporterClient;
32 namespace breakpad {
34 namespace {
36 BreakpadRef gBreakpadRef = NULL;
38 void SetCrashKeyValue(NSString* key, NSString* value) {
39   // Comment repeated from header to prevent confusion:
40   // IMPORTANT: On OS X, the key/value pairs are sent to the crash server
41   // out of bounds and not recorded on disk in the minidump, this means
42   // that if you look at the minidump file locally you won't see them!
43   if (gBreakpadRef == NULL) {
44     return;
45   }
47   BreakpadAddUploadParameter(gBreakpadRef, key, value);
50 void ClearCrashKeyValue(NSString* key) {
51   if (gBreakpadRef == NULL) {
52     return;
53   }
55   BreakpadRemoveUploadParameter(gBreakpadRef, key);
58 void SetCrashKeyValueImpl(const base::StringPiece& key,
59                           const base::StringPiece& value) {
60   @autoreleasepool {
61     SetCrashKeyValue(base::SysUTF8ToNSString(key.as_string()),
62                      base::SysUTF8ToNSString(value.as_string()));
63   }
66 void ClearCrashKeyValueImpl(const base::StringPiece& key) {
67   @autoreleasepool {
68     ClearCrashKeyValue(base::SysUTF8ToNSString(key.as_string()));
69   }
72 bool FatalMessageHandler(int severity, const char* file, int line,
73                          size_t message_start, const std::string& str) {
74   // Do not handle non-FATAL.
75   if (severity != logging::LOG_FATAL)
76     return false;
78   // In case of OOM condition, this code could be reentered when
79   // constructing and storing the key.  Using a static is not
80   // thread-safe, but if multiple threads are in the process of a
81   // fatal crash at the same time, this should work.
82   static bool guarded = false;
83   if (guarded)
84     return false;
86   base::AutoReset<bool> guard(&guarded, true);
88   // Only log last path component.  This matches logging.cc.
89   if (file) {
90     const char* slash = strrchr(file, '/');
91     if (slash)
92       file = slash + 1;
93   }
95   NSString* fatal_key = @"LOG_FATAL";
96   NSString* fatal_value =
97       [NSString stringWithFormat:@"%s:%d: %s",
98                                  file, line, str.c_str() + message_start];
99   SetCrashKeyValue(fatal_key, fatal_value);
101   // Rather than including the code to force the crash here, allow the
102   // caller to do it.
103   return false;
106 // BreakpadGenerateAndSendReport() does not report the current
107 // thread.  This class can be used to spin up a thread to run it.
108 class DumpHelper : public base::PlatformThread::Delegate {
109  public:
110   static void DumpWithoutCrashing() {
111     DumpHelper dumper;
112     base::PlatformThreadHandle handle;
113     if (base::PlatformThread::Create(0, &dumper, &handle)) {
114       // The entire point of this is to block so that the correct
115       // stack is logged.
116       base::ThreadRestrictions::ScopedAllowIO allow_io;
117       base::PlatformThread::Join(handle);
118     }
119   }
121  private:
122   DumpHelper() {}
124   void ThreadMain() override {
125     base::PlatformThread::SetName("CrDumpHelper");
126     BreakpadGenerateAndSendReport(gBreakpadRef);
127   }
129   DISALLOW_COPY_AND_ASSIGN(DumpHelper);
132 void SIGABRTHandler(int signal) {
133   // The OSX abort() (link below) masks all signals for the process,
134   // and all except SIGABRT for the thread.  SIGABRT will be masked
135   // when the SIGABRT is sent, which means at this point only SIGKILL
136   // and SIGSTOP can be delivered.  Unmask others so that the code
137   // below crashes as desired.
138   //
139   // http://www.opensource.apple.com/source/Libc/Libc-825.26/stdlib/FreeBSD/abort.c
140   sigset_t mask;
141   sigemptyset(&mask);
142   sigaddset(&mask, signal);
143   pthread_sigmask(SIG_SETMASK, &mask, NULL);
145   // Most interesting operations are not safe in a signal handler, just crash.
146   char* volatile death_ptr = NULL;
147   *death_ptr = '!';
150 }  // namespace
152 bool IsCrashReporterEnabled() {
153   return gBreakpadRef != NULL;
156 // Only called for a branded build of Chrome.app.
157 void InitCrashReporter(const std::string& process_type) {
158   DCHECK(!gBreakpadRef);
159   base::mac::ScopedNSAutoreleasePool autorelease_pool;
161   // Check whether crash reporting should be enabled. If enterprise
162   // configuration management controls crash reporting, it takes precedence.
163   // Otherwise, check whether the user has consented to stats and crash
164   // reporting. The browser process can make this determination directly.
165   // Helper processes may not have access to the disk or to the same data as
166   // the browser process, so the browser passes the decision to them on the
167   // command line.
168   NSBundle* main_bundle = base::mac::FrameworkBundle();
169   bool is_browser = !base::mac::IsBackgroundOnlyProcess();
170   bool enable_breakpad = false;
171   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
173   if (is_browser) {
174     // Since the configuration management infrastructure is possibly not
175     // initialized when this code runs, read the policy preference directly.
176     if (!GetCrashReporterClient()->ReportingIsEnforcedByPolicy(
177             &enable_breakpad)) {
178       // Controlled by the user. The crash reporter may be enabled by
179       // preference or through an environment variable, but the kDisableBreakpad
180       // switch overrides both.
181       enable_breakpad = GetCrashReporterClient()->GetCollectStatsConsent() ||
182                         GetCrashReporterClient()->IsRunningUnattended();
183       enable_breakpad &= !command_line->HasSwitch(switches::kDisableBreakpad);
184     }
185   } else {
186     // This is a helper process, check the command line switch.
187     enable_breakpad = command_line->HasSwitch(switches::kEnableCrashReporter);
188   }
190   if (!enable_breakpad) {
191     VLOG_IF(1, is_browser) << "Breakpad disabled";
192     return;
193   }
195   // Tell Breakpad where crash_inspector and crash_report_sender are.
196   NSString* resource_path = [main_bundle resourcePath];
197   NSString *inspector_location =
198       [resource_path stringByAppendingPathComponent:@"crash_inspector"];
199   NSString *reporter_bundle_location =
200       [resource_path stringByAppendingPathComponent:@"crash_report_sender.app"];
201   NSString *reporter_location =
202       [[NSBundle bundleWithPath:reporter_bundle_location] executablePath];
204   if (!inspector_location || !reporter_location) {
205     VLOG_IF(1, is_browser && base::mac::AmIBundled()) << "Breakpad disabled";
206     return;
207   }
209   NSDictionary* info_dictionary = [main_bundle infoDictionary];
210   NSMutableDictionary *breakpad_config =
211       [[info_dictionary mutableCopy] autorelease];
212   [breakpad_config setObject:inspector_location
213                       forKey:@BREAKPAD_INSPECTOR_LOCATION];
214   [breakpad_config setObject:reporter_location
215                       forKey:@BREAKPAD_REPORTER_EXE_LOCATION];
217   // In the main application (the browser process), crashes can be passed to
218   // the system's Crash Reporter.  This allows the system to notify the user
219   // when the application crashes, and provide the user with the option to
220   // restart it.
221   if (is_browser)
222     [breakpad_config setObject:@"NO" forKey:@BREAKPAD_SEND_AND_EXIT];
224   base::FilePath dir_crash_dumps;
225   GetCrashReporterClient()->GetCrashDumpLocation(&dir_crash_dumps);
226   [breakpad_config setObject:base::SysUTF8ToNSString(dir_crash_dumps.value())
227                       forKey:@BREAKPAD_DUMP_DIRECTORY];
229   // Temporarily run Breakpad in-process on 10.10 and later because APIs that
230   // it depends on got broken (http://crbug.com/386208).
231   // This can catch crashes in the browser process only.
232   if (is_browser && base::mac::IsOSYosemiteOrLater()) {
233     [breakpad_config setObject:[NSNumber numberWithBool:YES]
234                         forKey:@BREAKPAD_IN_PROCESS];
235   }
237   // Initialize Breakpad.
238   gBreakpadRef = BreakpadCreate(breakpad_config);
239   if (!gBreakpadRef) {
240     LOG_IF(ERROR, base::mac::AmIBundled()) << "Breakpad initialization failed";
241     return;
242   }
244   // Initialize the scoped crash key system.
245   base::debug::SetCrashKeyReportingFunctions(&SetCrashKeyValueImpl,
246                                              &ClearCrashKeyValueImpl);
247   GetCrashReporterClient()->RegisterCrashKeys();
249   // Set Breakpad metadata values.  These values are added to Info.plist during
250   // the branded Google Chrome.app build.
251   SetCrashKeyValue(@"ver", [info_dictionary objectForKey:@BREAKPAD_VERSION]);
252   SetCrashKeyValue(@"prod", [info_dictionary objectForKey:@BREAKPAD_PRODUCT]);
253   SetCrashKeyValue(@"plat", @"OS X");
255   if (!is_browser) {
256     // Get the guid from the command line switch.
257     std::string client_guid =
258         command_line->GetSwitchValueASCII(switches::kEnableCrashReporter);
259     GetCrashReporterClient()->SetCrashReporterClientIdFromGUID(client_guid);
260   }
262   logging::SetLogMessageHandler(&FatalMessageHandler);
263   base::debug::SetDumpWithoutCrashingFunction(&DumpHelper::DumpWithoutCrashing);
265   // abort() sends SIGABRT, which breakpad does not intercept.
266   // Register a signal handler to crash in a way breakpad will
267   // intercept.
268   struct sigaction sigact;
269   memset(&sigact, 0, sizeof(sigact));
270   sigact.sa_handler = SIGABRTHandler;
271   CHECK(0 == sigaction(SIGABRT, &sigact, NULL));
274 void InitCrashProcessInfo(const std::string& process_type_switch) {
275   if (gBreakpadRef == NULL) {
276     return;
277   }
279   // Determine the process type.
280   NSString* process_type = @"browser";
281   if (!process_type_switch.empty()) {
282     process_type = base::SysUTF8ToNSString(process_type_switch);
283   }
285   GetCrashReporterClient()->InstallAdditionalFilters(gBreakpadRef);
287   // Store process type in crash dump.
288   SetCrashKeyValue(@"ptype", process_type);
290   NSString* pid_value =
291       [NSString stringWithFormat:@"%d", static_cast<unsigned int>(getpid())];
292   SetCrashKeyValue(@"pid", pid_value);
295 }  // namespace breakpad