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