blob: d6d43f2a5545204ddaf0645ac399cad2ca27ae84 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/crash/core/common/crash_keys.h"
#include "base/command_line.h"
#include "base/debug/crash_logging.h"
#include "base/format_macros.h"
#include "base/logging.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "components/crash/core/common/crash_key.h"
namespace crash_keys {
#if defined(OS_MACOSX)
// Crashpad owns the "guid" key. Chrome's metrics client ID is a separate ID
// carried in a distinct "metrics_client_id" field.
const char kMetricsClientId[] = "metrics_client_id";
#elif defined(OS_WIN)
// TODO(scottmg): While transitioning to Crashpad, there are some executables
// that use Crashpad (which use kMetricsClientId), and some that use Breakpad
// (kClientId), and they both use this file. For now we define both, but once
// Breakpad is no longer used on Windows, we will no longer need kClientId, and
// this can be combined with the OS_MACOSX block above.
const char kMetricsClientId[] = "metrics_client_id";
const char kClientId[] = "guid";
#else
const char kClientId[] = "guid";
#endif
const char kChannel[] = "channel";
const char kNumVariations[] = "num-experiments";
const char kVariations[] = "variations";
const char kSwitchFormat[] = "switch-%" PRIuS;
void SetMetricsClientIdFromGUID(const std::string& metrics_client_guid) {
std::string stripped_guid(metrics_client_guid);
// Remove all instance of '-' char from the GUID. So BCD-WXY becomes BCDWXY.
base::ReplaceSubstringsAfterOffset(
&stripped_guid, 0, "-", base::StringPiece());
if (stripped_guid.empty())
return;
#if defined(OS_MACOSX) || defined(OS_WIN)
// The crash client ID is maintained by Crashpad and is distinct from the
// metrics client ID, which is carried in its own key.
base::debug::SetCrashKeyValue(kMetricsClientId, stripped_guid);
#else
// The crash client ID is set by the application when Breakpad is in use.
// The same ID as the metrics client ID is used.
base::debug::SetCrashKeyValue(kClientId, stripped_guid);
#endif
}
void ClearMetricsClientId() {
#if defined(OS_MACOSX) || defined(OS_WIN)
// Crashpad always monitors for crashes, but doesn't upload them when
// crash reporting is disabled. The preference to upload crash reports is
// linked to the preference for metrics reporting. When metrics reporting is
// disabled, don't put the metrics client ID into crash dumps. This way, crash
// reports that are saved but not uploaded will not have a metrics client ID
// from the time that metrics reporting was disabled even if they are uploaded
// by user action at a later date.
//
// Breakpad cannot be enabled or disabled without an application restart, and
// it needs to use the metrics client ID as its stable crash client ID, so
// leave its client ID intact even when metrics reporting is disabled while
// the application is running.
base::debug::ClearCrashKey(kMetricsClientId);
#endif
}
void SetVariationsList(const std::vector<std::string>& variations) {
base::debug::SetCrashKeyValue(kNumVariations,
base::StringPrintf("%" PRIuS, variations.size()));
std::string variations_string;
variations_string.reserve(kHugeSize);
for (size_t i = 0; i < variations.size(); ++i) {
const std::string& variation = variations[i];
// Do not truncate an individual experiment.
if (variations_string.size() + variation.size() >= kHugeSize)
break;
variations_string += variation;
variations_string += ",";
}
base::debug::SetCrashKeyValue(kVariations, variations_string);
}
void GetCrashKeysForCommandLineSwitches(
std::vector<base::debug::CrashKey>* keys) {
DCHECK(keys);
// Use static storage for formatted key names, since they will persist for
// the duration of the program.
static char formatted_keys[kSwitchesMaxCount][sizeof(kSwitchFormat) + 1] =
{{ 0 }};
const size_t formatted_key_len = sizeof(formatted_keys[0]);
// sizeof(kSwitchFormat) is platform-dependent, so make sure formatted_keys
// actually have space for a 2-digit switch number plus null-terminator.
static_assert(formatted_key_len >= 10,
"insufficient space for \"switch-NN\"");
for (size_t i = 0; i < kSwitchesMaxCount; ++i) {
// Name the keys using 1-based indexing.
int n = base::snprintf(formatted_keys[i], formatted_key_len, kSwitchFormat,
i + 1);
DCHECK_GT(n, 0);
base::debug::CrashKey crash_key = { formatted_keys[i], kSmallSize };
keys->push_back(crash_key);
}
}
void SetSwitchesFromCommandLine(const base::CommandLine& command_line,
SwitchFilterFunction skip_filter) {
const base::CommandLine::StringVector& argv = command_line.argv();
// Set the number of switches in case size > kNumSwitches.
// num-switches is capped at 15 entries, so only two digits are stored.
static crash_reporter::CrashKeyString<2> num_switches_key("num-switches");
num_switches_key.Set(base::StringPrintf("%" PRIuS, argv.size() - 1));
size_t key_i = 1; // Key names are 1-indexed.
// Go through the argv, skipping the exec path. Stop if there are too many
// switches to hold in crash keys.
for (size_t i = 1; i < argv.size() && key_i <= crash_keys::kSwitchesMaxCount;
++i) {
#if defined(OS_WIN)
std::string switch_str = base::WideToUTF8(argv[i]);
#else
std::string switch_str = argv[i];
#endif
// Skip uninteresting switches.
if (skip_filter && (*skip_filter)(switch_str))
continue;
std::string key = base::StringPrintf(kSwitchFormat, key_i++);
base::debug::SetCrashKeyValue(key, switch_str);
}
// Clear any remaining switches.
for (; key_i <= kSwitchesMaxCount; ++key_i)
base::debug::ClearCrashKey(base::StringPrintf(kSwitchFormat, key_i));
}
} // namespace crash_keys