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(×tamp, 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