| // Copyright 2017 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. |
| |
| // NOTE: This file is only compiled when Crashpad is not used as the crash |
| // reproter. |
| |
| #include "components/crash/core/common/crash_key.h" |
| |
| #include "base/debug/crash_logging.h" |
| #include "base/format_macros.h" |
| #include "base/strings/string_piece.h" |
| #include "base/strings/stringprintf.h" |
| #include "build/build_config.h" |
| #include "components/crash/core/common/crash_key_base_support.h" |
| #include "components/crash/core/common/crash_key_internal.h" |
| |
| #if defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_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(base::StringPiece value) { |
| 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.as_string(); |
| 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 individaul chunks. |
| base::StringPiece 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 ResetCrashKeysForTesting() { |
| internal::ResetCrashKeyStorageForTesting(); |
| base::debug::SetCrashKeyImplementation(nullptr); |
| } |
| |
| } // namespace crash_reporter |