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 #include "chrome/installer/util/google_update_settings.h" 15 16 #if defined(OS_MACOSX) 17 #include "breakpad/src/common/simple_string_dictionary.h" 18 #elif defined(OS_WIN) 19 #include "breakpad/src/client/windows/common/ipc_protocol.h" 20 #elif defined(OS_CHROMEOS) 21 #include "chrome/common/chrome_switches.h" 22 #include "gpu/command_buffer/service/gpu_switches.h" 23 #include "ui/gl/gl_switches.h" 24 #endif 25 26 namespace crash_keys { 27 28 // A small crash key, guaranteed to never be split into multiple pieces. 29 const size_t kSmallSize = 63; 30 31 // A medium crash key, which will be chunked on certain platforms but not 32 // others. Guaranteed to never be more than four chunks. 33 const size_t kMediumSize = kSmallSize * 4; 34 35 // A large crash key, which will be chunked on all platforms. This should be 36 // used sparingly. 37 const size_t kLargeSize = kSmallSize * 16; 38 39 // The maximum lengths specified by breakpad include the trailing NULL, so 40 // the actual length of the string is one less. 41 #if defined(OS_MACOSX) 42 static const size_t kSingleChunkLength = 43 google_breakpad::SimpleStringDictionary::value_size - 1; 44 #elif defined(OS_WIN) 45 static const size_t kSingleChunkLength = 46 google_breakpad::CustomInfoEntry::kValueMaxLength - 1; 47 #else 48 static const size_t kSingleChunkLength = 63; 49 #endif 50 51 // Guarantees for crash key sizes. 52 COMPILE_ASSERT(kSmallSize <= kSingleChunkLength, 53 crash_key_chunk_size_too_small); 54 #if defined(OS_MACOSX) 55 COMPILE_ASSERT(kMediumSize <= kSingleChunkLength, 56 mac_has_medium_size_crash_key_chunks); 57 #endif 58 59 const char kClientID[] = "guid"; 60 61 const char kChannel[] = "channel"; 62 63 const char kActiveURL[] = "url-chunk"; 64 65 const char kSwitch[] = "switch-%" PRIuS; 66 const char kNumSwitches[] = "num-switches"; 67 68 const char kNumVariations[] = "num-experiments"; 69 const char kVariations[] = "variations"; 70 71 const char kExtensionID[] = "extension-%" PRIuS; 72 const char kNumExtensionsCount[] = "num-extensions"; 73 74 const char kNumberOfViews[] = "num-views"; 75 76 const char kShutdownType[] = "shutdown-type"; 77 78 #if !defined(OS_ANDROID) 79 const char kGPUVendorID[] = "gpu-venid"; 80 const char kGPUDeviceID[] = "gpu-devid"; 81 #endif 82 const char kGPUDriverVersion[] = "gpu-driver"; 83 const char kGPUPixelShaderVersion[] = "gpu-psver"; 84 const char kGPUVertexShaderVersion[] = "gpu-vsver"; 85 #if defined(OS_MACOSX) 86 const char kGPUGLVersion[] = "gpu-glver"; 87 #elif defined(OS_POSIX) 88 const char kGPUVendor[] = "gpu-gl-vendor"; 89 const char kGPURenderer[] = "gpu-gl-renderer"; 90 #endif 91 92 const char kPrinterInfo[] = "prn-info-%" PRIuS; 93 94 #if defined(OS_CHROMEOS) 95 const char kNumberOfUsers[] = "num-users"; 96 #endif 97 98 #if defined(OS_MACOSX) 99 namespace mac { 100 101 const char kFirstNSException[] = "firstexception"; 102 const char kFirstNSExceptionTrace[] = "firstexception_bt"; 103 104 const char kLastNSException[] = "lastexception"; 105 const char kLastNSExceptionTrace[] = "lastexception_bt"; 106 107 const char kNSException[] = "nsexception"; 108 const char kNSExceptionTrace[] = "nsexception_bt"; 109 110 const char kSendAction[] = "sendaction"; 111 112 const char kZombie[] = "zombie"; 113 const char kZombieTrace[] = "zombie_dealloc_bt"; 114 115 } // namespace mac 116 #endif 117 118 size_t RegisterChromeCrashKeys() { 119 // The following keys may be chunked by the underlying crash logging system, 120 // but ultimately constitute a single key-value pair. 121 base::debug::CrashKey fixed_keys[] = { 122 { kClientID, kSmallSize }, 123 { kChannel, kSmallSize }, 124 { kActiveURL, kLargeSize }, 125 { kNumSwitches, kSmallSize }, 126 { kNumVariations, kSmallSize }, 127 { kVariations, kLargeSize }, 128 { kNumExtensionsCount, kSmallSize }, 129 { kNumberOfViews, kSmallSize }, 130 { kShutdownType, kSmallSize }, 131 #if !defined(OS_ANDROID) 132 { kGPUVendorID, kSmallSize }, 133 { kGPUDeviceID, kSmallSize }, 134 #endif 135 { kGPUDriverVersion, kSmallSize }, 136 { kGPUPixelShaderVersion, kSmallSize }, 137 { kGPUVertexShaderVersion, kSmallSize }, 138 #if defined(OS_MACOSX) 139 { kGPUGLVersion, kSmallSize }, 140 #elif defined(OS_POSIX) 141 { kGPUVendor, kSmallSize }, 142 { kGPURenderer, kSmallSize }, 143 #endif 144 145 // base/: 146 { "dm-usage", kSmallSize }, 147 // content/: 148 { "ppapi_path", kMediumSize }, 149 { "subresource_url", kLargeSize }, 150 #if defined(OS_CHROMEOS) 151 { kNumberOfUsers, kSmallSize }, 152 #endif 153 #if defined(OS_MACOSX) 154 { mac::kFirstNSException, kMediumSize }, 155 { mac::kFirstNSExceptionTrace, kMediumSize }, 156 { mac::kLastNSException, kMediumSize }, 157 { mac::kLastNSExceptionTrace, kMediumSize }, 158 { mac::kNSException, kMediumSize }, 159 { mac::kNSExceptionTrace, kMediumSize }, 160 { mac::kSendAction, kMediumSize }, 161 { mac::kZombie, kMediumSize }, 162 { mac::kZombieTrace, kMediumSize }, 163 // content/: 164 { "channel_error_bt", kMediumSize }, 165 { "remove_route_bt", kMediumSize }, 166 { "rwhvm_window", kMediumSize }, 167 // media/: 168 { "VideoCaptureDeviceQTKit", kSmallSize }, 169 #endif 170 }; 171 172 // This dynamic set of keys is used for sets of key value pairs when gathering 173 // a collection of data, like command line switches or extension IDs. 174 std::vector<base::debug::CrashKey> keys( 175 fixed_keys, fixed_keys + arraysize(fixed_keys)); 176 177 // Register the switches. 178 { 179 // The fixed_keys names are string constants. Use static storage for 180 // formatted key names as well, since they will persist for the duration of 181 // the program. 182 static char formatted_keys[kSwitchesMaxCount][sizeof(kSwitch) + 1] = 183 {{ 0 }}; 184 const size_t formatted_key_len = sizeof(formatted_keys[0]); 185 for (size_t i = 0; i < kSwitchesMaxCount; ++i) { 186 // Name the keys using 1-based indexing. 187 int n = base::snprintf( 188 formatted_keys[i], formatted_key_len, kSwitch, i + 1); 189 DCHECK_GT(n, 0); 190 base::debug::CrashKey crash_key = { formatted_keys[i], kSmallSize }; 191 keys.push_back(crash_key); 192 } 193 } 194 195 // Register the extension IDs. 196 { 197 static char formatted_keys[kExtensionIDMaxCount][sizeof(kExtensionID) + 1] = 198 {{ 0 }}; 199 const size_t formatted_key_len = sizeof(formatted_keys[0]); 200 for (size_t i = 0; i < kExtensionIDMaxCount; ++i) { 201 int n = base::snprintf( 202 formatted_keys[i], formatted_key_len, kExtensionID, i + 1); 203 DCHECK_GT(n, 0); 204 base::debug::CrashKey crash_key = { formatted_keys[i], kSmallSize }; 205 keys.push_back(crash_key); 206 } 207 } 208 209 // Register the printer info. 210 { 211 static char formatted_keys[kPrinterInfoCount][sizeof(kPrinterInfo) + 1] = 212 {{ 0 }}; 213 const size_t formatted_key_len = sizeof(formatted_keys[0]); 214 for (size_t i = 0; i < kPrinterInfoCount; ++i) { 215 // Key names are 1-indexed. 216 int n = base::snprintf( 217 formatted_keys[i], formatted_key_len, kPrinterInfo, i + 1); 218 DCHECK_GT(n, 0); 219 base::debug::CrashKey crash_key = { formatted_keys[i], kSmallSize }; 220 keys.push_back(crash_key); 221 } 222 } 223 224 return base::debug::InitCrashKeys(&keys.at(0), keys.size(), 225 kSingleChunkLength); 226 } 227 228 void SetClientID(const std::string& client_id) { 229 std::string guid(client_id); 230 // Remove all instance of '-' char from the GUID. So BCD-WXY becomes BCDWXY. 231 ReplaceSubstringsAfterOffset(&guid, 0, "-", ""); 232 if (guid.empty()) 233 return; 234 235 base::debug::SetCrashKeyValue(kClientID, guid); 236 GoogleUpdateSettings::SetMetricsId(guid); 237 } 238 239 static bool IsBoringSwitch(const std::string& flag) { 240 #if defined(OS_WIN) 241 return StartsWithASCII(flag, "--channel=", true) || 242 243 // No point to including this since we already have a ptype field. 244 StartsWithASCII(flag, "--type=", true) || 245 246 // Not particularly interesting 247 StartsWithASCII(flag, "--flash-broker=", true) || 248 249 // Just about everything has this, don't bother. 250 StartsWithASCII(flag, "/prefetch:", true) || 251 252 // We handle the plugin path separately since it is usually too big 253 // to fit in the switches (limited to 63 characters). 254 StartsWithASCII(flag, "--plugin-path=", true) || 255 256 // This is too big so we end up truncating it anyway. 257 StartsWithASCII(flag, "--force-fieldtrials=", true) || 258 259 // These surround the flags that were added by about:flags, it lets 260 // you distinguish which flags were added manually via the command 261 // line versus those added through about:flags. For the most part 262 // we don't care how an option was enabled, so we strip these. 263 // (If you need to know can always look at the PEB). 264 flag == "--flag-switches-begin" || 265 flag == "--flag-switches-end"; 266 #elif defined(OS_CHROMEOS) 267 static const char* kIgnoreSwitches[] = { 268 ::switches::kEnableCompositingForFixedPosition, 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