xref: /aosp_15_r20/external/perfetto/src/trace_processor/util/trace_blob_view_reader.h (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2024 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 SRC_TRACE_PROCESSOR_UTIL_TRACE_BLOB_VIEW_READER_H_
18 #define SRC_TRACE_PROCESSOR_UTIL_TRACE_BLOB_VIEW_READER_H_
19 
20 #include <cstddef>
21 #include <cstdint>
22 #include <cstdio>
23 #include <cstring>
24 #include <optional>
25 #include <string_view>
26 
27 #include "perfetto/base/logging.h"
28 #include "perfetto/ext/base/circular_queue.h"
29 #include "perfetto/public/compiler.h"
30 #include "perfetto/trace_processor/trace_blob_view.h"
31 
32 namespace perfetto::trace_processor::util {
33 
34 // Helper class which handles all the complexity of reading pieces of data which
35 // span across multiple TraceBlobView chunks. It takes care of:
36 //  1) Buffering data until it can be read.
37 //  2) Stitching together the cross-chunk spanning pieces.
38 //  3) Dropping data when it is no longer necessary to be buffered.
39 class TraceBlobViewReader {
40  private:
41   struct Entry {
42     // File offset of the first byte in `data`.
43     size_t start_offset;
44     TraceBlobView data;
end_offsetEntry45     size_t end_offset() const { return start_offset + data.size(); }
46   };
47 
48  public:
49   class Iterator {
50    public:
51     ~Iterator() = default;
52 
53     Iterator(const Iterator&) = default;
54     Iterator& operator=(const Iterator&) = default;
55 
56     Iterator(Iterator&&) = default;
57     Iterator& operator=(Iterator&&) = default;
58 
59     // Tries to advance the iterator `size` bytes forward. Returns true if
60     // the advance was successful and false if it would overflow the iterator.
61     // If false is returned, the state of the iterator is not changed.
MaybeAdvance(size_t delta)62     bool MaybeAdvance(size_t delta) {
63       file_offset_ += delta;
64       if (PERFETTO_LIKELY(file_offset_ < iter_->end_offset())) {
65         return true;
66       }
67       if (file_offset_ == end_offset_) {
68         return true;
69       }
70       if (file_offset_ > end_offset_) {
71         file_offset_ -= delta;
72         return false;
73       }
74       do {
75         ++iter_;
76       } while (file_offset_ >= iter_->end_offset());
77       return true;
78     }
79 
80     // Tries to read `size` bytes from the iterator.  Returns a TraceBlobView
81     // containing the data if `size` bytes were available and std::nullopt
82     // otherwise. If std::nullopt is returned, the state of the iterator is not
83     // changed.
MaybeRead(size_t delta)84     std::optional<TraceBlobView> MaybeRead(size_t delta) {
85       std::optional<TraceBlobView> tbv =
86           reader_->SliceOff(file_offset(), delta);
87       if (PERFETTO_LIKELY(tbv)) {
88         PERFETTO_CHECK(MaybeAdvance(delta));
89       }
90       return tbv;
91     }
92 
93     // Tries to find a byte equal to |chr| in the iterator and, if found,
94     // advance to it. Returns a TraceBlobView containing the data if the byte
95     // was found and could be advanced to and std::nullopt if no such byte was
96     // found before the end of the iterator. If std::nullopt is returned, the
97     // state of the iterator is not changed.
MaybeFindAndRead(uint8_t chr)98     std::optional<TraceBlobView> MaybeFindAndRead(uint8_t chr) {
99       size_t begin = file_offset();
100       if (!MaybeFindAndAdvanceInner(chr)) {
101         return std::nullopt;
102       }
103       std::optional<TraceBlobView> tbv =
104           reader_->SliceOff(begin, file_offset() - begin);
105       PERFETTO_CHECK(tbv);
106       PERFETTO_CHECK(MaybeAdvance(1));
107       return tbv;
108     }
109 
110     uint8_t operator*() const {
111       PERFETTO_DCHECK(file_offset_ < iter_->end_offset());
112       return iter_->data.data()[file_offset_ - iter_->start_offset];
113     }
114 
115     explicit operator bool() const { return file_offset_ != end_offset_; }
116 
file_offset()117     size_t file_offset() const { return file_offset_; }
118 
119    private:
120     friend TraceBlobViewReader;
121 
Iterator(const TraceBlobViewReader * reader,base::CircularQueue<Entry>::Iterator iter,size_t file_offset,size_t end_offset)122     Iterator(const TraceBlobViewReader* reader,
123              base::CircularQueue<Entry>::Iterator iter,
124              size_t file_offset,
125              size_t end_offset)
126         : reader_(reader),
127           iter_(iter),
128           file_offset_(file_offset),
129           end_offset_(end_offset) {}
130 
131     // Tries to find a byte equal to |chr| in the iterator and, if found,
132     // advance to it. Returns true if the byte was found and could be advanced
133     // to and false if no such byte was found before the end of the iterator. If
134     // false is returned, the state of the iterator is not changed.
MaybeFindAndAdvanceInner(uint8_t chr)135     bool MaybeFindAndAdvanceInner(uint8_t chr) {
136       size_t off = file_offset_;
137       while (off < end_offset_) {
138         size_t iter_off = off - iter_->start_offset;
139         size_t iter_rem = iter_->data.size() - iter_off;
140         const auto* p = reinterpret_cast<const uint8_t*>(
141             memchr(iter_->data.data() + iter_off, chr, iter_rem));
142         if (p) {
143           file_offset_ =
144               iter_->start_offset + static_cast<size_t>(p - iter_->data.data());
145           return true;
146         }
147         off = iter_->end_offset();
148         ++iter_;
149       }
150       return false;
151     }
152 
153     const TraceBlobViewReader* reader_;
154     base::CircularQueue<Entry>::Iterator iter_;
155     size_t file_offset_;
156     size_t end_offset_;
157   };
158 
GetIterator()159   Iterator GetIterator() const {
160     return {this, data_.begin(), start_offset(), end_offset()};
161   }
162 
163   // Adds a `TraceBlobView` at the back.
164   void PushBack(TraceBlobView);
165 
166   // Shrinks the buffer by dropping data from the front of the buffer until the
167   // given offset is reached. If not enough data is present as much data as
168   // possible will be dropped and `false` will be returned.
169   //
170   // Note:
171   //  * if `offset` < 'file_offset()' this method will CHECK fail.
172   //  * calling this function invalidates all iterators created from this
173   //  reader.
174   bool PopFrontUntil(size_t offset);
175 
176   // Shrinks the buffer by dropping `bytes` from the front of the buffer. If not
177   // enough data is present as much data as possible will be dropped and `false`
178   // will be returned.
179   //
180   // Note: calling this function invalidates all iterators created from this
181   // reader.
PopFrontBytes(size_t bytes)182   bool PopFrontBytes(size_t bytes) {
183     return PopFrontUntil(start_offset() + bytes);
184   }
185 
186   // Creates a TraceBlobView by slicing this reader starting at |offset| and
187   // spanning |length| bytes.
188   //
189   // If possible, this method will try to avoid copies and simply slice an
190   // input TraceBlobView. However, that may not be possible and it so, it will
191   // allocate a new chunk of memory and copy over the data instead.
192   //
193   // NOTE: If `offset` < 'file_offset()' this method will CHECK fail.
194   std::optional<TraceBlobView> SliceOff(size_t offset, size_t length) const;
195 
196   // Similar to SliceOff but this method will not combine slices but instead
197   // potentially return multiple chunks. Useful if we are extracting slices to
198   // forward them to a `ChunkedTraceReader`.
199   std::vector<TraceBlobView> MultiSliceOff(size_t offset, size_t length) const;
200 
201   // Returns the offset to the start of the available data.
start_offset()202   size_t start_offset() const {
203     return data_.empty() ? end_offset_ : data_.front().start_offset;
204   }
205 
206   // Returns the offset to the end of the available data.
end_offset()207   size_t end_offset() const { return end_offset_; }
208 
209   // Returns the number of bytes of buffered data.
avail()210   size_t avail() const { return end_offset() - start_offset(); }
211 
empty()212   bool empty() const { return data_.empty(); }
213 
214  private:
215   template <typename Visitor>
216   auto SliceOffImpl(size_t offset, size_t length, Visitor& visitor) const;
217 
218   // CircularQueue has no const_iterator, so mutable is needed to access it from
219   // const methods.
220   mutable base::CircularQueue<Entry> data_;
221   size_t end_offset_ = 0;
222 };
223 
224 }  // namespace perfetto::trace_processor::util
225 
226 #endif  // SRC_TRACE_PROCESSOR_UTIL_TRACE_BLOB_VIEW_READER_H_
227