xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/perf/spe_tokenizer.cc (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 #include "src/trace_processor/importers/perf/spe_tokenizer.h"
18 
19 #include <cstdint>
20 #include <cstring>
21 #include <memory>
22 #include <optional>
23 #include <utility>
24 
25 #include "perfetto/base/logging.h"
26 #include "perfetto/base/status.h"
27 #include "perfetto/ext/base/status_or.h"
28 #include "perfetto/trace_processor/trace_blob_view.h"
29 #include "src/trace_processor/importers/common/clock_tracker.h"
30 #include "src/trace_processor/importers/perf/aux_data_tokenizer.h"
31 #include "src/trace_processor/importers/perf/aux_record.h"
32 #include "src/trace_processor/importers/perf/itrace_start_record.h"
33 #include "src/trace_processor/importers/perf/spe.h"
34 #include "src/trace_processor/sorter/trace_sorter.h"
35 #include "src/trace_processor/storage/stats.h"
36 #include "src/trace_processor/types/trace_processor_context.h"
37 #include "src/trace_processor/util/trace_blob_view_reader.h"
38 
39 namespace perfetto::trace_processor::perf_importer {
40 namespace {
41 
42 class SpeStream : public AuxDataStream {
43  public:
SpeStream(TraceProcessorContext * context,AuxStream * stream)44   explicit SpeStream(TraceProcessorContext* context, AuxStream* stream)
45       : context_(context), stream_(*stream) {}
46 
OnDataLoss(uint64_t)47   void OnDataLoss(uint64_t) override {
48     // Clear any inflight parsing.
49     buffer_.PopFrontUntil(buffer_.end_offset());
50   }
51 
OnItraceStartRecord(ItraceStartRecord)52   base::Status OnItraceStartRecord(ItraceStartRecord) override {
53     // Clear any inflight parsing.
54     buffer_.PopFrontUntil(buffer_.end_offset());
55     return base::OkStatus();
56   }
57 
Parse(AuxRecord aux,TraceBlobView data)58   base::Status Parse(AuxRecord aux, TraceBlobView data) override {
59     last_aux_record_ = std::move(aux);
60     buffer_.PushBack(std::move(data));
61     while (ProcessRecord()) {
62     }
63     return base::OkStatus();
64   }
65 
NotifyEndOfStream()66   base::Status NotifyEndOfStream() override { return base::OkStatus(); }
67 
68  private:
69   // A SPE trace is just a stream of SPE records which in turn are a collection
70   // of packets. An End or Timestamp packet signals the end of the current
71   // record. This method will read the stream until an end of record condition,
72   // emit the record to the sorter, consume the bytes from the buffer, and
73   // finally return true. If not enough data is available to parse a full record
74   // it returns false and the internal buffer is not modified.
ProcessRecord()75   bool ProcessRecord() {
76     for (auto it = buffer_.GetIterator(); it;) {
77       uint8_t byte_0 = *it;
78       // Must be true (we passed the for loop condition).
79       it.MaybeAdvance(1);
80 
81       if (spe::IsExtendedHeader(byte_0)) {
82         if (!it) {
83           return false;
84         }
85         uint8_t byte_1 = *it;
86         uint8_t payload_size =
87             spe::ExtendedHeader(byte_0, byte_1).GetPayloadSize();
88         if (!it.MaybeAdvance(payload_size + 1)) {
89           return false;
90         }
91         continue;
92       }
93 
94       spe::ShortHeader short_header(byte_0);
95       uint8_t payload_size = short_header.GetPayloadSize();
96       if (!it.MaybeAdvance(payload_size)) {
97         return false;
98       }
99 
100       if (short_header.IsEndPacket()) {
101         size_t record_len = it.file_offset() - buffer_.start_offset();
102         TraceBlobView record =
103             *buffer_.SliceOff(buffer_.start_offset(), record_len);
104         buffer_.PopFrontUntil(it.file_offset());
105         Emit(std::move(record), std::nullopt);
106         return true;
107       }
108 
109       if (short_header.IsTimestampPacket()) {
110         size_t record_len = it.file_offset() - buffer_.start_offset();
111         TraceBlobView record =
112             *buffer_.SliceOff(buffer_.start_offset(), record_len);
113         buffer_.PopFrontUntil(it.file_offset());
114         Emit(std::move(record), ReadTimestamp(record));
115         return true;
116       }
117     }
118     return false;
119   }
120 
ReadTimestamp(const TraceBlobView & record)121   uint64_t ReadTimestamp(const TraceBlobView& record) {
122     PERFETTO_CHECK(record.size() >= 8);
123     uint64_t timestamp;
124     memcpy(&timestamp, record.data() + record.size() - 8, 8);
125     return timestamp;
126   }
127 
128   // Emits a record to the sorter. You can optionally pass the cycles value
129   // contained in the timestamp packet which will be used to determine the trace
130   // timestamp.
Emit(TraceBlobView record,std::optional<uint64_t> cycles)131   void Emit(TraceBlobView record, std::optional<uint64_t> cycles) {
132     PERFETTO_CHECK(last_aux_record_);
133 
134     std::optional<uint64_t> perf_time;
135 
136     if (cycles.has_value()) {
137       perf_time = stream_.ConvertTscToPerfTime(*cycles);
138     } else {
139       context_->storage->IncrementStats(stats::spe_no_timestamp);
140     }
141 
142     if (!perf_time && last_aux_record_->sample_id.has_value()) {
143       perf_time = last_aux_record_->sample_id->time();
144     }
145 
146     if (!perf_time) {
147       context_->sorter->PushSpeRecord(context_->sorter->max_timestamp(),
148                                       std::move(record));
149       return;
150     }
151 
152     base::StatusOr<int64_t> trace_time = context_->clock_tracker->ToTraceTime(
153         last_aux_record_->attr->clock_id(), static_cast<int64_t>(*perf_time));
154     if (!trace_time.ok()) {
155       context_->storage->IncrementStats(stats::spe_record_dropped);
156       return;
157     }
158     context_->sorter->PushSpeRecord(*trace_time, std::move(record));
159   }
160 
161   TraceProcessorContext* const context_;
162   AuxStream& stream_;
163   util::TraceBlobViewReader buffer_;
164   std::optional<AuxRecord> last_aux_record_;
165 };
166 
167 }  // namespace
168 
169 SpeTokenizer::~SpeTokenizer() = default;
InitializeAuxDataStream(AuxStream * stream)170 base::StatusOr<AuxDataStream*> SpeTokenizer::InitializeAuxDataStream(
171     AuxStream* stream) {
172   streams_.push_back(std::make_unique<SpeStream>(context_, stream));
173   return streams_.back().get();
174 }
175 
176 }  // namespace perfetto::trace_processor::perf_importer
177