Ensure low-memory renderers retry failed loads correctly.
[chromium-blink-merge.git] / components / crash / app / crashpad_mac.mm
blobcab4133eb78e6cb94cd47732e51c0961b944b4d3
1 // Copyright 2015 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 #include "components/crash/app/crashpad_mac.h"
7 #include <string.h>
8 #include <unistd.h>
10 #include <algorithm>
11 #include <map>
12 #include <vector>
14 #include "base/auto_reset.h"
15 #include "base/debug/crash_logging.h"
16 #include "base/debug/dump_without_crashing.h"
17 #include "base/files/file_path.h"
18 #include "base/logging.h"
19 #include "base/mac/bundle_locations.h"
20 #include "base/mac/foundation_util.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/string_piece.h"
23 #include "base/strings/stringprintf.h"
24 #include "base/strings/sys_string_conversions.h"
25 #include "components/crash/app/crash_reporter_client.h"
26 #include "third_party/crashpad/crashpad/client/crash_report_database.h"
27 #include "third_party/crashpad/crashpad/client/crashpad_client.h"
28 #include "third_party/crashpad/crashpad/client/crashpad_info.h"
29 #include "third_party/crashpad/crashpad/client/settings.h"
30 #include "third_party/crashpad/crashpad/client/simple_string_dictionary.h"
31 #include "third_party/crashpad/crashpad/client/simulate_crash.h"
33 namespace crash_reporter {
35 namespace {
37 crashpad::SimpleStringDictionary* g_simple_string_dictionary;
38 crashpad::CrashReportDatabase* g_database;
40 void SetCrashKeyValue(const base::StringPiece& key,
41                       const base::StringPiece& value) {
42   g_simple_string_dictionary->SetKeyValue(key.data(), value.data());
45 void ClearCrashKey(const base::StringPiece& key) {
46   g_simple_string_dictionary->RemoveKey(key.data());
49 bool LogMessageHandler(int severity,
50                        const char* file,
51                        int line,
52                        size_t message_start,
53                        const std::string& string) {
54   // Only handle FATAL.
55   if (severity != logging::LOG_FATAL) {
56     return false;
57   }
59   // In case of an out-of-memory condition, this code could be reentered when
60   // constructing and storing the key. Using a static is not thread-safe, but if
61   // multiple threads are in the process of a fatal crash at the same time, this
62   // should work.
63   static bool guarded = false;
64   if (guarded) {
65     return false;
66   }
67   base::AutoReset<bool> guard(&guarded, true);
69   // Only log last path component.  This matches logging.cc.
70   if (file) {
71     const char* slash = strrchr(file, '/');
72     if (slash) {
73       file = slash + 1;
74     }
75   }
77   std::string message = base::StringPrintf("%s:%d: %s", file, line,
78                                            string.c_str() + message_start);
79   SetCrashKeyValue("LOG_FATAL", message);
81   // Rather than including the code to force the crash here, allow the caller to
82   // do it.
83   return false;
86 void DumpWithoutCrashing() {
87   CRASHPAD_SIMULATE_CRASH();
90 }  // namespace
92 void InitializeCrashpad(const std::string& process_type) {
93   static bool initialized = false;
94   DCHECK(!initialized);
95   initialized = true;
97   const bool browser_process = process_type.empty();
98   CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
100   base::FilePath database_path;  // Only valid in the browser process.
102   if (browser_process) {
103     @autoreleasepool {
104       base::FilePath framework_bundle_path = base::mac::FrameworkBundlePath();
105       base::FilePath handler_path =
106           framework_bundle_path.Append("Helpers").Append("crashpad_handler");
108       // Is there a way to recover if this fails?
109       crash_reporter_client->GetCrashDumpLocation(&database_path);
111       // TODO(mark): Reading the Breakpad keys is temporary and transitional. At
112       // the very least, they should be renamed to Crashpad. For the time being,
113       // this isn't the worst thing: Crashpad is still uploading to a
114       // Breakpad-type server, after all.
115       NSBundle* framework_bundle = base::mac::FrameworkBundle();
116       NSString* product = base::mac::ObjCCast<NSString>(
117           [framework_bundle objectForInfoDictionaryKey:@"BreakpadProduct"]);
118       NSString* version = base::mac::ObjCCast<NSString>(
119           [framework_bundle objectForInfoDictionaryKey:@"BreakpadVersion"]);
120       NSString* url_ns = base::mac::ObjCCast<NSString>(
121           [framework_bundle objectForInfoDictionaryKey:@"BreakpadURL"]);
123       std::string url = base::SysNSStringToUTF8(url_ns);
125       std::map<std::string, std::string> process_annotations;
126       process_annotations["prod"] = base::SysNSStringToUTF8(product);
127       process_annotations["ver"] = base::SysNSStringToUTF8(version);
128       process_annotations["plat"] = std::string("OS X");
130       crashpad::CrashpadClient crashpad_client;
131       if (crashpad_client.StartHandler(handler_path, database_path, url,
132                                        process_annotations,
133                                        std::vector<std::string>())) {
134         crashpad_client.UseHandler();
135       }
136     }  // @autoreleasepool
137   }
139   crashpad::CrashpadInfo* crashpad_info =
140       crashpad::CrashpadInfo::GetCrashpadInfo();
142 #if defined(NDEBUG)
143   const bool is_debug_build = false;
144 #else
145   const bool is_debug_build = true;
146 #endif
148   // Disable forwarding to the system's crash reporter in processes other than
149   // the browser process. For the browser, the system's crash reporter presents
150   // the crash UI to the user, so it's desirable there. Additionally, having
151   // crash reports appear in ~/Library/Logs/DiagnosticReports provides a
152   // fallback. Forwarding is turned off for debug-mode builds even for the
153   // browser process, because the system's crash reporter can take a very long
154   // time to chew on symbols.
155   if (!browser_process || is_debug_build) {
156     crashpad_info->set_system_crash_reporter_forwarding(
157         crashpad::TriState::kDisabled);
158   }
160   g_simple_string_dictionary = new crashpad::SimpleStringDictionary();
161   crashpad_info->set_simple_annotations(g_simple_string_dictionary);
163   base::debug::SetCrashKeyReportingFunctions(SetCrashKeyValue, ClearCrashKey);
164   crash_reporter_client->RegisterCrashKeys();
166   SetCrashKeyValue("ptype", browser_process ? base::StringPiece("browser")
167                                             : base::StringPiece(process_type));
168   SetCrashKeyValue("pid", base::IntToString(getpid()));
170   logging::SetLogMessageHandler(LogMessageHandler);
172   // If clients called CRASHPAD_SIMULATE_CRASH() instead of
173   // base::debug::DumpWithoutCrashing(), these dumps would appear as crashes in
174   // the correct function, at the correct file and line. This would be
175   // preferable to having all occurrences show up in DumpWithoutCrashing() at
176   // the same file and line.
177   base::debug::SetDumpWithoutCrashingFunction(DumpWithoutCrashing);
179   if (browser_process) {
180     g_database =
181         crashpad::CrashReportDatabase::Initialize(database_path).release();
183     bool enable_uploads = false;
184     if (!crash_reporter_client->ReportingIsEnforcedByPolicy(&enable_uploads)) {
185       enable_uploads = crash_reporter_client->GetCollectStatsConsent() &&
186                        !crash_reporter_client->IsRunningUnattended();
187       // Breakpad provided a --disable-breakpad switch to disable crash dumping
188       // (not just uploading) here. Crashpad doesn't need it: dumping is enabled
189       // unconditionally and uploading is gated on consent, which tests/bots
190       // shouldn't have. As a precaution, we also force disable uploading on
191       // bots even if consent is present.
192     }
194     SetUploadsEnabled(enable_uploads);
195   }
198 void SetUploadsEnabled(bool enable_uploads) {
199   if (g_database) {
200     crashpad::Settings* settings = g_database->GetSettings();
201     settings->SetUploadsEnabled(enable_uploads);
202   }
205 bool GetUploadsEnabled() {
206   if (g_database) {
207     crashpad::Settings* settings = g_database->GetSettings();
208     bool enable_uploads;
209     if (settings->GetUploadsEnabled(&enable_uploads)) {
210       return enable_uploads;
211     }
212   }
214   return false;
217 void GetUploadedReports(std::vector<UploadedReport>* uploaded_reports) {
218   uploaded_reports->clear();
220   if (!g_database) {
221     return;
222   }
224   std::vector<crashpad::CrashReportDatabase::Report> completed_reports;
225   crashpad::CrashReportDatabase::OperationStatus status =
226       g_database->GetCompletedReports(&completed_reports);
227   if (status != crashpad::CrashReportDatabase::kNoError) {
228     return;
229   }
231   for (const crashpad::CrashReportDatabase::Report& completed_report :
232        completed_reports) {
233     if (completed_report.uploaded) {
234       UploadedReport uploaded_report;
235       uploaded_report.local_id = completed_report.uuid.ToString();
236       uploaded_report.remote_id = completed_report.id;
237       uploaded_report.creation_time = completed_report.creation_time;
239       uploaded_reports->push_back(uploaded_report);
240     }
241   }
243   struct {
244     bool operator()(const UploadedReport& a, const UploadedReport& b) {
245       return a.creation_time >= b.creation_time;
246     }
247   } sort_by_time;
248   std::sort(uploaded_reports->begin(), uploaded_reports->end(), sort_by_time);
251 }  // namespace crash_reporter