• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
RegisterChromeCrashKeys()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 
SetClientID(const std::string & client_id)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 
IsBoringSwitch(const std::string & flag)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 
SetSwitchesFromCommandLine(const CommandLine * command_line)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 
SetVariationsList(const std::vector<std::string> & variations)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 
SetActiveExtensions(const std::set<std::string> & extensions)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 
ScopedPrinterInfo(const base::StringPiece & data)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 
~ScopedPrinterInfo()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