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