1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef INCLUDE_PERFETTO_EXT_BASE_CRASH_KEYS_H_ 18 #define INCLUDE_PERFETTO_EXT_BASE_CRASH_KEYS_H_ 19 20 #include <algorithm> 21 #include <atomic> 22 23 #include <stdint.h> 24 #include <string.h> 25 26 #include "perfetto/base/compiler.h" 27 #include "perfetto/ext/base/string_view.h" 28 29 // Crash keys are very simple global variables with static-storage that 30 // are reported on crash time for managed crashes (CHECK/FATAL/Watchdog). 31 // - Translation units can define a CrashKey and register it at some point 32 // during initialization. 33 // - CrashKey instances must be long-lived. They should really be just global 34 // static variable in the anonymous namespace. 35 // Example: 36 // subsystem_1.cc 37 // CrashKey g_client_id("ipc_client_id"); 38 // ... 39 // OnIpcReceived(client_id) { 40 // g_client_id.Set(client_id); 41 // ... // Process the IPC 42 // g_client_id.Clear(); 43 // } 44 // Or equivalently: 45 // OnIpcReceived(client_id) { 46 // auto scoped_key = g_client_id.SetScoped(client_id); 47 // ... // Process the IPC 48 // } 49 // 50 // If a crash happens while processing the IPC, the crash report will 51 // have a line "ipc_client_id: 42". 52 // 53 // Thread safety considerations: 54 // CrashKeys can be registered and set/cleared from any thread. 55 // There is no compelling use-case to have full acquire/release consistency when 56 // setting a key. This means that if a thread crashes immediately after a 57 // crash key has been set on another thread, the value printed on the crash 58 // report could be incomplete. The code guarantees defined behavior and does 59 // not rely on null-terminated string (in the worst case 32 bytes of random 60 // garbage will be printed out). 61 62 // The tests live in logging_unittest.cc. 63 64 namespace perfetto { 65 namespace base { 66 67 constexpr size_t kCrashKeyMaxStrSize = 32; 68 69 // CrashKey instances must be long lived 70 class CrashKey { 71 public: 72 class ScopedClear { 73 public: ScopedClear(CrashKey * k)74 explicit ScopedClear(CrashKey* k) : key_(k) {} ~ScopedClear()75 ~ScopedClear() { 76 if (key_) 77 key_->Clear(); 78 } 79 ScopedClear(const ScopedClear&) = delete; 80 ScopedClear& operator=(const ScopedClear&) = delete; 81 ScopedClear& operator=(ScopedClear&&) = delete; ScopedClear(ScopedClear && other)82 ScopedClear(ScopedClear&& other) noexcept : key_(other.key_) { 83 other.key_ = nullptr; 84 } 85 86 private: 87 CrashKey* key_; 88 }; 89 90 // constexpr so it can be used in the anon namespace without requiring a 91 // global constructor. 92 // |name| must be a long-lived string. CrashKey(const char * name)93 constexpr explicit CrashKey(const char* name) 94 : registered_{}, type_(Type::kUnset), name_(name), str_value_{} {} 95 CrashKey(const CrashKey&) = delete; 96 CrashKey& operator=(const CrashKey&) = delete; 97 CrashKey(CrashKey&&) = delete; 98 CrashKey& operator=(CrashKey&&) = delete; 99 100 enum class Type : uint8_t { kUnset = 0, kInt, kStr }; 101 Clear()102 void Clear() { 103 int_value_.store(0, std::memory_order_relaxed); 104 type_.store(Type::kUnset, std::memory_order_relaxed); 105 } 106 Set(int64_t value)107 void Set(int64_t value) { 108 int_value_.store(value, std::memory_order_relaxed); 109 type_.store(Type::kInt, std::memory_order_relaxed); 110 if (PERFETTO_UNLIKELY(!registered_.load(std::memory_order_relaxed))) 111 Register(); 112 } 113 Set(StringView sv)114 void Set(StringView sv) { 115 size_t len = std::min(sv.size(), sizeof(str_value_) - 1); 116 for (size_t i = 0; i < len; ++i) 117 str_value_[i].store(sv.data()[i], std::memory_order_relaxed); 118 str_value_[len].store('\0', std::memory_order_relaxed); 119 type_.store(Type::kStr, std::memory_order_relaxed); 120 if (PERFETTO_UNLIKELY(!registered_.load(std::memory_order_relaxed))) 121 Register(); 122 } 123 SetScoped(int64_t value)124 ScopedClear SetScoped(int64_t value) PERFETTO_WARN_UNUSED_RESULT { 125 Set(value); 126 return ScopedClear(this); 127 } 128 SetScoped(StringView sv)129 ScopedClear SetScoped(StringView sv) PERFETTO_WARN_UNUSED_RESULT { 130 Set(sv); 131 return ScopedClear(this); 132 } 133 134 void Register(); 135 int_value()136 int64_t int_value() const { 137 return int_value_.load(std::memory_order_relaxed); 138 } 139 size_t ToString(char* dst, size_t len); 140 141 private: 142 std::atomic<bool> registered_; 143 std::atomic<Type> type_; 144 const char* const name_; 145 union { 146 std::atomic<char> str_value_[kCrashKeyMaxStrSize]; 147 std::atomic<int64_t> int_value_; 148 }; 149 }; 150 151 // Fills |dst| with a string containing one line for each crash key 152 // (excluding the unset ones). 153 // Returns number of chars written, without counting the NUL terminator. 154 // This is used in logging.cc when emitting the crash report abort message. 155 size_t SerializeCrashKeys(char* dst, size_t len); 156 157 void UnregisterAllCrashKeysForTesting(); 158 159 } // namespace base 160 } // namespace perfetto 161 162 #endif // INCLUDE_PERFETTO_EXT_BASE_CRASH_KEYS_H_ 163