| OLD | NEW |
| (Empty) | |
| 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. |
| 4 |
| 5 #include "ios/chrome/browser/crash_report/breakpad_helper.h" |
| 6 |
| 7 #import <Foundation/Foundation.h> |
| 8 |
| 9 #include "base/auto_reset.h" |
| 10 #include "base/bind.h" |
| 11 #include "base/debug/crash_logging.h" |
| 12 #include "base/files/file_enumerator.h" |
| 13 #include "base/files/file_path.h" |
| 14 #include "base/files/file_util.h" |
| 15 #include "base/location.h" |
| 16 #include "base/logging.h" |
| 17 #include "base/path_service.h" |
| 18 #include "base/strings/sys_string_conversions.h" |
| 19 #include "ios/chrome/browser/chrome_paths.h" |
| 20 #import "ios/chrome/browser/crash_report/crash_report_user_application_state.h" |
| 21 #include "ios/web/public/web_thread.h" |
| 22 |
| 23 // TODO(stuartmorgan): Move this up where it belongs once |
| 24 // http://code.google.com/p/google-breakpad/issues/detail?id=487 |
| 25 // is fixed. For now, put it at the end to avoid compiler errors. |
| 26 #import "breakpad/src/client/ios/BreakpadController.h" |
| 27 |
| 28 namespace breakpad_helper { |
| 29 |
| 30 namespace { |
| 31 |
| 32 // Key in NSUserDefaults for a Boolean value that stores whether to upload |
| 33 // crash reports. |
| 34 NSString* const kCrashReportsUploadingEnabledKey = |
| 35 @"CrashReportsUploadingEnabled"; |
| 36 |
| 37 NSString* const kCrashedInBackground = @"crashed_in_background"; |
| 38 NSString* const kFreeDiskInKB = @"free_disk_in_kb"; |
| 39 NSString* const kFreeMemoryInKB = @"free_memory_in_kb"; |
| 40 NSString* const kMemoryWarningInProgress = @"memory_warning_in_progress"; |
| 41 NSString* const kMemoryWarningCount = @"memory_warning_count"; |
| 42 NSString* const kUptimeAtRestoreInMs = @"uptime_at_restore_in_ms"; |
| 43 NSString* const kUploadedInRecoveryMode = @"uploaded_in_recovery_mode"; |
| 44 |
| 45 // Multiple state information are combined into one CrachReportMultiParameter |
| 46 // to save limited and finite number of ReportParameters. |
| 47 // These are the values grouped in the user_application_state parameter. |
| 48 NSString* const kDataProxyIsEnabled = @"dataproxy"; |
| 49 NSString* const kOrientationState = @"orient"; |
| 50 NSString* const kSignedIn = @"signIn"; |
| 51 NSString* const kIsShowingPDF = @"pdf"; |
| 52 NSString* const kVideoPlaying = @"avplay"; |
| 53 |
| 54 // Whether the crash reporter is enabled. |
| 55 bool g_crash_reporter_enabled = false; |
| 56 |
| 57 void DeleteAllReportsInDirectory(base::FilePath directory) { |
| 58 base::FileEnumerator enumerator(directory, false, |
| 59 base::FileEnumerator::FILES); |
| 60 base::FilePath cur_file; |
| 61 while (!(cur_file = enumerator.Next()).value().empty()) { |
| 62 if (cur_file.BaseName().value() != kReporterLogFilename) |
| 63 base::DeleteFile(cur_file, false); |
| 64 } |
| 65 } |
| 66 |
| 67 // Callback for base::debug::SetCrashKeyReportingFunctions |
| 68 void SetCrashKeyValueImpl(const base::StringPiece& key, |
| 69 const base::StringPiece& value) { |
| 70 AddReportParameter(base::SysUTF8ToNSString(key.as_string()), |
| 71 base::SysUTF8ToNSString(value.as_string()), true); |
| 72 } |
| 73 |
| 74 // Callback for base::debug::SetCrashKeyReportingFunctions |
| 75 void ClearCrashKeyValueImpl(const base::StringPiece& key) { |
| 76 RemoveReportParameter(base::SysUTF8ToNSString(key.as_string())); |
| 77 } |
| 78 |
| 79 // Callback for logging::SetLogMessageHandler |
| 80 bool FatalMessageHandler(int severity, |
| 81 const char* file, |
| 82 int line, |
| 83 size_t message_start, |
| 84 const std::string& str) { |
| 85 // Do not handle non-FATAL. |
| 86 if (severity != logging::LOG_FATAL) |
| 87 return false; |
| 88 |
| 89 // In case of OOM condition, this code could be reentered when |
| 90 // constructing and storing the key. Using a static is not |
| 91 // thread-safe, but if multiple threads are in the process of a |
| 92 // fatal crash at the same time, this should work. |
| 93 static bool guarded = false; |
| 94 if (guarded) |
| 95 return false; |
| 96 |
| 97 base::AutoReset<bool> guard(&guarded, true); |
| 98 |
| 99 // Only log last path component. This matches logging.cc. |
| 100 if (file) { |
| 101 const char* slash = strrchr(file, '/'); |
| 102 if (slash) |
| 103 file = slash + 1; |
| 104 } |
| 105 |
| 106 NSString* fatal_key = @"LOG_FATAL"; |
| 107 NSString* fatal_value = [NSString |
| 108 stringWithFormat:@"%s:%d: %s", file, line, str.c_str() + message_start]; |
| 109 AddReportParameter(fatal_key, fatal_value, true); |
| 110 |
| 111 // Rather than including the code to force the crash here, allow the |
| 112 // caller to do it. |
| 113 return false; |
| 114 } |
| 115 |
| 116 // Caches the uploading flag in NSUserDefaults, so that we can access the value |
| 117 // in safe mode. |
| 118 void CacheUploadingEnabled(bool uploading_enabled) { |
| 119 NSUserDefaults* user_defaults = [NSUserDefaults standardUserDefaults]; |
| 120 [user_defaults setBool:uploading_enabled ? YES : NO |
| 121 forKey:kCrashReportsUploadingEnabledKey]; |
| 122 } |
| 123 |
| 124 } // namespace |
| 125 |
| 126 void Start(const std::string& channel_name) { |
| 127 DCHECK(!g_crash_reporter_enabled); |
| 128 [[BreakpadController sharedInstance] start:YES]; |
| 129 base::debug::SetCrashKeyReportingFunctions(&SetCrashKeyValueImpl, |
| 130 &ClearCrashKeyValueImpl); |
| 131 logging::SetLogMessageHandler(&FatalMessageHandler); |
| 132 g_crash_reporter_enabled = true; |
| 133 // Register channel information. |
| 134 if (channel_name.length()) { |
| 135 AddReportParameter(@"channel", base::SysUTF8ToNSString(channel_name), true); |
| 136 } |
| 137 // Notifying the PathService on the location of the crashes so that crashes |
| 138 // can be displayed to the user on the about:crashes page. |
| 139 NSArray* cachesDirectories = NSSearchPathForDirectoriesInDomains( |
| 140 NSCachesDirectory, NSUserDomainMask, YES); |
| 141 NSString* cachePath = [cachesDirectories objectAtIndex:0]; |
| 142 NSString* dumpDirectory = |
| 143 [cachePath stringByAppendingPathComponent:@kDefaultLibrarySubdirectory]; |
| 144 PathService::Override(ios::DIR_CRASH_DUMPS, |
| 145 base::FilePath(base::SysNSStringToUTF8(dumpDirectory))); |
| 146 } |
| 147 |
| 148 void SetEnabled(bool enabled) { |
| 149 if (g_crash_reporter_enabled == enabled) |
| 150 return; |
| 151 g_crash_reporter_enabled = enabled; |
| 152 if (g_crash_reporter_enabled) { |
| 153 [[BreakpadController sharedInstance] start:NO]; |
| 154 } else { |
| 155 [[BreakpadController sharedInstance] stop]; |
| 156 CacheUploadingEnabled(false); |
| 157 } |
| 158 } |
| 159 |
| 160 void SetUploadingEnabled(bool enabled) { |
| 161 CacheUploadingEnabled(g_crash_reporter_enabled && enabled); |
| 162 |
| 163 if (!g_crash_reporter_enabled) |
| 164 return; |
| 165 [[BreakpadController sharedInstance] setUploadingEnabled:enabled]; |
| 166 } |
| 167 |
| 168 bool IsUploadingEnabled() { |
| 169 // Return the value cached by CacheUploadingEnabled(). |
| 170 return [[NSUserDefaults standardUserDefaults] |
| 171 boolForKey:kCrashReportsUploadingEnabledKey]; |
| 172 } |
| 173 |
| 174 void CleanupCrashReports() { |
| 175 base::FilePath crash_directory; |
| 176 PathService::Get(ios::DIR_CRASH_DUMPS, &crash_directory); |
| 177 web::WebThread::PostBlockingPoolTask( |
| 178 FROM_HERE, base::Bind(&DeleteAllReportsInDirectory, crash_directory)); |
| 179 } |
| 180 |
| 181 void AddReportParameter(NSString* key, NSString* value, bool async) { |
| 182 if (!g_crash_reporter_enabled) |
| 183 return; |
| 184 if (async) { |
| 185 [[BreakpadController sharedInstance] addUploadParameter:value forKey:key]; |
| 186 return; |
| 187 } |
| 188 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); |
| 189 [[BreakpadController sharedInstance] withBreakpadRef:^(BreakpadRef ref) { |
| 190 if (ref) |
| 191 BreakpadAddUploadParameter(ref, key, value); |
| 192 dispatch_semaphore_signal(semaphore); |
| 193 }]; |
| 194 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); |
| 195 dispatch_release(semaphore); |
| 196 } |
| 197 |
| 198 int GetCrashReportCount() { |
| 199 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); |
| 200 __block int outerCrashReportCount = 0; |
| 201 [[BreakpadController sharedInstance] getCrashReportCount:^(int count) { |
| 202 outerCrashReportCount = count; |
| 203 dispatch_semaphore_signal(semaphore); |
| 204 }]; |
| 205 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); |
| 206 dispatch_release(semaphore); |
| 207 return outerCrashReportCount; |
| 208 } |
| 209 |
| 210 void GetCrashReportCount(void (^callback)(int)) { |
| 211 [[BreakpadController sharedInstance] getCrashReportCount:callback]; |
| 212 } |
| 213 |
| 214 bool HasReportToUpload() { |
| 215 return GetCrashReportCount() > 0; |
| 216 } |
| 217 |
| 218 void RemoveReportParameter(NSString* key) { |
| 219 if (!g_crash_reporter_enabled) |
| 220 return; |
| 221 [[BreakpadController sharedInstance] removeUploadParameterForKey:key]; |
| 222 } |
| 223 |
| 224 void SetCurrentlyInBackground(bool background) { |
| 225 if (background) |
| 226 AddReportParameter(kCrashedInBackground, @"yes", true); |
| 227 else |
| 228 RemoveReportParameter(kCrashedInBackground); |
| 229 } |
| 230 |
| 231 void SetMemoryWarningCount(int count) { |
| 232 if (count) { |
| 233 AddReportParameter(kMemoryWarningCount, |
| 234 [NSString stringWithFormat:@"%d", count], true); |
| 235 } else { |
| 236 RemoveReportParameter(kMemoryWarningCount); |
| 237 } |
| 238 } |
| 239 |
| 240 void SetMemoryWarningInProgress(bool value) { |
| 241 if (value) |
| 242 AddReportParameter(kMemoryWarningInProgress, @"yes", true); |
| 243 else |
| 244 RemoveReportParameter(kMemoryWarningInProgress); |
| 245 } |
| 246 |
| 247 void SetCurrentFreeMemoryInKB(int value) { |
| 248 AddReportParameter(kFreeMemoryInKB, [NSString stringWithFormat:@"%d", value], |
| 249 true); |
| 250 } |
| 251 |
| 252 void SetCurrentFreeDiskInKB(int value) { |
| 253 AddReportParameter(kFreeDiskInKB, [NSString stringWithFormat:@"%d", value], |
| 254 true); |
| 255 } |
| 256 |
| 257 void SetCurrentTabIsPDF(bool value) { |
| 258 if (value) { |
| 259 [[CrashReportUserApplicationState sharedInstance] |
| 260 incrementValue:kIsShowingPDF]; |
| 261 } else { |
| 262 [[CrashReportUserApplicationState sharedInstance] |
| 263 decrementValue:kIsShowingPDF]; |
| 264 } |
| 265 } |
| 266 |
| 267 void SetCurrentOrientation(int statusBarOrientation, int deviceOrientation) { |
| 268 DCHECK((statusBarOrientation < 10) && (deviceOrientation < 10)); |
| 269 int deviceAndUIOrientation = 10 * statusBarOrientation + deviceOrientation; |
| 270 [[CrashReportUserApplicationState sharedInstance] |
| 271 setValue:kOrientationState |
| 272 withValue:deviceAndUIOrientation]; |
| 273 } |
| 274 |
| 275 void SetCurrentlySignedIn(bool signedIn) { |
| 276 if (signedIn) { |
| 277 [[CrashReportUserApplicationState sharedInstance] setValue:kSignedIn |
| 278 withValue:1]; |
| 279 } else { |
| 280 [[CrashReportUserApplicationState sharedInstance] removeValue:kSignedIn]; |
| 281 } |
| 282 } |
| 283 |
| 284 void SetDataReductionProxyIsEnabled(bool value) { |
| 285 if (value) { |
| 286 [[CrashReportUserApplicationState sharedInstance] |
| 287 setValue:kDataProxyIsEnabled |
| 288 withValue:value]; |
| 289 } else { |
| 290 [[CrashReportUserApplicationState sharedInstance] |
| 291 removeValue:kDataProxyIsEnabled]; |
| 292 } |
| 293 } |
| 294 |
| 295 void MediaStreamPlaybackDidStart() { |
| 296 [[CrashReportUserApplicationState sharedInstance] |
| 297 incrementValue:kVideoPlaying]; |
| 298 } |
| 299 |
| 300 void MediaStreamPlaybackDidStop() { |
| 301 [[CrashReportUserApplicationState sharedInstance] |
| 302 decrementValue:kVideoPlaying]; |
| 303 } |
| 304 |
| 305 // Records the current process uptime in the "uptime_at_restore_in_ms". This |
| 306 // will allow engineers to dremel crash logs to find crash whose delta between |
| 307 // process uptime at crash and process uptime at restore is smaller than X |
| 308 // seconds and find insta-crashers. |
| 309 void WillStartCrashRestoration() { |
| 310 if (!g_crash_reporter_enabled) |
| 311 return; |
| 312 // We use gettimeofday and BREAKPAD_PROCESS_START_TIME to compute the |
| 313 // uptime to stay as close as possible as how breakpad computes the |
| 314 // "ProcessUptime" in order to have meaningful comparison in dremel. |
| 315 struct timeval tv; |
| 316 gettimeofday(&tv, NULL); |
| 317 // The values stored in the breakpad log are only accessible through a |
| 318 // BreakpadRef. To record the process uptime at restore, the value of |
| 319 // BREAKPAD_PROCESS_START_TIME is required to compute the delta. |
| 320 [[BreakpadController sharedInstance] withBreakpadRef:^(BreakpadRef ref) { |
| 321 if (!ref) |
| 322 return; |
| 323 NSString* processStartTimeSecondsString = |
| 324 BreakpadKeyValue(ref, @BREAKPAD_PROCESS_START_TIME); |
| 325 if (!processStartTimeSecondsString) |
| 326 return; |
| 327 |
| 328 time_t processStartTimeSeconds = |
| 329 [processStartTimeSecondsString longLongValue]; |
| 330 time_t processUptimeSeconds = tv.tv_sec - processStartTimeSeconds; |
| 331 unsigned long long processUptimeMilliseconds = |
| 332 static_cast<unsigned long long>(processUptimeSeconds) * |
| 333 base::Time::kMillisecondsPerSecond; |
| 334 BreakpadAddUploadParameter( |
| 335 ref, kUptimeAtRestoreInMs, |
| 336 [NSString stringWithFormat:@"%llu", processUptimeMilliseconds]); |
| 337 }]; |
| 338 } |
| 339 |
| 340 void StartUploadingReportsInRecoveryMode() { |
| 341 if (!g_crash_reporter_enabled) |
| 342 return; |
| 343 [[BreakpadController sharedInstance] stop]; |
| 344 [[BreakpadController sharedInstance] setParametersToAddAtUploadTime:@{ |
| 345 kUploadedInRecoveryMode : @"yes" |
| 346 }]; |
| 347 [[BreakpadController sharedInstance] setUploadInterval:1]; |
| 348 [[BreakpadController sharedInstance] start:NO]; |
| 349 [[BreakpadController sharedInstance] setUploadingEnabled:YES]; |
| 350 } |
| 351 |
| 352 void RestoreDefaultConfiguration() { |
| 353 if (!g_crash_reporter_enabled) |
| 354 return; |
| 355 [[BreakpadController sharedInstance] stop]; |
| 356 [[BreakpadController sharedInstance] resetConfiguration]; |
| 357 [[BreakpadController sharedInstance] start:NO]; |
| 358 [[BreakpadController sharedInstance] setUploadingEnabled:NO]; |
| 359 } |
| 360 |
| 361 } // namespace breakpad_helper |
| OLD | NEW |