• 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 
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 
25 namespace crash_keys {
26 
27 // A small crash key, guaranteed to never be split into multiple pieces.
28 const 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.
32 const size_t kMediumSize = kSmallSize * 4;
33 
34 // A large crash key, which will be chunked on all platforms. This should be
35 // used sparingly.
36 const 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)
41 static const size_t kSingleChunkLength =
42     google_breakpad::SimpleStringDictionary::value_size - 1;
43 #elif defined(OS_WIN)
44 static const size_t kSingleChunkLength =
45     google_breakpad::CustomInfoEntry::kValueMaxLength - 1;
46 #else
47 static const size_t kSingleChunkLength = 63;
48 #endif
49 
50 // Guarantees for crash key sizes.
51 COMPILE_ASSERT(kSmallSize <= kSingleChunkLength,
52                crash_key_chunk_size_too_small);
53 #if defined(OS_MACOSX)
54 COMPILE_ASSERT(kMediumSize <= kSingleChunkLength,
55                mac_has_medium_size_crash_key_chunks);
56 #endif
57 
58 const char kClientId[] = "guid";
59 
60 const char kChannel[] = "channel";
61 
62 const char kActiveURL[] = "url-chunk";
63 
64 const char kFontKeyName[] = "font_key_name";
65 
66 const char kSwitch[] = "switch-%" PRIuS;
67 const char kNumSwitches[] = "num-switches";
68 
69 const char kNumVariations[] = "num-experiments";
70 const char kVariations[] = "variations";
71 
72 const char kExtensionID[] = "extension-%" PRIuS;
73 const char kNumExtensionsCount[] = "num-extensions";
74 
75 const char kNumberOfViews[] = "num-views";
76 
77 const char kShutdownType[] = "shutdown-type";
78 
79 #if !defined(OS_ANDROID)
80 const char kGPUVendorID[] = "gpu-venid";
81 const char kGPUDeviceID[] = "gpu-devid";
82 #endif
83 const char kGPUDriverVersion[] = "gpu-driver";
84 const char kGPUPixelShaderVersion[] = "gpu-psver";
85 const char kGPUVertexShaderVersion[] = "gpu-vsver";
86 #if defined(OS_MACOSX)
87 const char kGPUGLVersion[] = "gpu-glver";
88 #elif defined(OS_POSIX)
89 const char kGPUVendor[] = "gpu-gl-vendor";
90 const char kGPURenderer[] = "gpu-gl-renderer";
91 #endif
92 
93 const char kPrinterInfo[] = "prn-info-%" PRIuS;
94 
95 #if defined(OS_CHROMEOS)
96 const char kNumberOfUsers[] = "num-users";
97 #endif
98 
99 #if defined(OS_MACOSX)
100 namespace mac {
101 
102 const char kFirstNSException[] = "firstexception";
103 const char kFirstNSExceptionTrace[] = "firstexception_bt";
104 
105 const char kLastNSException[] = "lastexception";
106 const char kLastNSExceptionTrace[] = "lastexception_bt";
107 
108 const char kNSException[] = "nsexception";
109 const char kNSExceptionTrace[] = "nsexception_bt";
110 
111 const char kSendAction[] = "sendaction";
112 
113 const char kZombie[] = "zombie";
114 const char kZombieTrace[] = "zombie_dealloc_bt";
115 
116 }  // namespace mac
117 #endif
118 
RegisterChromeCrashKeys()119 size_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 
SetCrashClientIdFromGUID(const std::string & client_guid)230 void 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 
IsBoringSwitch(const std::string & flag)240 static 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 
SetSwitchesFromCommandLine(const CommandLine * command_line)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 
SetVariationsList(const std::vector<std::string> & variations)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 
SetActiveExtensions(const std::set<std::string> & extensions)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 
ScopedPrinterInfo(const base::StringPiece & data)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 
~ScopedPrinterInfo()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