// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// NOTE: This file is only compiled when Crashpad is not used as the crash
// reproter.
#include <string_view>
#include "base/debug/crash_logging.h"
#include "base/format_macros.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "components/crash/core/common/crash_key.h"
#include "components/crash/core/common/crash_key_base_support.h"
#include "components/crash/core/common/crash_key_internal.h"
#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN)
#error "This file should not be used when Crashpad is available, nor on iOS."
#endif
namespace crash_reporter {
namespace internal {
namespace {
// String used to format chunked key names. The __1 through __N syntax is
// recognized by the crash collector, which will then stitch the numbered
// parts back into a single string value.
const char kChunkFormatString[] = "%s__%" PRIuS;
static TransitionalCrashKeyStorage* g_storage = nullptr;
constexpr size_t kUnsetStorageSlotSentinel =
TransitionalCrashKeyStorage::num_entries;
} // namespace
TransitionalCrashKeyStorage* GetCrashKeyStorage() {
if (!g_storage) {
g_storage = new internal::TransitionalCrashKeyStorage();
}
return g_storage;
}
void ResetCrashKeyStorageForTesting() {
auto* storage = g_storage;
g_storage = nullptr;
delete storage;
}
void CrashKeyStringImpl::Set(std::string_view value) {
// This check cannot be in the constructor because it is constexpr. Use _LT
// rather than _LE to account for the terminating \0.
DCHECK_LT(strlen(name_), kCrashKeyStorageKeySize);
const size_t kValueMaxLength = index_array_count_ * kCrashKeyStorageValueSize;
TransitionalCrashKeyStorage* storage = GetCrashKeyStorage();
value = value.substr(0, kValueMaxLength);
// If there is only one slot for the value, then handle it directly.
if (index_array_count_ == 1) {
std::string value_string(value);
if (is_set()) {
storage->SetValueAtIndex(index_array_[0], value_string.c_str());
} else {
index_array_[0] = storage->SetKeyValue(name_, value_string.c_str());
}
return;
}
// If the value fits in a single slot, the name of the key should not
// end with the __1 suffix of the chunked format.
if (value.length() < kCrashKeyStorageValueSize - 1) {
if (index_array_[1] != kUnsetStorageSlotSentinel) {
// If switching from chunked to non-chunked, clear all the values.
Clear();
index_array_[0] = storage->SetKeyValue(name_, value.data());
} else if (index_array_[0] != kUnsetStorageSlotSentinel) {
// The single entry was previously set.
storage->SetValueAtIndex(index_array_[0], value.data());
} else {
// This key was not previously set.
index_array_[0] = storage->SetKeyValue(name_, value.data());
}
return;
}
// If the key was previously set, but only using one slot, then the chunk
// name will change (from |name| to |name__1|).
if (index_array_[0] != kUnsetStorageSlotSentinel &&
index_array_[1] == kUnsetStorageSlotSentinel) {
storage->RemoveAtIndex(index_array_[0]);
index_array_[0] = kUnsetStorageSlotSentinel;
}
// Otherwise, break the value into chunks labeled name__1 through name__N,
// where N is |index_array_count_|.
size_t offset = 0;
for (size_t i = 0; i < index_array_count_; ++i) {
if (offset < value.length()) {
// The storage NUL-terminates the value, so ensure that a byte is
// not lost when setting individual chunks.
std::string_view chunk =
value.substr(offset, kCrashKeyStorageValueSize - 1);
offset += chunk.length();
if (index_array_[i] == kUnsetStorageSlotSentinel) {
std::string chunk_name =
base::StringPrintf(kChunkFormatString, name_, i + 1);
index_array_[i] =
storage->SetKeyValue(chunk_name.c_str(), chunk.data());
} else {
storage->SetValueAtIndex(index_array_[i], chunk.data());
}
} else {
storage->RemoveAtIndex(index_array_[i]);
index_array_[i] = kUnsetStorageSlotSentinel;
}
}
}
void CrashKeyStringImpl::Clear() {
for (size_t i = 0; i < index_array_count_; ++i) {
GetCrashKeyStorage()->RemoveAtIndex(index_array_[i]);
index_array_[i] = kUnsetStorageSlotSentinel;
}
}
bool CrashKeyStringImpl::is_set() const {
return index_array_[0] != kUnsetStorageSlotSentinel;
}
} // namespace internal
void InitializeCrashKeys() {
internal::GetCrashKeyStorage();
InitializeCrashKeyBaseSupport();
}
std::string GetCrashKeyValue(const std::string& key_name) {
const char* value =
internal::GetCrashKeyStorage()->GetValueForKey(key_name.c_str());
if (value)
return value;
return std::string();
}
void InitializeCrashKeysForTesting() {
InitializeCrashKeys();
}
void ResetCrashKeysForTesting() {
internal::ResetCrashKeyStorageForTesting();
base::debug::SetCrashKeyImplementation(nullptr);
}
} // namespace crash_reporter