Add ICU message format support
[chromium-blink-merge.git] / chrome / common / crash_keys.cc
blob2934d97886c9de3fa9df75fa6307bb78b852d92d
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.
5 #include "chrome/common/crash_keys.h"
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/common/chrome_switches.h"
15 #include "content/public/common/content_switches.h"
16 #include "ipc/ipc_switches.h"
18 #if defined(OS_MACOSX)
19 #include "breakpad/src/common/simple_string_dictionary.h"
20 #elif defined(OS_WIN)
21 #include "breakpad/src/client/windows/common/ipc_protocol.h"
22 #elif defined(OS_CHROMEOS)
23 #include "chrome/common/chrome_switches.h"
24 #include "gpu/command_buffer/service/gpu_switches.h"
25 #include "ui/gl/gl_switches.h"
26 #endif
28 namespace crash_keys {
30 // A small crash key, guaranteed to never be split into multiple pieces.
31 const size_t kSmallSize = 63;
33 // A medium crash key, which will be chunked on certain platforms but not
34 // others. Guaranteed to never be more than four chunks.
35 const size_t kMediumSize = kSmallSize * 4;
37 // A large crash key, which will be chunked on all platforms. This should be
38 // used sparingly.
39 const size_t kLargeSize = kSmallSize * 16;
41 // The maximum lengths specified by breakpad include the trailing NULL, so
42 // the actual length of the string is one less.
43 #if defined(OS_MACOSX)
44 static const size_t kSingleChunkLength =
45 google_breakpad::SimpleStringDictionary::value_size - 1;
46 #elif defined(OS_WIN)
47 static const size_t kSingleChunkLength =
48 google_breakpad::CustomInfoEntry::kValueMaxLength - 1;
49 #else
50 static const size_t kSingleChunkLength = 63;
51 #endif
53 // Guarantees for crash key sizes.
54 static_assert(kSmallSize <= kSingleChunkLength,
55 "crash key chunk size too small");
56 #if defined(OS_MACOSX)
57 static_assert(kMediumSize <= kSingleChunkLength,
58 "mac has medium size crash key chunks");
59 #endif
61 #if defined(OS_MACOSX)
62 // Crashpad owns the "guid" key. Chrome's metrics client ID is a separate ID
63 // carried in a distinct "metrics_client_id" field.
64 const char kMetricsClientId[] = "metrics_client_id";
65 #else
66 const char kClientId[] = "guid";
67 #endif
69 const char kChannel[] = "channel";
71 const char kActiveURL[] = "url-chunk";
73 const char kFontKeyName[] = "font_key_name";
75 const char kSwitch[] = "switch-%" PRIuS;
76 const char kNumSwitches[] = "num-switches";
78 const char kNumVariations[] = "num-experiments";
79 const char kVariations[] = "variations";
81 const char kExtensionID[] = "extension-%" PRIuS;
82 const char kNumExtensionsCount[] = "num-extensions";
84 const char kShutdownType[] = "shutdown-type";
86 #if !defined(OS_ANDROID)
87 const char kGPUVendorID[] = "gpu-venid";
88 const char kGPUDeviceID[] = "gpu-devid";
89 #endif
90 const char kGPUDriverVersion[] = "gpu-driver";
91 const char kGPUPixelShaderVersion[] = "gpu-psver";
92 const char kGPUVertexShaderVersion[] = "gpu-vsver";
93 #if defined(OS_MACOSX)
94 const char kGPUGLVersion[] = "gpu-glver";
95 #elif defined(OS_POSIX)
96 const char kGPUVendor[] = "gpu-gl-vendor";
97 const char kGPURenderer[] = "gpu-gl-renderer";
98 #endif
100 const char kPrinterInfo[] = "prn-info-%" PRIuS;
102 #if defined(OS_CHROMEOS)
103 const char kNumberOfUsers[] = "num-users";
104 #endif
106 #if defined(OS_MACOSX)
107 namespace mac {
109 const char kFirstNSException[] = "firstexception";
110 const char kFirstNSExceptionTrace[] = "firstexception_bt";
112 const char kLastNSException[] = "lastexception";
113 const char kLastNSExceptionTrace[] = "lastexception_bt";
115 const char kNSException[] = "nsexception";
116 const char kNSExceptionTrace[] = "nsexception_bt";
118 const char kSendAction[] = "sendaction";
120 const char kZombie[] = "zombie";
121 const char kZombieTrace[] = "zombie_dealloc_bt";
123 } // namespace mac
124 #endif
126 #if defined(KASKO)
127 const char kKaskoGuid[] = "kasko-guid";
128 const char kKaskoEquivalentGuid[] = "kasko-equivalent-guid";
129 #endif
131 // Used to help investigate bug 464926. NOTE: This value is defined multiple
132 // places in the codebase due to layering issues. DO NOT change the value here
133 // without changing it in all other places that it is defined in the codebase
134 // (search for |kBug464926CrashKey|).
135 const char kBug464926CrashKey[] = "bug-464926-info";
137 const char kViewCount[] = "view-count";
139 size_t RegisterChromeCrashKeys() {
140 // The following keys may be chunked by the underlying crash logging system,
141 // but ultimately constitute a single key-value pair.
142 base::debug::CrashKey fixed_keys[] = {
143 #if defined(OS_MACOSX)
144 { kMetricsClientId, kSmallSize },
145 #else
146 { kClientId, kSmallSize },
147 #endif
148 { kChannel, kSmallSize },
149 { kActiveURL, kLargeSize },
150 { kNumSwitches, kSmallSize },
151 { kNumVariations, kSmallSize },
152 { kVariations, kLargeSize },
153 { kNumExtensionsCount, kSmallSize },
154 { kShutdownType, kSmallSize },
155 #if !defined(OS_ANDROID)
156 { kGPUVendorID, kSmallSize },
157 { kGPUDeviceID, kSmallSize },
158 #endif
159 { kGPUDriverVersion, kSmallSize },
160 { kGPUPixelShaderVersion, kSmallSize },
161 { kGPUVertexShaderVersion, kSmallSize },
162 #if defined(OS_MACOSX)
163 { kGPUGLVersion, kSmallSize },
164 #elif defined(OS_POSIX)
165 { kGPUVendor, kSmallSize },
166 { kGPURenderer, kSmallSize },
167 #endif
169 // content/:
170 { "discardable-memory-allocated", kSmallSize },
171 { "discardable-memory-free", kSmallSize },
172 { kFontKeyName, kSmallSize},
173 { "ppapi_path", kMediumSize },
174 { "subresource_url", kLargeSize },
175 { "total-discardable-memory-allocated", kSmallSize },
176 #if defined(OS_CHROMEOS)
177 { kNumberOfUsers, kSmallSize },
178 #endif
179 #if defined(OS_MACOSX)
180 { mac::kFirstNSException, kMediumSize },
181 { mac::kFirstNSExceptionTrace, kMediumSize },
182 { mac::kLastNSException, kMediumSize },
183 { mac::kLastNSExceptionTrace, kMediumSize },
184 { mac::kNSException, kMediumSize },
185 { mac::kNSExceptionTrace, kMediumSize },
186 { mac::kSendAction, kMediumSize },
187 { mac::kZombie, kMediumSize },
188 { mac::kZombieTrace, kMediumSize },
189 // content/:
190 { "channel_error_bt", kMediumSize },
191 { "remove_route_bt", kMediumSize },
192 { "rwhvm_window", kMediumSize },
193 // media/:
194 { "VideoCaptureDeviceQTKit", kSmallSize },
195 #endif
196 #if defined(KASKO)
197 { kKaskoGuid, kSmallSize },
198 { kKaskoEquivalentGuid, kSmallSize },
199 #endif
200 { kBug464926CrashKey, kSmallSize },
201 { kViewCount, kSmallSize },
204 // This dynamic set of keys is used for sets of key value pairs when gathering
205 // a collection of data, like command line switches or extension IDs.
206 std::vector<base::debug::CrashKey> keys(
207 fixed_keys, fixed_keys + arraysize(fixed_keys));
209 // Register the switches.
211 // The fixed_keys names are string constants. Use static storage for
212 // formatted key names as well, since they will persist for the duration of
213 // the program.
214 static char formatted_keys[kSwitchesMaxCount][sizeof(kSwitch) + 1] =
215 {{ 0 }};
216 const size_t formatted_key_len = sizeof(formatted_keys[0]);
217 for (size_t i = 0; i < kSwitchesMaxCount; ++i) {
218 // Name the keys using 1-based indexing.
219 int n = base::snprintf(
220 formatted_keys[i], formatted_key_len, kSwitch, i + 1);
221 DCHECK_GT(n, 0);
222 base::debug::CrashKey crash_key = { formatted_keys[i], kSmallSize };
223 keys.push_back(crash_key);
227 // Register the extension IDs.
229 static char formatted_keys[kExtensionIDMaxCount][sizeof(kExtensionID) + 1] =
230 {{ 0 }};
231 const size_t formatted_key_len = sizeof(formatted_keys[0]);
232 for (size_t i = 0; i < kExtensionIDMaxCount; ++i) {
233 int n = base::snprintf(
234 formatted_keys[i], formatted_key_len, kExtensionID, i + 1);
235 DCHECK_GT(n, 0);
236 base::debug::CrashKey crash_key = { formatted_keys[i], kSmallSize };
237 keys.push_back(crash_key);
241 // Register the printer info.
243 static char formatted_keys[kPrinterInfoCount][sizeof(kPrinterInfo) + 1] =
244 {{ 0 }};
245 const size_t formatted_key_len = sizeof(formatted_keys[0]);
246 for (size_t i = 0; i < kPrinterInfoCount; ++i) {
247 // Key names are 1-indexed.
248 int n = base::snprintf(
249 formatted_keys[i], formatted_key_len, kPrinterInfo, i + 1);
250 DCHECK_GT(n, 0);
251 base::debug::CrashKey crash_key = { formatted_keys[i], kSmallSize };
252 keys.push_back(crash_key);
256 return base::debug::InitCrashKeys(&keys.at(0), keys.size(),
257 kSingleChunkLength);
260 void SetMetricsClientIdFromGUID(const std::string& metrics_client_guid) {
261 std::string stripped_guid(metrics_client_guid);
262 // Remove all instance of '-' char from the GUID. So BCD-WXY becomes BCDWXY.
263 base::ReplaceSubstringsAfterOffset(
264 &stripped_guid, 0, "-", base::StringPiece());
265 if (stripped_guid.empty())
266 return;
268 #if defined(OS_MACOSX)
269 // The crash client ID is maintained by Crashpad and is distinct from the
270 // metrics client ID, which is carried in its own key.
271 base::debug::SetCrashKeyValue(kMetricsClientId, stripped_guid);
272 #else
273 // The crash client ID is set by the application when Breakpad is in use.
274 // The same ID as the metrics client ID is used.
275 base::debug::SetCrashKeyValue(kClientId, stripped_guid);
276 #endif
279 void ClearMetricsClientId() {
280 #if defined(OS_MACOSX)
281 // Crashpad always monitors for crashes, but doesn't upload them when
282 // crash reporting is disabled. The preference to upload crash reports is
283 // linked to the preference for metrics reporting. When metrics reporting is
284 // disabled, don't put the metrics client ID into crash dumps. This way, crash
285 // reports that are saved but not uploaded will not have a metrics client ID
286 // from the time that metrics reporting was disabled even if they are uploaded
287 // by user action at a later date.
289 // Breakpad cannot be enabled or disabled without an application restart, and
290 // it needs to use the metrics client ID as its stable crash client ID, so
291 // leave its client ID intact even when metrics reporting is disabled while
292 // the application is running.
293 base::debug::ClearCrashKey(kMetricsClientId);
294 #endif
297 static bool IsBoringSwitch(const std::string& flag) {
298 static const char* const kIgnoreSwitches[] = {
299 switches::kEnableLogging,
300 switches::kFlagSwitchesBegin,
301 switches::kFlagSwitchesEnd,
302 switches::kLoggingLevel,
303 #if defined(OS_WIN)
304 // This file is linked into both chrome.dll and chrome.exe. However //ipc
305 // is only in the .dll, so this needs to be a literal rather than the
306 // constant.
307 "channel", // switches::kProcessChannelID
308 #else
309 switches::kProcessChannelID,
310 #endif
311 switches::kProcessType,
312 switches::kV,
313 switches::kVModule,
314 #if defined(OS_WIN)
315 switches::kForceFieldTrials,
316 switches::kPluginPath,
317 #elif defined(OS_MACOSX)
318 switches::kMetricsClientID,
319 #elif defined(OS_CHROMEOS)
320 switches::kPpapiFlashArgs,
321 switches::kPpapiFlashPath,
322 switches::kRegisterPepperPlugins,
323 switches::kUIPrioritizeInGpuProcess,
324 switches::kUseGL,
325 switches::kUserDataDir,
326 // Cros/CC flags are specified as raw strings to avoid dependency.
327 "child-wallpaper-large",
328 "child-wallpaper-small",
329 "default-wallpaper-large",
330 "default-wallpaper-small",
331 "guest-wallpaper-large",
332 "guest-wallpaper-small",
333 "enterprise-enable-forced-re-enrollment",
334 "enterprise-enrollment-initial-modulus",
335 "enterprise-enrollment-modulus-limit",
336 "login-profile",
337 "login-user",
338 "max-unused-resource-memory-usage-percentage",
339 "termination-message-file",
340 "use-cras",
341 #endif
344 #if defined(OS_WIN)
345 // Just about everything has this, don't bother.
346 if (base::StartsWith(flag, "/prefetch:", base::CompareCase::SENSITIVE))
347 return true;
348 #endif
350 if (!base::StartsWith(flag, "--", base::CompareCase::SENSITIVE))
351 return false;
352 size_t end = flag.find("=");
353 size_t len = (end == std::string::npos) ? flag.length() - 2 : end - 2;
354 for (size_t i = 0; i < arraysize(kIgnoreSwitches); ++i) {
355 if (flag.compare(2, len, kIgnoreSwitches[i]) == 0)
356 return true;
358 return false;
361 void SetSwitchesFromCommandLine(const base::CommandLine* command_line) {
362 DCHECK(command_line);
363 if (!command_line)
364 return;
366 const base::CommandLine::StringVector& argv = command_line->argv();
368 // Set the number of switches in case size > kNumSwitches.
369 base::debug::SetCrashKeyValue(kNumSwitches,
370 base::StringPrintf("%" PRIuS, argv.size() - 1));
372 size_t key_i = 1; // Key names are 1-indexed.
374 // Go through the argv, skipping the exec path.
375 for (size_t i = 1; i < argv.size(); ++i) {
376 #if defined(OS_WIN)
377 std::string switch_str = base::WideToUTF8(argv[i]);
378 #else
379 std::string switch_str = argv[i];
380 #endif
382 // Skip uninteresting switches.
383 if (IsBoringSwitch(switch_str))
384 continue;
386 // Stop if there are too many switches.
387 if (i > crash_keys::kSwitchesMaxCount)
388 break;
390 std::string key = base::StringPrintf(kSwitch, key_i++);
391 base::debug::SetCrashKeyValue(key, switch_str);
394 // Clear any remaining switches.
395 for (; key_i <= kSwitchesMaxCount; ++key_i) {
396 base::debug::ClearCrashKey(base::StringPrintf(kSwitch, key_i));
400 void SetVariationsList(const std::vector<std::string>& variations) {
401 base::debug::SetCrashKeyValue(kNumVariations,
402 base::StringPrintf("%" PRIuS, variations.size()));
404 std::string variations_string;
405 variations_string.reserve(kLargeSize);
407 for (size_t i = 0; i < variations.size(); ++i) {
408 const std::string& variation = variations[i];
409 // Do not truncate an individual experiment.
410 if (variations_string.size() + variation.size() >= kLargeSize)
411 break;
412 variations_string += variation;
413 variations_string += ",";
416 base::debug::SetCrashKeyValue(kVariations, variations_string);
419 void SetActiveExtensions(const std::set<std::string>& extensions) {
420 base::debug::SetCrashKeyValue(kNumExtensionsCount,
421 base::StringPrintf("%" PRIuS, extensions.size()));
423 std::set<std::string>::const_iterator it = extensions.begin();
424 for (size_t i = 0; i < kExtensionIDMaxCount; ++i) {
425 std::string key = base::StringPrintf(kExtensionID, i + 1);
426 if (it == extensions.end()) {
427 base::debug::ClearCrashKey(key);
428 } else {
429 base::debug::SetCrashKeyValue(key, *it);
430 ++it;
435 ScopedPrinterInfo::ScopedPrinterInfo(const base::StringPiece& data) {
436 std::vector<std::string> info = base::SplitString(
437 data.as_string(), ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
438 for (size_t i = 0; i < kPrinterInfoCount; ++i) {
439 std::string key = base::StringPrintf(kPrinterInfo, i + 1);
440 std::string value;
441 if (i < info.size())
442 value = info[i];
443 base::debug::SetCrashKeyValue(key, value);
447 ScopedPrinterInfo::~ScopedPrinterInfo() {
448 for (size_t i = 0; i < kPrinterInfoCount; ++i) {
449 std::string key = base::StringPrintf(kPrinterInfo, i + 1);
450 base::debug::ClearCrashKey(key);
454 } // namespace crash_keys