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 25namespace crash_keys { 26 27// A small crash key, guaranteed to never be split into multiple pieces. 28const 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. 32const size_t kMediumSize = kSmallSize * 4; 33 34// A large crash key, which will be chunked on all platforms. This should be 35// used sparingly. 36const 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) 41static const size_t kSingleChunkLength = 42 google_breakpad::SimpleStringDictionary::value_size - 1; 43#elif defined(OS_WIN) 44static const size_t kSingleChunkLength = 45 google_breakpad::CustomInfoEntry::kValueMaxLength - 1; 46#else 47static const size_t kSingleChunkLength = 63; 48#endif 49 50// Guarantees for crash key sizes. 51COMPILE_ASSERT(kSmallSize <= kSingleChunkLength, 52 crash_key_chunk_size_too_small); 53#if defined(OS_MACOSX) 54COMPILE_ASSERT(kMediumSize <= kSingleChunkLength, 55 mac_has_medium_size_crash_key_chunks); 56#endif 57 58const char kClientId[] = "guid"; 59 60const char kChannel[] = "channel"; 61 62const char kActiveURL[] = "url-chunk"; 63 64const char kFontKeyName[] = "font_key_name"; 65 66const char kSwitch[] = "switch-%" PRIuS; 67const char kNumSwitches[] = "num-switches"; 68 69const char kNumVariations[] = "num-experiments"; 70const char kVariations[] = "variations"; 71 72const char kExtensionID[] = "extension-%" PRIuS; 73const char kNumExtensionsCount[] = "num-extensions"; 74 75const char kNumberOfViews[] = "num-views"; 76 77const char kShutdownType[] = "shutdown-type"; 78 79#if !defined(OS_ANDROID) 80const char kGPUVendorID[] = "gpu-venid"; 81const char kGPUDeviceID[] = "gpu-devid"; 82#endif 83const char kGPUDriverVersion[] = "gpu-driver"; 84const char kGPUPixelShaderVersion[] = "gpu-psver"; 85const char kGPUVertexShaderVersion[] = "gpu-vsver"; 86#if defined(OS_MACOSX) 87const char kGPUGLVersion[] = "gpu-glver"; 88#elif defined(OS_POSIX) 89const char kGPUVendor[] = "gpu-gl-vendor"; 90const char kGPURenderer[] = "gpu-gl-renderer"; 91#endif 92 93const char kPrinterInfo[] = "prn-info-%" PRIuS; 94 95#if defined(OS_CHROMEOS) 96const char kNumberOfUsers[] = "num-users"; 97#endif 98 99#if defined(OS_MACOSX) 100namespace mac { 101 102const char kFirstNSException[] = "firstexception"; 103const char kFirstNSExceptionTrace[] = "firstexception_bt"; 104 105const char kLastNSException[] = "lastexception"; 106const char kLastNSExceptionTrace[] = "lastexception_bt"; 107 108const char kNSException[] = "nsexception"; 109const char kNSExceptionTrace[] = "nsexception_bt"; 110 111const char kSendAction[] = "sendaction"; 112 113const char kZombie[] = "zombie"; 114const char kZombieTrace[] = "zombie_dealloc_bt"; 115 116} // namespace mac 117#endif 118 119size_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 230void 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 240static 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 311void 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 350void 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 369void 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 385ScopedPrinterInfo::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 397ScopedPrinterInfo::~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