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 #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