1 // Copyright (c) 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 #include "chrome/common/crash_keys.h" 6 7 #include "base/command_line.h" 8 #include "base/format_macros.h" 9 #include "base/logging.h" 10 #include "base/strings/string_split.h" 11 #include "base/strings/string_util.h" 12 #include "base/strings/stringprintf.h" 13 #include "base/strings/utf_string_conversions.h" 14 15 #if defined(OS_MACOSX) 16 #include "breakpad/src/common/simple_string_dictionary.h" 17 #elif defined(OS_WIN) 18 #include "breakpad/src/client/windows/common/ipc_protocol.h" 19 #elif defined(OS_CHROMEOS) 20 #include "chrome/common/chrome_switches.h" 21 #include "gpu/command_buffer/service/gpu_switches.h" 22 #include "ui/gl/gl_switches.h" 23 #endif 24 25 namespace crash_keys { 26 27 // A small crash key, guaranteed to never be split into multiple pieces. 28 const size_t kSmallSize = 63; 29 30 // A medium crash key, which will be chunked on certain platforms but not 31 // others. Guaranteed to never be more than four chunks. 32 const size_t kMediumSize = kSmallSize * 4; 33 34 // A large crash key, which will be chunked on all platforms. This should be 35 // used sparingly. 36 const size_t kLargeSize = kSmallSize * 16; 37 38 // The maximum lengths specified by breakpad include the trailing NULL, so 39 // the actual length of the string is one less. 40 #if defined(OS_MACOSX) 41 static const size_t kSingleChunkLength = 42 google_breakpad::SimpleStringDictionary::value_size - 1; 43 #elif defined(OS_WIN) 44 static const size_t kSingleChunkLength = 45 google_breakpad::CustomInfoEntry::kValueMaxLength - 1; 46 #else 47 static const size_t kSingleChunkLength = 63; 48 #endif 49 50 // Guarantees for crash key sizes. 51 COMPILE_ASSERT(kSmallSize <= kSingleChunkLength, 52 crash_key_chunk_size_too_small); 53 #if defined(OS_MACOSX) 54 COMPILE_ASSERT(kMediumSize <= kSingleChunkLength, 55 mac_has_medium_size_crash_key_chunks); 56 #endif 57 58 const char kClientId[] = "guid"; 59 60 const char kChannel[] = "channel"; 61 62 const char kActiveURL[] = "url-chunk"; 63 64 const char kFontKeyName[] = "font_key_name"; 65 66 const char kSwitch[] = "switch-%" PRIuS; 67 const char kNumSwitches[] = "num-switches"; 68 69 const char kNumVariations[] = "num-experiments"; 70 const char kVariations[] = "variations"; 71 72 const char kExtensionID[] = "extension-%" PRIuS; 73 const char kNumExtensionsCount[] = "num-extensions"; 74 75 const char kNumberOfViews[] = "num-views"; 76 77 const char kShutdownType[] = "shutdown-type"; 78 79 #if !defined(OS_ANDROID) 80 const char kGPUVendorID[] = "gpu-venid"; 81 const char kGPUDeviceID[] = "gpu-devid"; 82 #endif 83 const char kGPUDriverVersion[] = "gpu-driver"; 84 const char kGPUPixelShaderVersion[] = "gpu-psver"; 85 const char kGPUVertexShaderVersion[] = "gpu-vsver"; 86 #if defined(OS_MACOSX) 87 const char kGPUGLVersion[] = "gpu-glver"; 88 #elif defined(OS_POSIX) 89 const char kGPUVendor[] = "gpu-gl-vendor"; 90 const char kGPURenderer[] = "gpu-gl-renderer"; 91 #endif 92 93 const char kPrinterInfo[] = "prn-info-%" PRIuS; 94 95 #if defined(OS_CHROMEOS) 96 const char kNumberOfUsers[] = "num-users"; 97 #endif 98 99 #if defined(OS_MACOSX) 100 namespace mac { 101 102 const char kFirstNSException[] = "firstexception"; 103 const char kFirstNSExceptionTrace[] = "firstexception_bt"; 104 105 const char kLastNSException[] = "lastexception"; 106 const char kLastNSExceptionTrace[] = "lastexception_bt"; 107 108 const char kNSException[] = "nsexception"; 109 const char kNSExceptionTrace[] = "nsexception_bt"; 110 111 const char kSendAction[] = "sendaction"; 112 113 const char kZombie[] = "zombie"; 114 const char kZombieTrace[] = "zombie_dealloc_bt"; 115 116 } // namespace mac 117 #endif 118 119 size_t RegisterChromeCrashKeys() { 120 // The following keys may be chunked by the underlying crash logging system, 121 // but ultimately constitute a single key-value pair. 122 base::debug::CrashKey fixed_keys[] = { 123 { kClientId, kSmallSize }, 124 { kChannel, kSmallSize }, 125 { kActiveURL, kLargeSize }, 126 { kNumSwitches, kSmallSize }, 127 { kNumVariations, kSmallSize }, 128 { kVariations, kLargeSize }, 129 { kNumExtensionsCount, kSmallSize }, 130 { kNumberOfViews, kSmallSize }, 131 { kShutdownType, kSmallSize }, 132 #if !defined(OS_ANDROID) 133 { kGPUVendorID, kSmallSize }, 134 { kGPUDeviceID, kSmallSize }, 135 #endif 136 { kGPUDriverVersion, kSmallSize }, 137 { kGPUPixelShaderVersion, kSmallSize }, 138 { kGPUVertexShaderVersion, kSmallSize }, 139 #if defined(OS_MACOSX) 140 { kGPUGLVersion, kSmallSize }, 141 #elif defined(OS_POSIX) 142 { kGPUVendor, kSmallSize }, 143 { kGPURenderer, kSmallSize }, 144 #endif 145 146 // base/: 147 { "dm-usage", kSmallSize }, 148 // content/: 149 { kFontKeyName, kSmallSize}, 150 { "ppapi_path", kMediumSize }, 151 { "subresource_url", kLargeSize }, 152 #if defined(OS_CHROMEOS) 153 { kNumberOfUsers, kSmallSize }, 154 #endif 155 #if defined(OS_MACOSX) 156 { mac::kFirstNSException, kMediumSize }, 157 { mac::kFirstNSExceptionTrace, kMediumSize }, 158 { mac::kLastNSException, kMediumSize }, 159 { mac::kLastNSExceptionTrace, kMediumSize }, 160 { mac::kNSException, kMediumSize }, 161 { mac::kNSExceptionTrace, kMediumSize }, 162 { mac::kSendAction, kMediumSize }, 163 { mac::kZombie, kMediumSize }, 164 { mac::kZombieTrace, kMediumSize }, 165 // content/: 166 { "channel_error_bt", kMediumSize }, 167 { "remove_route_bt", kMediumSize }, 168 { "rwhvm_window", kMediumSize }, 169 // media/: 170 { "VideoCaptureDeviceQTKit", kSmallSize }, 171 #endif 172 }; 173 174 // This dynamic set of keys is used for sets of key value pairs when gathering 175 // a collection of data, like command line switches or extension IDs. 176 std::vector<base::debug::CrashKey> keys( 177 fixed_keys, fixed_keys + arraysize(fixed_keys)); 178 179 // Register the switches. 180 { 181 // The fixed_keys names are string constants. Use static storage for 182 // formatted key names as well, since they will persist for the duration of 183 // the program. 184 static char formatted_keys[kSwitchesMaxCount][sizeof(kSwitch) + 1] = 185 {{ 0 }}; 186 const size_t formatted_key_len = sizeof(formatted_keys[0]); 187 for (size_t i = 0; i < kSwitchesMaxCount; ++i) { 188 // Name the keys using 1-based indexing. 189 int n = base::snprintf( 190 formatted_keys[i], formatted_key_len, kSwitch, i + 1); 191 DCHECK_GT(n, 0); 192 base::debug::CrashKey crash_key = { formatted_keys[i], kSmallSize }; 193 keys.push_back(crash_key); 194 } 195 } 196 197 // Register the extension IDs. 198 { 199 static char formatted_keys[kExtensionIDMaxCount][sizeof(kExtensionID) + 1] = 200 {{ 0 }}; 201 const size_t formatted_key_len = sizeof(formatted_keys[0]); 202 for (size_t i = 0; i < kExtensionIDMaxCount; ++i) { 203 int n = base::snprintf( 204 formatted_keys[i], formatted_key_len, kExtensionID, i + 1); 205 DCHECK_GT(n, 0); 206 base::debug::CrashKey crash_key = { formatted_keys[i], kSmallSize }; 207 keys.push_back(crash_key); 208 } 209 } 210 211 // Register the printer info. 212 { 213 static char formatted_keys[kPrinterInfoCount][sizeof(kPrinterInfo) + 1] = 214 {{ 0 }}; 215 const size_t formatted_key_len = sizeof(formatted_keys[0]); 216 for (size_t i = 0; i < kPrinterInfoCount; ++i) { 217 // Key names are 1-indexed. 218 int n = base::snprintf( 219 formatted_keys[i], formatted_key_len, kPrinterInfo, i + 1); 220 DCHECK_GT(n, 0); 221 base::debug::CrashKey crash_key = { formatted_keys[i], kSmallSize }; 222 keys.push_back(crash_key); 223 } 224 } 225 226 return base::debug::InitCrashKeys(&keys.at(0), keys.size(), 227 kSingleChunkLength); 228 } 229 230 void SetCrashClientIdFromGUID(const std::string& client_guid) { 231 std::string stripped_guid(client_guid); 232 // Remove all instance of '-' char from the GUID. So BCD-WXY becomes BCDWXY. 233 ReplaceSubstringsAfterOffset(&stripped_guid, 0, "-", ""); 234 if (stripped_guid.empty()) 235 return; 236 237 base::debug::SetCrashKeyValue(kClientId, stripped_guid); 238 } 239 240 static bool IsBoringSwitch(const std::string& flag) { 241 #if defined(OS_WIN) 242 return StartsWithASCII(flag, "--channel=", true) || 243 244 // No point to including this since we already have a ptype field. 245 StartsWithASCII(flag, "--type=", true) || 246 247 // Not particularly interesting 248 StartsWithASCII(flag, "--flash-broker=", true) || 249 250 // Just about everything has this, don't bother. 251 StartsWithASCII(flag, "/prefetch:", true) || 252 253 // We handle the plugin path separately since it is usually too big 254 // to fit in the switches (limited to 63 characters). 255 StartsWithASCII(flag, "--plugin-path=", true) || 256 257 // This is too big so we end up truncating it anyway. 258 StartsWithASCII(flag, "--force-fieldtrials=", true) || 259 260 // These surround the flags that were added by about:flags, it lets 261 // you distinguish which flags were added manually via the command 262 // line versus those added through about:flags. For the most part 263 // we don't care how an option was enabled, so we strip these. 264 // (If you need to know can always look at the PEB). 265 flag == "--flag-switches-begin" || 266 flag == "--flag-switches-end"; 267 #elif defined(OS_CHROMEOS) 268 static const char* kIgnoreSwitches[] = { 269 ::switches::kEnableImplSidePainting, 270 ::switches::kEnableLogging, 271 ::switches::kFlagSwitchesBegin, 272 ::switches::kFlagSwitchesEnd, 273 ::switches::kLoggingLevel, 274 ::switches::kPpapiFlashArgs, 275 ::switches::kPpapiFlashPath, 276 ::switches::kRegisterPepperPlugins, 277 ::switches::kUIPrioritizeInGpuProcess, 278 ::switches::kUseGL, 279 ::switches::kUserDataDir, 280 ::switches::kV, 281 ::switches::kVModule, 282 // Cros/CC flgas are specified as raw strings to avoid dependency. 283 "ash-default-wallpaper-large", 284 "ash-default-wallpaper-small", 285 "ash-guest-wallpaper-large", 286 "ash-guest-wallpaper-small", 287 "enterprise-enable-forced-re-enrollment", 288 "enterprise-enrollment-initial-modulus", 289 "enterprise-enrollment-modulus-limit", 290 "login-profile", 291 "login-user", 292 "max-tiles-for-interest-area", 293 "max-unused-resource-memory-usage-percentage", 294 "termination-message-file", 295 "use-cras", 296 }; 297 if (!StartsWithASCII(flag, "--", true)) 298 return false; 299 std::size_t end = flag.find("="); 300 int len = (end == std::string::npos) ? flag.length() - 2 : end - 2; 301 for (size_t i = 0; i < arraysize(kIgnoreSwitches); ++i) { 302 if (flag.compare(2, len, kIgnoreSwitches[i]) == 0) 303 return true; 304 } 305 return false; 306 #else 307 return false; 308 #endif 309 } 310 311 void SetSwitchesFromCommandLine(const CommandLine* command_line) { 312 DCHECK(command_line); 313 if (!command_line) 314 return; 315 316 const CommandLine::StringVector& argv = command_line->argv(); 317 318 // Set the number of switches in case size > kNumSwitches. 319 base::debug::SetCrashKeyValue(kNumSwitches, 320 base::StringPrintf("%" PRIuS, argv.size() - 1)); 321 322 size_t key_i = 1; // Key names are 1-indexed. 323 324 // Go through the argv, skipping the exec path. 325 for (size_t i = 1; i < argv.size(); ++i) { 326 #if defined(OS_WIN) 327 std::string switch_str = base::WideToUTF8(argv[i]); 328 #else 329 std::string switch_str = argv[i]; 330 #endif 331 332 // Skip uninteresting switches. 333 if (IsBoringSwitch(switch_str)) 334 continue; 335 336 // Stop if there are too many switches. 337 if (i > crash_keys::kSwitchesMaxCount) 338 break; 339 340 std::string key = base::StringPrintf(kSwitch, key_i++); 341 base::debug::SetCrashKeyValue(key, switch_str); 342 } 343 344 // Clear any remaining switches. 345 for (; key_i <= kSwitchesMaxCount; ++key_i) { 346 base::debug::ClearCrashKey(base::StringPrintf(kSwitch, key_i)); 347 } 348 } 349 350 void SetVariationsList(const std::vector<std::string>& variations) { 351 base::debug::SetCrashKeyValue(kNumVariations, 352 base::StringPrintf("%" PRIuS, variations.size())); 353 354 std::string variations_string; 355 variations_string.reserve(kLargeSize); 356 357 for (size_t i = 0; i < variations.size(); ++i) { 358 const std::string& variation = variations[i]; 359 // Do not truncate an individual experiment. 360 if (variations_string.size() + variation.size() >= kLargeSize) 361 break; 362 variations_string += variation; 363 variations_string += ","; 364 } 365 366 base::debug::SetCrashKeyValue(kVariations, variations_string); 367 } 368 369 void SetActiveExtensions(const std::set<std::string>& extensions) { 370 base::debug::SetCrashKeyValue(kNumExtensionsCount, 371 base::StringPrintf("%" PRIuS, extensions.size())); 372 373 std::set<std::string>::const_iterator it = extensions.begin(); 374 for (size_t i = 0; i < kExtensionIDMaxCount; ++i) { 375 std::string key = base::StringPrintf(kExtensionID, i + 1); 376 if (it == extensions.end()) { 377 base::debug::ClearCrashKey(key); 378 } else { 379 base::debug::SetCrashKeyValue(key, *it); 380 ++it; 381 } 382 } 383 } 384 385 ScopedPrinterInfo::ScopedPrinterInfo(const base::StringPiece& data) { 386 std::vector<std::string> info; 387 base::SplitString(data.as_string(), ';', &info); 388 for (size_t i = 0; i < kPrinterInfoCount; ++i) { 389 std::string key = base::StringPrintf(kPrinterInfo, i + 1); 390 std::string value; 391 if (i < info.size()) 392 value = info[i]; 393 base::debug::SetCrashKeyValue(key, value); 394 } 395 } 396 397 ScopedPrinterInfo::~ScopedPrinterInfo() { 398 for (size_t i = 0; i < kPrinterInfoCount; ++i) { 399 std::string key = base::StringPrintf(kPrinterInfo, i + 1); 400 base::debug::ClearCrashKey(key); 401 } 402 } 403 404 } // namespace crash_keys 405