• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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