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