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