crash_keys.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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
22namespace crash_keys {
23
24// A small crash key, guaranteed to never be split into multiple pieces.
25const 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.
29const size_t kMediumSize = kSmallSize * 4;
30
31// A large crash key, which will be chunked on all platforms. This should be
32// used sparingly.
33const 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)
38static const size_t kSingleChunkLength =
39    google_breakpad::SimpleStringDictionary::value_size - 1;
40#elif defined(OS_WIN)
41static const size_t kSingleChunkLength =
42    google_breakpad::CustomInfoEntry::kValueMaxLength - 1;
43#else
44static const size_t kSingleChunkLength = 63;
45#endif
46
47// Guarantees for crash key sizes.
48COMPILE_ASSERT(kSmallSize <= kSingleChunkLength,
49               crash_key_chunk_size_too_small);
50#if defined(OS_MACOSX)
51COMPILE_ASSERT(kMediumSize <= kSingleChunkLength,
52               mac_has_medium_size_crash_key_chunks);
53#endif
54
55const char kClientID[] = "guid";
56
57const char kChannel[] = "channel";
58
59const char kActiveURL[] = "url-chunk";
60
61const char kSwitch[] = "switch-%" PRIuS;
62const char kNumSwitches[] = "num-switches";
63
64const char kNumVariations[] = "num-experiments";
65const char kVariations[] = "variations";
66
67const char kExtensionID[] = "extension-%" PRIuS;
68const char kNumExtensionsCount[] = "num-extensions";
69
70const char kNumberOfViews[] = "num-views";
71
72#if !defined(OS_ANDROID)
73const char kGPUVendorID[] = "gpu-venid";
74const char kGPUDeviceID[] = "gpu-devid";
75#endif
76const char kGPUDriverVersion[] = "gpu-driver";
77const char kGPUPixelShaderVersion[] = "gpu-psver";
78const char kGPUVertexShaderVersion[] = "gpu-vsver";
79#if defined(OS_MACOSX)
80const char kGPUGLVersion[] = "gpu-glver";
81#elif defined(OS_POSIX)
82const char kGPUVendor[] = "gpu-gl-vendor";
83const char kGPURenderer[] = "gpu-gl-renderer";
84#endif
85
86const char kPrinterInfo[] = "prn-info-%" PRIuS;
87
88#if defined(OS_MACOSX)
89namespace mac {
90
91const char kFirstNSException[] = "firstexception";
92const char kFirstNSExceptionTrace[] = "firstexception_bt";
93
94const char kLastNSException[] = "lastexception";
95const char kLastNSExceptionTrace[] = "lastexception_bt";
96
97const char kNSException[] = "nsexception";
98const char kNSExceptionTrace[] = "nsexception_bt";
99
100const char kSendAction[] = "sendaction";
101
102const char kZombie[] = "zombie";
103const char kZombieTrace[] = "zombie_dealloc_bt";
104
105}  // namespace mac
106#endif
107
108size_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
212void 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
223static 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-fieldtest=", 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
255void 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
294void 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
313void 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
329ScopedPrinterInfo::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
341ScopedPrinterInfo::~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