Home | History | Annotate | Download | only in common
      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