1 /* 2 * Copyright (C) 2017 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_PROTOZERO_SCATTERED_STREAM_WRITER_H_ 18 #define INCLUDE_PERFETTO_PROTOZERO_SCATTERED_STREAM_WRITER_H_ 19 20 #include <assert.h> 21 #include <stddef.h> 22 #include <stdint.h> 23 #include <string.h> 24 25 #include "perfetto/base/export.h" 26 #include "perfetto/base/utils.h" 27 #include "perfetto/protozero/contiguous_memory_range.h" 28 29 namespace protozero { 30 31 // This class deals with the following problem: append-only proto messages want 32 // to write a stream of bytes, without caring about the implementation of the 33 // underlying buffer (which concretely will be either the trace ring buffer 34 // or a heap-allocated buffer). The main deal is: proto messages don't know in 35 // advance what their size will be. 36 // Due to the tracing buffer being split into fixed-size chunks, on some 37 // occasions, these writes need to be spread over two (or more) non-contiguous 38 // chunks of memory. Similarly, when the buffer is backed by the heap, we want 39 // to avoid realloc() calls, as they might cause a full copy of the contents 40 // of the buffer. 41 // The purpose of this class is to abstract away the non-contiguous write logic. 42 // This class knows how to deal with writes as long as they fall in the same 43 // ContiguousMemoryRange and defers the chunk-chaining logic to the Delegate. 44 class PERFETTO_EXPORT ScatteredStreamWriter { 45 public: 46 class PERFETTO_EXPORT Delegate { 47 public: 48 virtual ~Delegate(); 49 virtual ContiguousMemoryRange GetNewBuffer() = 0; 50 }; 51 52 explicit ScatteredStreamWriter(Delegate* delegate); 53 ~ScatteredStreamWriter(); 54 WriteByte(uint8_t value)55 inline void WriteByte(uint8_t value) { 56 if (write_ptr_ >= cur_range_.end) 57 Extend(); 58 *write_ptr_++ = value; 59 } 60 61 // Assumes that the caller checked that there is enough headroom. 62 // TODO(primiano): perf optimization, this is a tracing hot path. The 63 // compiler can make strong optimization on memcpy if the size arg is a 64 // constexpr. Make a templated variant of this for fixed-size writes. 65 // TODO(primiano): restrict / noalias might also help. WriteBytesUnsafe(const uint8_t * src,size_t size)66 inline void WriteBytesUnsafe(const uint8_t* src, size_t size) { 67 uint8_t* const end = write_ptr_ + size; 68 assert(end <= cur_range_.end); 69 memcpy(write_ptr_, src, size); 70 write_ptr_ = end; 71 } 72 WriteBytes(const uint8_t * src,size_t size)73 inline void WriteBytes(const uint8_t* src, size_t size) { 74 uint8_t* const end = write_ptr_ + size; 75 if (PERFETTO_LIKELY(end <= cur_range_.end)) 76 return WriteBytesUnsafe(src, size); 77 WriteBytesSlowPath(src, size); 78 } 79 80 void WriteBytesSlowPath(const uint8_t* src, size_t size); 81 82 // Reserves a fixed amount of bytes to be backfilled later. The reserved range 83 // is guaranteed to be contiguous and not span across chunks. |size| has to be 84 // <= than the size of a new buffer returned by the Delegate::GetNewBuffer(). 85 uint8_t* ReserveBytes(size_t size); 86 87 // Fast (but unsafe) version of the above. The caller must have previously 88 // checked that there are at least |size| contiguous bytes available. 89 // Returns only the start pointer of the reservation. ReserveBytesUnsafe(size_t size)90 uint8_t* ReserveBytesUnsafe(size_t size) { 91 uint8_t* begin = write_ptr_; 92 write_ptr_ += size; 93 assert(write_ptr_ <= cur_range_.end); 94 return begin; 95 } 96 97 // Resets the buffer boundaries and the write pointer to the given |range|. 98 // Subsequent WriteByte(s) will write into |range|. 99 void Reset(ContiguousMemoryRange range); 100 101 // Number of contiguous free bytes in |cur_range_| that can be written without 102 // requesting a new buffer. bytes_available()103 size_t bytes_available() const { 104 return static_cast<size_t>(cur_range_.end - write_ptr_); 105 } 106 write_ptr()107 uint8_t* write_ptr() const { return write_ptr_; } 108 written()109 uint64_t written() const { 110 return written_previously_ + 111 static_cast<uint64_t>(write_ptr_ - cur_range_.begin); 112 } 113 114 private: 115 ScatteredStreamWriter(const ScatteredStreamWriter&) = delete; 116 ScatteredStreamWriter& operator=(const ScatteredStreamWriter&) = delete; 117 118 void Extend(); 119 120 Delegate* const delegate_; 121 ContiguousMemoryRange cur_range_; 122 uint8_t* write_ptr_; 123 uint64_t written_previously_ = 0; 124 }; 125 126 } // namespace protozero 127 128 #endif // INCLUDE_PERFETTO_PROTOZERO_SCATTERED_STREAM_WRITER_H_ 129