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_HEAP_BUFFER_H_
18 #define INCLUDE_PERFETTO_PROTOZERO_SCATTERED_HEAP_BUFFER_H_
19 
20 #include <memory>
21 #include <string>
22 #include <vector>
23 
24 #include "perfetto/base/export.h"
25 #include "perfetto/base/logging.h"
26 #include "perfetto/protozero/root_message.h"
27 #include "perfetto/protozero/scattered_stream_writer.h"
28 
29 namespace protozero {
30 
31 class Message;
32 
33 class PERFETTO_EXPORT ScatteredHeapBuffer
34     : public protozero::ScatteredStreamWriter::Delegate {
35  public:
36   class PERFETTO_EXPORT Slice {
37    public:
38     Slice();
39     explicit Slice(size_t size);
40     Slice(Slice&& slice) noexcept;
41     ~Slice();
42     Slice& operator=(Slice&&);
43 
GetTotalRange()44     inline protozero::ContiguousMemoryRange GetTotalRange() const {
45       return {buffer_.get(), buffer_.get() + size_};
46     }
47 
GetUsedRange()48     inline protozero::ContiguousMemoryRange GetUsedRange() const {
49       return {buffer_.get(), buffer_.get() + size_ - unused_bytes_};
50     }
51 
start()52     uint8_t* start() const { return buffer_.get(); }
size()53     size_t size() const { return size_; }
unused_bytes()54     size_t unused_bytes() const { return unused_bytes_; }
set_unused_bytes(size_t unused_bytes)55     void set_unused_bytes(size_t unused_bytes) {
56       PERFETTO_DCHECK(unused_bytes_ <= size_);
57       unused_bytes_ = unused_bytes;
58     }
59 
60     void Clear();
61 
62    private:
63     std::unique_ptr<uint8_t[]> buffer_;
64     size_t size_;
65     size_t unused_bytes_;
66   };
67 
68   ScatteredHeapBuffer(size_t initial_slice_size_bytes = 128,
69                       size_t maximum_slice_size_bytes = 128 * 1024);
70   ~ScatteredHeapBuffer() override;
71 
72   // protozero::ScatteredStreamWriter::Delegate implementation.
73   protozero::ContiguousMemoryRange GetNewBuffer() override;
74 
75   // Return the slices backing this buffer, adjusted for the number of bytes the
76   // writer has written.
77   const std::vector<Slice>& GetSlices();
78 
79   // Stitch all the slices into a single contiguous buffer.
80   std::vector<uint8_t> StitchSlices();
81 
82   // Note that the returned ranges point back to this buffer and thus cannot
83   // outlive it.
84   std::vector<protozero::ContiguousMemoryRange> GetRanges();
85 
86   // Note that size of the last slice isn't updated to reflect the number of
87   // bytes written by the trace writer.
slices()88   const std::vector<Slice>& slices() const { return slices_; }
89 
set_writer(protozero::ScatteredStreamWriter * writer)90   void set_writer(protozero::ScatteredStreamWriter* writer) {
91     writer_ = writer;
92   }
93 
94   // Update unused_bytes() of the current |Slice| based on the writer's state.
95   void AdjustUsedSizeOfCurrentSlice();
96 
97   // Returns the total size the slices occupy in heap memory (including unused).
98   size_t GetTotalSize();
99 
100   // Reset the contents of this buffer but retain one slice allocation (if it
101   // exists) to be reused for future writes.
102   void Reset();
103 
104  private:
105   size_t next_slice_size_;
106   const size_t maximum_slice_size_;
107   protozero::ScatteredStreamWriter* writer_ = nullptr;
108   std::vector<Slice> slices_;
109 
110   // Used to keep an allocated slice around after this buffer is reset.
111   Slice cached_slice_;
112 };
113 
114 // Helper function to create heap-based protozero messages in one line.
115 // Useful when manually serializing a protozero message (primarily in
116 // tests/utilities). So instead of the following:
117 //   protozero::MyMessage msg;
118 //   protozero::ScatteredHeapBuffer shb;
119 //   protozero::ScatteredStreamWriter writer(&shb);
120 //   shb.set_writer(&writer);
121 //   msg.Reset(&writer);
122 //   ...
123 // You can write:
124 //   protozero::HeapBuffered<protozero::MyMessage> msg;
125 //   msg->set_stuff(...);
126 //   msg.SerializeAsString();
127 template <typename T = ::protozero::Message>
128 class HeapBuffered {
129  public:
HeapBuffered()130   HeapBuffered() : HeapBuffered(4096, 4096) {}
HeapBuffered(size_t initial_slice_size_bytes,size_t maximum_slice_size_bytes)131   HeapBuffered(size_t initial_slice_size_bytes, size_t maximum_slice_size_bytes)
132       : shb_(initial_slice_size_bytes, maximum_slice_size_bytes),
133         writer_(&shb_) {
134     shb_.set_writer(&writer_);
135     msg_.Reset(&writer_);
136   }
137 
138   // This can't be neither copied nor moved because Message hands out pointers
139   // to itself when creating submessages.
140   HeapBuffered(const HeapBuffered&) = delete;
141   HeapBuffered& operator=(const HeapBuffered&) = delete;
142   HeapBuffered(HeapBuffered&&) = delete;
143   HeapBuffered& operator=(HeapBuffered&&) = delete;
144 
get()145   T* get() { return &msg_; }
146   T* operator->() { return &msg_; }
147 
empty()148   bool empty() const { return shb_.slices().empty(); }
149 
SerializeAsArray()150   std::vector<uint8_t> SerializeAsArray() {
151     msg_.Finalize();
152     return shb_.StitchSlices();
153   }
154 
SerializeAsString()155   std::string SerializeAsString() {
156     auto vec = SerializeAsArray();
157     return std::string(reinterpret_cast<const char*>(vec.data()), vec.size());
158   }
159 
GetRanges()160   std::vector<protozero::ContiguousMemoryRange> GetRanges() {
161     msg_.Finalize();
162     return shb_.GetRanges();
163   }
164 
GetSlices()165   const std::vector<ScatteredHeapBuffer::Slice>& GetSlices() {
166     msg_.Finalize();
167     return shb_.GetSlices();
168   }
169 
Reset()170   void Reset() {
171     shb_.Reset();
172     writer_.Reset(protozero::ContiguousMemoryRange{});
173     msg_.Reset(&writer_);
174     PERFETTO_DCHECK(empty());
175   }
176 
177  private:
178   ScatteredHeapBuffer shb_;
179   ScatteredStreamWriter writer_;
180   RootMessage<T> msg_;
181 };
182 
183 }  // namespace protozero
184 
185 #endif  // INCLUDE_PERFETTO_PROTOZERO_SCATTERED_HEAP_BUFFER_H_
186