|  | /* | 
|  | * Copyright (C) 2021 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #ifndef SRC_BASE_LOG_RING_BUFFER_H_ | 
|  | #define SRC_BASE_LOG_RING_BUFFER_H_ | 
|  |  | 
|  | #include <stddef.h> | 
|  | #include <stdio.h> | 
|  |  | 
|  | #include <array> | 
|  | #include <atomic> | 
|  |  | 
|  | #include "perfetto/ext/base/string_view.h" | 
|  | #include "perfetto/ext/base/thread_annotations.h" | 
|  |  | 
|  | namespace perfetto { | 
|  | namespace base { | 
|  |  | 
|  | // Defined out of line because a static constexpr requires static storage if | 
|  | // ODR-used, not worth adding a .cc file just for tests. | 
|  | constexpr size_t kLogRingBufEntries = 8; | 
|  | constexpr size_t kLogRingBufMsgLen = 256; | 
|  |  | 
|  | // A static non-allocating ring-buffer to hold the most recent log events. | 
|  | // This class is really an implementation detail of logging.cc. The only reason | 
|  | // why is fully defined in a dedicated header is for allowing unittesting, | 
|  | // without leaking extra headers into logging.h (which is a high-fanout header). | 
|  | // This is used to report the last logs in a crash report when a CHECK/FATAL | 
|  | // is encountered. | 
|  | // This class has just an Append() method to insert events into the buffer and | 
|  | // a Read() to read the events in FIFO order. Read() is non-destructive. | 
|  | // | 
|  | // Thread safety considerations: | 
|  | // - The Append() method can be called concurrently by several threads, unless | 
|  | //   there are > kLogRingBufEntries concurrent threads. Even if that happens, | 
|  | //   case some events will contain a mix of strings but the behavior of | 
|  | //   futher Append() and Read() is still defined. | 
|  | // - The Read() method is not thread safe but it's fine in practice. Even if | 
|  | //   it's called concurrently with other Append(), it only causes some partial | 
|  | //   events to be emitted in output. | 
|  | // In both cases, we never rely purely on \0, all operations are size-bound. | 
|  | // | 
|  | // See logging_unittest.cc for tests. | 
|  | class LogRingBuffer { | 
|  | public: | 
|  | LogRingBuffer() = default; | 
|  | LogRingBuffer(const LogRingBuffer&) = delete; | 
|  | LogRingBuffer& operator=(const LogRingBuffer&) = delete; | 
|  | LogRingBuffer(LogRingBuffer&&) = delete; | 
|  | LogRingBuffer& operator=(LogRingBuffer&&) = delete; | 
|  |  | 
|  | // This takes three arguments because it fits its only caller (logging.cc). | 
|  | // The args are just concatenated together (plus one space before the msg). | 
|  | void Append(StringView tstamp, StringView source, StringView log_msg) { | 
|  | // Reserve atomically a slot in the ring buffer, so any concurrent Append() | 
|  | // won't overlap (unless too many concurrent Append() happen together). | 
|  | // There is no strict synchronization here, |event_slot_| is atomic only for | 
|  | // the sake of avoiding colliding on the same slot but does NOT guarantee | 
|  | // full consistency and integrity of the log messages written in each slot. | 
|  | // A release-store (or acq+rel) won't be enough for full consistency. Two | 
|  | // threads that race on Append() and take the N+1 and N+2 slots could finish | 
|  | // the write in reverse order. So Read() would need to synchronize with | 
|  | // something else (either a per-slot atomic flag or with a second atomic | 
|  | // counter which is incremented after the snprintf). Both options increase | 
|  | // the cost of Append() with no huge benefits (90% of the perfetto services | 
|  | // where we use it is single thread, and the log ring buffer is disabled | 
|  | // on non-standalone builds like the SDK). | 
|  | uint32_t slot = event_slot_.fetch_add(1, std::memory_order_relaxed); | 
|  | slot = slot % kLogRingBufEntries; | 
|  |  | 
|  | char* const msg = events_[slot]; | 
|  | PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(msg, kLogRingBufMsgLen, | 
|  | "see comments in log_ring_buffer.h") | 
|  | snprintf(msg, kLogRingBufMsgLen, "%.*s%.*s %.*s", | 
|  | static_cast<int>(tstamp.size()), tstamp.data(), | 
|  | static_cast<int>(source.size()), source.data(), | 
|  | static_cast<int>(log_msg.size()), log_msg.data()); | 
|  | } | 
|  |  | 
|  | // Reads back the buffer in FIFO order, up to |len - 1| characters at most | 
|  | // (the -1 is because a NUL terminator is always appended, unless |len| == 0). | 
|  | // The string written in |dst| is guaranteed to be NUL-terminated, even if | 
|  | // |len| < buffer contents length. | 
|  | // Returns the number of bytes written in output, excluding the \0 terminator. | 
|  | size_t Read(char* dst, size_t len) { | 
|  | if (len == 0) | 
|  | return 0; | 
|  | // This is a relaxed-load because we don't need to fully synchronize on the | 
|  | // writing path for the reasons described in the fetch_add() above. | 
|  | const uint32_t event_slot = event_slot_.load(std::memory_order_relaxed); | 
|  | size_t dst_written = 0; | 
|  | for (uint32_t pos = 0; pos < kLogRingBufEntries; ++pos) { | 
|  | const uint32_t slot = (event_slot + pos) % kLogRingBufEntries; | 
|  | const char* src = events_[slot]; | 
|  | if (*src == '\0') | 
|  | continue;  // Empty slot. Skip. | 
|  | char* const wptr = dst + dst_written; | 
|  | // |src| might not be null terminated. This can happen if some | 
|  | // thread-race happened. Limit the copy length. | 
|  | const size_t limit = std::min(len - dst_written, kLogRingBufMsgLen); | 
|  | for (size_t i = 0; i < limit; ++i) { | 
|  | const char c = src[i]; | 
|  | ++dst_written; | 
|  | if (c == '\0' || i == limit - 1) { | 
|  | wptr[i] = '\n'; | 
|  | break; | 
|  | } | 
|  | // Skip non-printable ASCII characters to avoid confusing crash reports. | 
|  | // Note that this deliberately mangles \n. Log messages should not have | 
|  | // a \n in the middle and are NOT \n terminated. The trailing \n between | 
|  | // each line is appended by the if () branch above. | 
|  | const bool is_printable = c >= ' ' && c <= '~'; | 
|  | wptr[i] = is_printable ? c : '?'; | 
|  | } | 
|  | } | 
|  | // Ensure that the output string is null-terminated. | 
|  | PERFETTO_DCHECK(dst_written <= len); | 
|  | if (dst_written == len) { | 
|  | // In case of truncation we replace the last char with \0. But the return | 
|  | // value is the number of chars without \0, hence the --. | 
|  | dst[--dst_written] = '\0'; | 
|  | } else { | 
|  | dst[dst_written] = '\0'; | 
|  | } | 
|  | return dst_written; | 
|  | } | 
|  |  | 
|  | private: | 
|  | using EventBuf = char[kLogRingBufMsgLen]; | 
|  | EventBuf events_[kLogRingBufEntries]{}; | 
|  |  | 
|  | static_assert((kLogRingBufEntries & (kLogRingBufEntries - 1)) == 0, | 
|  | "kLogRingBufEntries must be a power of two"); | 
|  |  | 
|  | // A monotonically increasing counter incremented on each event written. | 
|  | // It determines which of the kLogRingBufEntries indexes in |events_| should | 
|  | // be used next. | 
|  | // It grows >> kLogRingBufEntries, it's supposed to be always used | 
|  | // mod(kLogRingBufEntries). A static_assert in the .cc file ensures that | 
|  | // kLogRingBufEntries is a power of two so wraps are aligned. | 
|  | std::atomic<uint32_t> event_slot_{}; | 
|  | }; | 
|  |  | 
|  | }  // namespace base | 
|  | }  // namespace perfetto | 
|  |  | 
|  | #endif  // SRC_BASE_LOG_RING_BUFFER_H_ |