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