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