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
15#if defined(OS_MACOSX)
16#include "breakpad/src/common/simple_string_dictionary.h"
17#elif defined(OS_WIN)
18#include "breakpad/src/client/windows/common/ipc_protocol.h"
19#elif defined(OS_CHROMEOS)
20#include "chrome/common/chrome_switches.h"
21#include "gpu/command_buffer/service/gpu_switches.h"
22#include "ui/gl/gl_switches.h"
23#endif
24
25namespace crash_keys {
26
27// A small crash key, guaranteed to never be split into multiple pieces.
28const size_t kSmallSize = 63;
29
30// A medium crash key, which will be chunked on certain platforms but not
31// others. Guaranteed to never be more than four chunks.
32const size_t kMediumSize = kSmallSize * 4;
33
34// A large crash key, which will be chunked on all platforms. This should be
35// used sparingly.
36const size_t kLargeSize = kSmallSize * 16;
37
38// The maximum lengths specified by breakpad include the trailing NULL, so
39// the actual length of the string is one less.
40#if defined(OS_MACOSX)
41static const size_t kSingleChunkLength =
42    google_breakpad::SimpleStringDictionary::value_size - 1;
43#elif defined(OS_WIN)
44static const size_t kSingleChunkLength =
45    google_breakpad::CustomInfoEntry::kValueMaxLength - 1;
46#else
47static const size_t kSingleChunkLength = 63;
48#endif
49
50// Guarantees for crash key sizes.
51COMPILE_ASSERT(kSmallSize <= kSingleChunkLength,
52               crash_key_chunk_size_too_small);
53#if defined(OS_MACOSX)
54COMPILE_ASSERT(kMediumSize <= kSingleChunkLength,
55               mac_has_medium_size_crash_key_chunks);
56#endif
57
58const char kClientId[] = "guid";
59
60const char kChannel[] = "channel";
61
62const char kActiveURL[] = "url-chunk";
63
64const char kFontKeyName[] = "font_key_name";
65
66const char kSwitch[] = "switch-%" PRIuS;
67const char kNumSwitches[] = "num-switches";
68
69const char kNumVariations[] = "num-experiments";
70const char kVariations[] = "variations";
71
72const char kExtensionID[] = "extension-%" PRIuS;
73const char kNumExtensionsCount[] = "num-extensions";
74
75const char kNumberOfViews[] = "num-views";
76
77const char kShutdownType[] = "shutdown-type";
78
79#if !defined(OS_ANDROID)
80const char kGPUVendorID[] = "gpu-venid";
81const char kGPUDeviceID[] = "gpu-devid";
82#endif
83const char kGPUDriverVersion[] = "gpu-driver";
84const char kGPUPixelShaderVersion[] = "gpu-psver";
85const char kGPUVertexShaderVersion[] = "gpu-vsver";
86#if defined(OS_MACOSX)
87const char kGPUGLVersion[] = "gpu-glver";
88#elif defined(OS_POSIX)
89const char kGPUVendor[] = "gpu-gl-vendor";
90const char kGPURenderer[] = "gpu-gl-renderer";
91#endif
92
93const char kPrinterInfo[] = "prn-info-%" PRIuS;
94
95#if defined(OS_CHROMEOS)
96const char kNumberOfUsers[] = "num-users";
97#endif
98
99#if defined(OS_MACOSX)
100namespace mac {
101
102const char kFirstNSException[] = "firstexception";
103const char kFirstNSExceptionTrace[] = "firstexception_bt";
104
105const char kLastNSException[] = "lastexception";
106const char kLastNSExceptionTrace[] = "lastexception_bt";
107
108const char kNSException[] = "nsexception";
109const char kNSExceptionTrace[] = "nsexception_bt";
110
111const char kSendAction[] = "sendaction";
112
113const char kZombie[] = "zombie";
114const char kZombieTrace[] = "zombie_dealloc_bt";
115
116}  // namespace mac
117#endif
118
119size_t RegisterChromeCrashKeys() {
120  // The following keys may be chunked by the underlying crash logging system,
121  // but ultimately constitute a single key-value pair.
122  base::debug::CrashKey fixed_keys[] = {
123    { kClientId, kSmallSize },
124    { kChannel, kSmallSize },
125    { kActiveURL, kLargeSize },
126    { kNumSwitches, kSmallSize },
127    { kNumVariations, kSmallSize },
128    { kVariations, kLargeSize },
129    { kNumExtensionsCount, kSmallSize },
130    { kNumberOfViews, kSmallSize },
131    { kShutdownType, kSmallSize },
132#if !defined(OS_ANDROID)
133    { kGPUVendorID, kSmallSize },
134    { kGPUDeviceID, kSmallSize },
135#endif
136    { kGPUDriverVersion, kSmallSize },
137    { kGPUPixelShaderVersion, kSmallSize },
138    { kGPUVertexShaderVersion, kSmallSize },
139#if defined(OS_MACOSX)
140    { kGPUGLVersion, kSmallSize },
141#elif defined(OS_POSIX)
142    { kGPUVendor, kSmallSize },
143    { kGPURenderer, kSmallSize },
144#endif
145
146    // base/:
147    { "dm-usage", kSmallSize },
148    // content/:
149    { kFontKeyName, kSmallSize},
150    { "ppapi_path", kMediumSize },
151    { "subresource_url", kLargeSize },
152#if defined(OS_CHROMEOS)
153    { kNumberOfUsers, kSmallSize },
154#endif
155#if defined(OS_MACOSX)
156    { mac::kFirstNSException, kMediumSize },
157    { mac::kFirstNSExceptionTrace, kMediumSize },
158    { mac::kLastNSException, kMediumSize },
159    { mac::kLastNSExceptionTrace, kMediumSize },
160    { mac::kNSException, kMediumSize },
161    { mac::kNSExceptionTrace, kMediumSize },
162    { mac::kSendAction, kMediumSize },
163    { mac::kZombie, kMediumSize },
164    { mac::kZombieTrace, kMediumSize },
165    // content/:
166    { "channel_error_bt", kMediumSize },
167    { "remove_route_bt", kMediumSize },
168    { "rwhvm_window", kMediumSize },
169    // media/:
170    { "VideoCaptureDeviceQTKit", kSmallSize },
171#endif
172  };
173
174  // This dynamic set of keys is used for sets of key value pairs when gathering
175  // a collection of data, like command line switches or extension IDs.
176  std::vector<base::debug::CrashKey> keys(
177      fixed_keys, fixed_keys + arraysize(fixed_keys));
178
179  // Register the switches.
180  {
181    // The fixed_keys names are string constants. Use static storage for
182    // formatted key names as well, since they will persist for the duration of
183    // the program.
184    static char formatted_keys[kSwitchesMaxCount][sizeof(kSwitch) + 1] =
185        {{ 0 }};
186    const size_t formatted_key_len = sizeof(formatted_keys[0]);
187    for (size_t i = 0; i < kSwitchesMaxCount; ++i) {
188      // Name the keys using 1-based indexing.
189      int n = base::snprintf(
190          formatted_keys[i], formatted_key_len, kSwitch, i + 1);
191      DCHECK_GT(n, 0);
192      base::debug::CrashKey crash_key = { formatted_keys[i], kSmallSize };
193      keys.push_back(crash_key);
194    }
195  }
196
197  // Register the extension IDs.
198  {
199    static char formatted_keys[kExtensionIDMaxCount][sizeof(kExtensionID) + 1] =
200        {{ 0 }};
201    const size_t formatted_key_len = sizeof(formatted_keys[0]);
202    for (size_t i = 0; i < kExtensionIDMaxCount; ++i) {
203      int n = base::snprintf(
204          formatted_keys[i], formatted_key_len, kExtensionID, i + 1);
205      DCHECK_GT(n, 0);
206      base::debug::CrashKey crash_key = { formatted_keys[i], kSmallSize };
207      keys.push_back(crash_key);
208    }
209  }
210
211  // Register the printer info.
212  {
213    static char formatted_keys[kPrinterInfoCount][sizeof(kPrinterInfo) + 1] =
214        {{ 0 }};
215    const size_t formatted_key_len = sizeof(formatted_keys[0]);
216    for (size_t i = 0; i < kPrinterInfoCount; ++i) {
217      // Key names are 1-indexed.
218      int n = base::snprintf(
219          formatted_keys[i], formatted_key_len, kPrinterInfo, i + 1);
220      DCHECK_GT(n, 0);
221      base::debug::CrashKey crash_key = { formatted_keys[i], kSmallSize };
222      keys.push_back(crash_key);
223    }
224  }
225
226  return base::debug::InitCrashKeys(&keys.at(0), keys.size(),
227                                    kSingleChunkLength);
228}
229
230void SetCrashClientIdFromGUID(const std::string& client_guid) {
231  std::string stripped_guid(client_guid);
232  // Remove all instance of '-' char from the GUID. So BCD-WXY becomes BCDWXY.
233  ReplaceSubstringsAfterOffset(&stripped_guid, 0, "-", "");
234  if (stripped_guid.empty())
235    return;
236
237  base::debug::SetCrashKeyValue(kClientId, stripped_guid);
238}
239
240static bool IsBoringSwitch(const std::string& flag) {
241#if defined(OS_WIN)
242  return StartsWithASCII(flag, "--channel=", true) ||
243
244         // No point to including this since we already have a ptype field.
245         StartsWithASCII(flag, "--type=", true) ||
246
247         // Not particularly interesting
248         StartsWithASCII(flag, "--flash-broker=", true) ||
249
250         // Just about everything has this, don't bother.
251         StartsWithASCII(flag, "/prefetch:", true) ||
252
253         // We handle the plugin path separately since it is usually too big
254         // to fit in the switches (limited to 63 characters).
255         StartsWithASCII(flag, "--plugin-path=", true) ||
256
257         // This is too big so we end up truncating it anyway.
258         StartsWithASCII(flag, "--force-fieldtrials=", true) ||
259
260         // These surround the flags that were added by about:flags, it lets
261         // you distinguish which flags were added manually via the command
262         // line versus those added through about:flags. For the most part
263         // we don't care how an option was enabled, so we strip these.
264         // (If you need to know can always look at the PEB).
265         flag == "--flag-switches-begin" ||
266         flag == "--flag-switches-end";
267#elif defined(OS_CHROMEOS)
268  static const char* kIgnoreSwitches[] = {
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
311void 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
350void 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
369void 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
385ScopedPrinterInfo::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
397ScopedPrinterInfo::~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