xref: /aosp_15_r20/external/perfetto/src/base/log_ring_buffer.h (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker /*
2*6dbdd20aSAndroid Build Coastguard Worker  * Copyright (C) 2021 The Android Open Source Project
3*6dbdd20aSAndroid Build Coastguard Worker  *
4*6dbdd20aSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*6dbdd20aSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*6dbdd20aSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*6dbdd20aSAndroid Build Coastguard Worker  *
8*6dbdd20aSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*6dbdd20aSAndroid Build Coastguard Worker  *
10*6dbdd20aSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*6dbdd20aSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*6dbdd20aSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*6dbdd20aSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*6dbdd20aSAndroid Build Coastguard Worker  * limitations under the License.
15*6dbdd20aSAndroid Build Coastguard Worker  */
16*6dbdd20aSAndroid Build Coastguard Worker 
17*6dbdd20aSAndroid Build Coastguard Worker #ifndef SRC_BASE_LOG_RING_BUFFER_H_
18*6dbdd20aSAndroid Build Coastguard Worker #define SRC_BASE_LOG_RING_BUFFER_H_
19*6dbdd20aSAndroid Build Coastguard Worker 
20*6dbdd20aSAndroid Build Coastguard Worker #include <stddef.h>
21*6dbdd20aSAndroid Build Coastguard Worker #include <stdio.h>
22*6dbdd20aSAndroid Build Coastguard Worker 
23*6dbdd20aSAndroid Build Coastguard Worker #include <array>
24*6dbdd20aSAndroid Build Coastguard Worker #include <atomic>
25*6dbdd20aSAndroid Build Coastguard Worker 
26*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/string_view.h"
27*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/thread_annotations.h"
28*6dbdd20aSAndroid Build Coastguard Worker 
29*6dbdd20aSAndroid Build Coastguard Worker namespace perfetto {
30*6dbdd20aSAndroid Build Coastguard Worker namespace base {
31*6dbdd20aSAndroid Build Coastguard Worker 
32*6dbdd20aSAndroid Build Coastguard Worker // Defined out of line because a static constexpr requires static storage if
33*6dbdd20aSAndroid Build Coastguard Worker // ODR-used, not worth adding a .cc file just for tests.
34*6dbdd20aSAndroid Build Coastguard Worker constexpr size_t kLogRingBufEntries = 8;
35*6dbdd20aSAndroid Build Coastguard Worker constexpr size_t kLogRingBufMsgLen = 256;
36*6dbdd20aSAndroid Build Coastguard Worker 
37*6dbdd20aSAndroid Build Coastguard Worker // A static non-allocating ring-buffer to hold the most recent log events.
38*6dbdd20aSAndroid Build Coastguard Worker // This class is really an implementation detail of logging.cc. The only reason
39*6dbdd20aSAndroid Build Coastguard Worker // why is fully defined in a dedicated header is for allowing unittesting,
40*6dbdd20aSAndroid Build Coastguard Worker // without leaking extra headers into logging.h (which is a high-fanout header).
41*6dbdd20aSAndroid Build Coastguard Worker // This is used to report the last logs in a crash report when a CHECK/FATAL
42*6dbdd20aSAndroid Build Coastguard Worker // is encountered.
43*6dbdd20aSAndroid Build Coastguard Worker // This class has just an Append() method to insert events into the buffer and
44*6dbdd20aSAndroid Build Coastguard Worker // a Read() to read the events in FIFO order. Read() is non-destructive.
45*6dbdd20aSAndroid Build Coastguard Worker //
46*6dbdd20aSAndroid Build Coastguard Worker // Thread safety considerations:
47*6dbdd20aSAndroid Build Coastguard Worker // - The Append() method can be called concurrently by several threads, unless
48*6dbdd20aSAndroid Build Coastguard Worker //   there are > kLogRingBufEntries concurrent threads. Even if that happens,
49*6dbdd20aSAndroid Build Coastguard Worker //   case some events will contain a mix of strings but the behavior of
50*6dbdd20aSAndroid Build Coastguard Worker //   futher Append() and Read() is still defined.
51*6dbdd20aSAndroid Build Coastguard Worker // - The Read() method is not thread safe but it's fine in practice. Even if
52*6dbdd20aSAndroid Build Coastguard Worker //   it's called concurrently with other Append(), it only causes some partial
53*6dbdd20aSAndroid Build Coastguard Worker //   events to be emitted in output.
54*6dbdd20aSAndroid Build Coastguard Worker // In both cases, we never rely purely on \0, all operations are size-bound.
55*6dbdd20aSAndroid Build Coastguard Worker //
56*6dbdd20aSAndroid Build Coastguard Worker // See logging_unittest.cc for tests.
57*6dbdd20aSAndroid Build Coastguard Worker class LogRingBuffer {
58*6dbdd20aSAndroid Build Coastguard Worker  public:
59*6dbdd20aSAndroid Build Coastguard Worker   LogRingBuffer() = default;
60*6dbdd20aSAndroid Build Coastguard Worker   LogRingBuffer(const LogRingBuffer&) = delete;
61*6dbdd20aSAndroid Build Coastguard Worker   LogRingBuffer& operator=(const LogRingBuffer&) = delete;
62*6dbdd20aSAndroid Build Coastguard Worker   LogRingBuffer(LogRingBuffer&&) = delete;
63*6dbdd20aSAndroid Build Coastguard Worker   LogRingBuffer& operator=(LogRingBuffer&&) = delete;
64*6dbdd20aSAndroid Build Coastguard Worker 
65*6dbdd20aSAndroid Build Coastguard Worker   // This takes three arguments because it fits its only caller (logging.cc).
66*6dbdd20aSAndroid Build Coastguard Worker   // The args are just concatenated together (plus one space before the msg).
Append(StringView tstamp,StringView source,StringView log_msg)67*6dbdd20aSAndroid Build Coastguard Worker   void Append(StringView tstamp, StringView source, StringView log_msg) {
68*6dbdd20aSAndroid Build Coastguard Worker     // Reserve atomically a slot in the ring buffer, so any concurrent Append()
69*6dbdd20aSAndroid Build Coastguard Worker     // won't overlap (unless too many concurrent Append() happen together).
70*6dbdd20aSAndroid Build Coastguard Worker     // There is no strict synchronization here, |event_slot_| is atomic only for
71*6dbdd20aSAndroid Build Coastguard Worker     // the sake of avoiding colliding on the same slot but does NOT guarantee
72*6dbdd20aSAndroid Build Coastguard Worker     // full consistency and integrity of the log messages written in each slot.
73*6dbdd20aSAndroid Build Coastguard Worker     // A release-store (or acq+rel) won't be enough for full consistency. Two
74*6dbdd20aSAndroid Build Coastguard Worker     // threads that race on Append() and take the N+1 and N+2 slots could finish
75*6dbdd20aSAndroid Build Coastguard Worker     // the write in reverse order. So Read() would need to synchronize with
76*6dbdd20aSAndroid Build Coastguard Worker     // something else (either a per-slot atomic flag or with a second atomic
77*6dbdd20aSAndroid Build Coastguard Worker     // counter which is incremented after the snprintf). Both options increase
78*6dbdd20aSAndroid Build Coastguard Worker     // the cost of Append() with no huge benefits (90% of the perfetto services
79*6dbdd20aSAndroid Build Coastguard Worker     // where we use it is single thread, and the log ring buffer is disabled
80*6dbdd20aSAndroid Build Coastguard Worker     // on non-standalone builds like the SDK).
81*6dbdd20aSAndroid Build Coastguard Worker     uint32_t slot = event_slot_.fetch_add(1, std::memory_order_relaxed);
82*6dbdd20aSAndroid Build Coastguard Worker     slot = slot % kLogRingBufEntries;
83*6dbdd20aSAndroid Build Coastguard Worker 
84*6dbdd20aSAndroid Build Coastguard Worker     char* const msg = events_[slot];
85*6dbdd20aSAndroid Build Coastguard Worker     PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(msg, kLogRingBufMsgLen,
86*6dbdd20aSAndroid Build Coastguard Worker                                         "see comments in log_ring_buffer.h")
87*6dbdd20aSAndroid Build Coastguard Worker     snprintf(msg, kLogRingBufMsgLen, "%.*s%.*s %.*s",
88*6dbdd20aSAndroid Build Coastguard Worker              static_cast<int>(tstamp.size()), tstamp.data(),
89*6dbdd20aSAndroid Build Coastguard Worker              static_cast<int>(source.size()), source.data(),
90*6dbdd20aSAndroid Build Coastguard Worker              static_cast<int>(log_msg.size()), log_msg.data());
91*6dbdd20aSAndroid Build Coastguard Worker   }
92*6dbdd20aSAndroid Build Coastguard Worker 
93*6dbdd20aSAndroid Build Coastguard Worker   // Reads back the buffer in FIFO order, up to |len - 1| characters at most
94*6dbdd20aSAndroid Build Coastguard Worker   // (the -1 is because a NUL terminator is always appended, unless |len| == 0).
95*6dbdd20aSAndroid Build Coastguard Worker   // The string written in |dst| is guaranteed to be NUL-terminated, even if
96*6dbdd20aSAndroid Build Coastguard Worker   // |len| < buffer contents length.
97*6dbdd20aSAndroid Build Coastguard Worker   // Returns the number of bytes written in output, excluding the \0 terminator.
Read(char * dst,size_t len)98*6dbdd20aSAndroid Build Coastguard Worker   size_t Read(char* dst, size_t len) {
99*6dbdd20aSAndroid Build Coastguard Worker     if (len == 0)
100*6dbdd20aSAndroid Build Coastguard Worker       return 0;
101*6dbdd20aSAndroid Build Coastguard Worker     // This is a relaxed-load because we don't need to fully synchronize on the
102*6dbdd20aSAndroid Build Coastguard Worker     // writing path for the reasons described in the fetch_add() above.
103*6dbdd20aSAndroid Build Coastguard Worker     const uint32_t event_slot = event_slot_.load(std::memory_order_relaxed);
104*6dbdd20aSAndroid Build Coastguard Worker     size_t dst_written = 0;
105*6dbdd20aSAndroid Build Coastguard Worker     for (uint32_t pos = 0; pos < kLogRingBufEntries; ++pos) {
106*6dbdd20aSAndroid Build Coastguard Worker       const uint32_t slot = (event_slot + pos) % kLogRingBufEntries;
107*6dbdd20aSAndroid Build Coastguard Worker       const char* src = events_[slot];
108*6dbdd20aSAndroid Build Coastguard Worker       if (*src == '\0')
109*6dbdd20aSAndroid Build Coastguard Worker         continue;  // Empty slot. Skip.
110*6dbdd20aSAndroid Build Coastguard Worker       char* const wptr = dst + dst_written;
111*6dbdd20aSAndroid Build Coastguard Worker       // |src| might not be null terminated. This can happen if some
112*6dbdd20aSAndroid Build Coastguard Worker       // thread-race happened. Limit the copy length.
113*6dbdd20aSAndroid Build Coastguard Worker       const size_t limit = std::min(len - dst_written, kLogRingBufMsgLen);
114*6dbdd20aSAndroid Build Coastguard Worker       for (size_t i = 0; i < limit; ++i) {
115*6dbdd20aSAndroid Build Coastguard Worker         const char c = src[i];
116*6dbdd20aSAndroid Build Coastguard Worker         ++dst_written;
117*6dbdd20aSAndroid Build Coastguard Worker         if (c == '\0' || i == limit - 1) {
118*6dbdd20aSAndroid Build Coastguard Worker           wptr[i] = '\n';
119*6dbdd20aSAndroid Build Coastguard Worker           break;
120*6dbdd20aSAndroid Build Coastguard Worker         }
121*6dbdd20aSAndroid Build Coastguard Worker         // Skip non-printable ASCII characters to avoid confusing crash reports.
122*6dbdd20aSAndroid Build Coastguard Worker         // Note that this deliberately mangles \n. Log messages should not have
123*6dbdd20aSAndroid Build Coastguard Worker         // a \n in the middle and are NOT \n terminated. The trailing \n between
124*6dbdd20aSAndroid Build Coastguard Worker         // each line is appended by the if () branch above.
125*6dbdd20aSAndroid Build Coastguard Worker         const bool is_printable = c >= ' ' && c <= '~';
126*6dbdd20aSAndroid Build Coastguard Worker         wptr[i] = is_printable ? c : '?';
127*6dbdd20aSAndroid Build Coastguard Worker       }
128*6dbdd20aSAndroid Build Coastguard Worker     }
129*6dbdd20aSAndroid Build Coastguard Worker     // Ensure that the output string is null-terminated.
130*6dbdd20aSAndroid Build Coastguard Worker     PERFETTO_DCHECK(dst_written <= len);
131*6dbdd20aSAndroid Build Coastguard Worker     if (dst_written == len) {
132*6dbdd20aSAndroid Build Coastguard Worker       // In case of truncation we replace the last char with \0. But the return
133*6dbdd20aSAndroid Build Coastguard Worker       // value is the number of chars without \0, hence the --.
134*6dbdd20aSAndroid Build Coastguard Worker       dst[--dst_written] = '\0';
135*6dbdd20aSAndroid Build Coastguard Worker     } else {
136*6dbdd20aSAndroid Build Coastguard Worker       dst[dst_written] = '\0';
137*6dbdd20aSAndroid Build Coastguard Worker     }
138*6dbdd20aSAndroid Build Coastguard Worker     return dst_written;
139*6dbdd20aSAndroid Build Coastguard Worker   }
140*6dbdd20aSAndroid Build Coastguard Worker 
141*6dbdd20aSAndroid Build Coastguard Worker  private:
142*6dbdd20aSAndroid Build Coastguard Worker   using EventBuf = char[kLogRingBufMsgLen];
143*6dbdd20aSAndroid Build Coastguard Worker   EventBuf events_[kLogRingBufEntries]{};
144*6dbdd20aSAndroid Build Coastguard Worker 
145*6dbdd20aSAndroid Build Coastguard Worker   static_assert((kLogRingBufEntries & (kLogRingBufEntries - 1)) == 0,
146*6dbdd20aSAndroid Build Coastguard Worker                 "kLogRingBufEntries must be a power of two");
147*6dbdd20aSAndroid Build Coastguard Worker 
148*6dbdd20aSAndroid Build Coastguard Worker   // A monotonically increasing counter incremented on each event written.
149*6dbdd20aSAndroid Build Coastguard Worker   // It determines which of the kLogRingBufEntries indexes in |events_| should
150*6dbdd20aSAndroid Build Coastguard Worker   // be used next.
151*6dbdd20aSAndroid Build Coastguard Worker   // It grows >> kLogRingBufEntries, it's supposed to be always used
152*6dbdd20aSAndroid Build Coastguard Worker   // mod(kLogRingBufEntries). A static_assert in the .cc file ensures that
153*6dbdd20aSAndroid Build Coastguard Worker   // kLogRingBufEntries is a power of two so wraps are aligned.
154*6dbdd20aSAndroid Build Coastguard Worker   std::atomic<uint32_t> event_slot_{};
155*6dbdd20aSAndroid Build Coastguard Worker };
156*6dbdd20aSAndroid Build Coastguard Worker 
157*6dbdd20aSAndroid Build Coastguard Worker }  // namespace base
158*6dbdd20aSAndroid Build Coastguard Worker }  // namespace perfetto
159*6dbdd20aSAndroid Build Coastguard Worker 
160*6dbdd20aSAndroid Build Coastguard Worker #endif  // SRC_BASE_LOG_RING_BUFFER_H_
161