1 /*
2 * Copyright (C) 2023 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/perf_data_tokenizer.h"
18
19 #include <cstddef>
20 #include <cstdint>
21 #include <cstring>
22 #include <optional>
23 #include <string>
24 #include <utility>
25 #include <vector>
26
27 #include "perfetto/base/flat_set.h"
28 #include "perfetto/base/logging.h"
29 #include "perfetto/base/status.h"
30 #include "perfetto/ext/base/status_or.h"
31 #include "perfetto/public/compiler.h"
32 #include "perfetto/trace_processor/trace_blob_view.h"
33 #include "protos/perfetto/trace/clock_snapshot.pbzero.h"
34 #include "protos/third_party/simpleperf/record_file.pbzero.h"
35 #include "src/trace_processor/importers/common/clock_tracker.h"
36 #include "src/trace_processor/importers/common/slice_tracker.h"
37 #include "src/trace_processor/importers/perf/attrs_section_reader.h"
38 #include "src/trace_processor/importers/perf/dso_tracker.h"
39 #include "src/trace_processor/importers/perf/features.h"
40 #include "src/trace_processor/importers/perf/perf_event.h"
41 #include "src/trace_processor/importers/perf/perf_file.h"
42 #include "src/trace_processor/importers/perf/perf_session.h"
43 #include "src/trace_processor/importers/perf/reader.h"
44 #include "src/trace_processor/importers/perf/record.h"
45 #include "src/trace_processor/importers/proto/perf_sample_tracker.h"
46 #include "src/trace_processor/sorter/trace_sorter.h"
47 #include "src/trace_processor/storage/stats.h"
48 #include "src/trace_processor/util/build_id.h"
49 #include "src/trace_processor/util/status_macros.h"
50
51 namespace perfetto {
52 namespace trace_processor {
53 namespace perf_importer {
54 namespace {
55
AddIds(uint8_t id_offset,uint64_t flags,base::FlatSet<uint8_t> & feature_ids)56 void AddIds(uint8_t id_offset,
57 uint64_t flags,
58 base::FlatSet<uint8_t>& feature_ids) {
59 for (size_t i = 0; i < sizeof(flags) * 8; ++i) {
60 if (flags & 1) {
61 feature_ids.insert(id_offset);
62 }
63 flags >>= 1;
64 ++id_offset;
65 }
66 }
67
ExtractFeatureIds(const uint64_t & flags,const uint64_t (& flags1)[3])68 base::FlatSet<uint8_t> ExtractFeatureIds(const uint64_t& flags,
69 const uint64_t (&flags1)[3]) {
70 base::FlatSet<uint8_t> feature_ids;
71 AddIds(0, flags, feature_ids);
72 AddIds(64, flags1[0], feature_ids);
73 AddIds(128, flags1[1], feature_ids);
74 AddIds(192, flags1[2], feature_ids);
75 return feature_ids;
76 }
77
ReadTime(const Record & record,std::optional<uint64_t> & time)78 bool ReadTime(const Record& record, std::optional<uint64_t>& time) {
79 if (!record.attr) {
80 time = std::nullopt;
81 return true;
82 }
83 Reader reader(record.payload.copy());
84 if (record.header.type != PERF_RECORD_SAMPLE) {
85 std::optional<size_t> offset = record.attr->time_offset_from_end();
86 if (!offset.has_value()) {
87 time = std::nullopt;
88 return true;
89 }
90 if (*offset > reader.size_left()) {
91 return false;
92 }
93 return reader.Skip(reader.size_left() - *offset) &&
94 reader.ReadOptional(time);
95 }
96
97 std::optional<size_t> offset = record.attr->time_offset_from_start();
98 if (!offset.has_value()) {
99 time = std::nullopt;
100 return true;
101 }
102 return reader.Skip(*offset) && reader.ReadOptional(time);
103 }
104
105 } // namespace
106
PerfDataTokenizer(TraceProcessorContext * ctx)107 PerfDataTokenizer::PerfDataTokenizer(TraceProcessorContext* ctx)
108 : context_(ctx) {}
109
110 PerfDataTokenizer::~PerfDataTokenizer() = default;
111
112 // A normal perf.data consts of:
113 // [ header ]
114 // [ attr section ]
115 // [ data section ]
116 // [ optional feature sections ]
117 //
118 // Where each "attr" describes one event type recorded in the file.
119 //
120 // Most file format documentation is outdated or misleading, instead see
121 // perf_session__do_write_header() in linux/tools/perf/util/header.c.
Parse(TraceBlobView blob)122 base::Status PerfDataTokenizer::Parse(TraceBlobView blob) {
123 buffer_.PushBack(std::move(blob));
124
125 base::StatusOr<ParsingResult> result = ParsingResult::kSuccess;
126 while (result.ok() && result.value() == ParsingResult::kSuccess &&
127 !buffer_.empty()) {
128 switch (parsing_state_) {
129 case ParsingState::kParseHeader:
130 result = ParseHeader();
131 break;
132
133 case ParsingState::kParseAttrs:
134 result = ParseAttrs();
135 break;
136
137 case ParsingState::kSeekRecords:
138 result = SeekRecords();
139 break;
140
141 case ParsingState::kParseRecords:
142 result = ParseRecords();
143 break;
144
145 case ParsingState::kParseFeatures:
146 result = ParseFeatures();
147 break;
148
149 case ParsingState::kParseFeatureSections:
150 result = ParseFeatureSections();
151 break;
152
153 case ParsingState::kDone:
154 result = base::ErrStatus("Unexpected data");
155 }
156 }
157 return result.status();
158 }
159
160 base::StatusOr<PerfDataTokenizer::ParsingResult>
ParseHeader()161 PerfDataTokenizer::ParseHeader() {
162 auto tbv = buffer_.SliceOff(0, sizeof(header_));
163 if (!tbv) {
164 return ParsingResult::kMoreDataNeeded;
165 }
166 PERFETTO_CHECK(Reader(std::move(*tbv)).Read(header_));
167
168 // TODO: Check for endianess (big endian will have letters reversed);
169 if (memcmp(header_.magic, PerfFile::kPerfMagic,
170 sizeof(PerfFile::kPerfMagic)) != 0) {
171 return util::ErrStatus("Invalid magic string");
172 }
173
174 if (header_.size != sizeof(PerfFile::Header)) {
175 return util::ErrStatus("Failed to perf file header size. Expected %" PRIu64
176 ", found %zu",
177 sizeof(PerfFile::Header));
178 }
179
180 feature_ids_ = ExtractFeatureIds(header_.flags, header_.flags1);
181 feature_headers_section_ = {header_.data.end(),
182 feature_ids_.size() * sizeof(PerfFile::Section)};
183 context_->clock_tracker->SetTraceTimeClock(
184 protos::pbzero::ClockSnapshot::Clock::MONOTONIC);
185
186 PERFETTO_CHECK(buffer_.PopFrontUntil(sizeof(PerfFile::Header)));
187 parsing_state_ = ParsingState::kParseAttrs;
188 return ParsingResult::kSuccess;
189 }
190
191 base::StatusOr<PerfDataTokenizer::ParsingResult>
ParseAttrs()192 PerfDataTokenizer::ParseAttrs() {
193 std::optional<TraceBlobView> tbv =
194 buffer_.SliceOff(header_.attrs.offset, header_.attrs.size);
195 if (!tbv) {
196 return ParsingResult::kMoreDataNeeded;
197 }
198
199 ASSIGN_OR_RETURN(AttrsSectionReader attr_reader,
200 AttrsSectionReader::Create(header_, std::move(*tbv)));
201
202 PerfSession::Builder builder(context_);
203 while (attr_reader.CanReadNext()) {
204 PerfFile::AttrsEntry entry;
205 RETURN_IF_ERROR(attr_reader.ReadNext(entry));
206
207 if (entry.ids.size % sizeof(uint64_t) != 0) {
208 return base::ErrStatus("Invalid id section size: %" PRIu64,
209 entry.ids.size);
210 }
211
212 tbv = buffer_.SliceOff(entry.ids.offset, entry.ids.size);
213 if (!tbv) {
214 return ParsingResult::kMoreDataNeeded;
215 }
216
217 std::vector<uint64_t> ids;
218 ids.resize(entry.ids.size / sizeof(uint64_t));
219 PERFETTO_CHECK(Reader(std::move(*tbv)).ReadVector(ids));
220
221 builder.AddAttrAndIds(entry.attr, std::move(ids));
222 }
223
224 ASSIGN_OR_RETURN(perf_session_, builder.Build());
225 parsing_state_ = ParsingState::kSeekRecords;
226 return ParsingResult::kSuccess;
227 }
228
229 base::StatusOr<PerfDataTokenizer::ParsingResult>
SeekRecords()230 PerfDataTokenizer::SeekRecords() {
231 if (!buffer_.PopFrontUntil(header_.data.offset)) {
232 return ParsingResult::kMoreDataNeeded;
233 }
234 parsing_state_ = ParsingState::kParseRecords;
235 return ParsingResult::kSuccess;
236 }
237
238 base::StatusOr<PerfDataTokenizer::ParsingResult>
ParseRecords()239 PerfDataTokenizer::ParseRecords() {
240 while (buffer_.file_offset() < header_.data.end()) {
241 Record record;
242
243 if (auto res = ParseRecord(record);
244 !res.ok() || *res != ParsingResult::kSuccess) {
245 return res;
246 }
247
248 if (!PushRecord(std::move(record))) {
249 context_->storage->IncrementStats(stats::perf_record_skipped);
250 }
251 }
252
253 parsing_state_ = ParsingState::kParseFeatureSections;
254 return ParsingResult::kSuccess;
255 }
256
ParseRecord(Record & record)257 base::StatusOr<PerfDataTokenizer::ParsingResult> PerfDataTokenizer::ParseRecord(
258 Record& record) {
259 record.session = perf_session_;
260 std::optional<TraceBlobView> tbv =
261 buffer_.SliceOff(buffer_.file_offset(), sizeof(record.header));
262 if (!tbv) {
263 return ParsingResult::kMoreDataNeeded;
264 }
265 PERFETTO_CHECK(Reader(std::move(*tbv)).Read(record.header));
266
267 if (record.header.size < sizeof(record.header)) {
268 return base::ErrStatus("Invalid record size: %" PRIu16, record.header.size);
269 }
270
271 tbv = buffer_.SliceOff(buffer_.file_offset() + sizeof(record.header),
272 record.header.size - sizeof(record.header));
273 if (!tbv) {
274 return ParsingResult::kMoreDataNeeded;
275 }
276
277 record.payload = std::move(*tbv);
278
279 base::StatusOr<RefPtr<const PerfEventAttr>> attr =
280 perf_session_->FindAttrForRecord(record.header, record.payload);
281 if (!attr.ok()) {
282 return base::ErrStatus("Unable to determine perf_event_attr for record. %s",
283 attr.status().c_message());
284 }
285 record.attr = *attr;
286
287 buffer_.PopFrontBytes(record.header.size);
288 return ParsingResult::kSuccess;
289 }
290
ToTraceTimestamp(std::optional<uint64_t> time)291 base::StatusOr<int64_t> PerfDataTokenizer::ToTraceTimestamp(
292 std::optional<uint64_t> time) {
293 base::StatusOr<int64_t> trace_ts =
294 time.has_value()
295 ? context_->clock_tracker->ToTraceTime(
296 protos::pbzero::ClockSnapshot::Clock::MONOTONIC,
297 static_cast<int64_t>(*time))
298 : std::max(latest_timestamp_, context_->sorter->max_timestamp());
299
300 if (PERFETTO_LIKELY(trace_ts.ok())) {
301 latest_timestamp_ = std::max(latest_timestamp_, *trace_ts);
302 }
303
304 return trace_ts;
305 }
306
PushRecord(Record record)307 bool PerfDataTokenizer::PushRecord(Record record) {
308 std::optional<uint64_t> time;
309 if (!ReadTime(record, time)) {
310 return false;
311 }
312
313 base::StatusOr<int64_t> trace_ts = ToTraceTimestamp(time);
314 if (!trace_ts.ok()) {
315 return false;
316 }
317
318 switch (record.header.type) {
319 case PERF_RECORD_AUXTRACE_INFO:
320 case PERF_RECORD_AUXTRACE:
321 case PERF_RECORD_AUX:
322 break;
323 default:
324 context_->sorter->PushPerfRecord(*trace_ts, std::move(record));
325 break;
326 }
327
328 return true;
329 }
330
331 base::StatusOr<PerfDataTokenizer::ParsingResult>
ParseFeatureSections()332 PerfDataTokenizer::ParseFeatureSections() {
333 PERFETTO_CHECK(buffer_.file_offset() == header_.data.end());
334 auto tbv = buffer_.SliceOff(feature_headers_section_.offset,
335 feature_headers_section_.size);
336 if (!tbv) {
337 return ParsingResult::kMoreDataNeeded;
338 }
339
340 Reader reader(std::move(*tbv));
341 for (auto feature_id : feature_ids_) {
342 feature_sections_.emplace_back(std::piecewise_construct,
343 std::forward_as_tuple(feature_id),
344 std::forward_as_tuple());
345 PERFETTO_CHECK(reader.Read(feature_sections_.back().second));
346 }
347
348 std::sort(feature_sections_.begin(), feature_sections_.end(),
349 [](const std::pair<uint8_t, PerfFile::Section>& lhs,
350 const std::pair<uint8_t, PerfFile::Section>& rhs) {
351 return lhs.second.offset > rhs.second.offset;
352 });
353
354 buffer_.PopFrontUntil(feature_headers_section_.end());
355 parsing_state_ = feature_sections_.empty() ? ParsingState::kDone
356 : ParsingState::kParseFeatures;
357 return ParsingResult::kSuccess;
358 }
359
360 base::StatusOr<PerfDataTokenizer::ParsingResult>
ParseFeatures()361 PerfDataTokenizer::ParseFeatures() {
362 while (!feature_sections_.empty()) {
363 const auto feature_id = feature_sections_.back().first;
364 const auto& section = feature_sections_.back().second;
365 auto tbv = buffer_.SliceOff(section.offset, section.size);
366 if (!tbv) {
367 return ParsingResult::kMoreDataNeeded;
368 }
369
370 RETURN_IF_ERROR(ParseFeature(feature_id, std::move(*tbv)));
371 buffer_.PopFrontUntil(section.end());
372 feature_sections_.pop_back();
373 }
374
375 parsing_state_ = ParsingState::kDone;
376 return ParsingResult::kSuccess;
377 }
378
ParseFeature(uint8_t feature_id,TraceBlobView data)379 base::Status PerfDataTokenizer::ParseFeature(uint8_t feature_id,
380 TraceBlobView data) {
381 switch (feature_id) {
382 case feature::ID_CMD_LINE: {
383 ASSIGN_OR_RETURN(std::vector<std::string> args,
384 feature::ParseCmdline(std::move(data)));
385 perf_session_->SetCmdline(args);
386 return base::OkStatus();
387 }
388
389 case feature::ID_EVENT_DESC:
390 return feature::EventDescription::Parse(
391 std::move(data), [&](feature::EventDescription desc) {
392 for (auto id : desc.ids) {
393 perf_session_->SetEventName(id, std::move(desc.event_string));
394 }
395 return base::OkStatus();
396 });
397
398 case feature::ID_BUILD_ID:
399 return feature::BuildId::Parse(
400 std::move(data), [&](feature::BuildId build_id) {
401 perf_session_->AddBuildId(
402 build_id.pid, std::move(build_id.filename),
403 BuildId::FromRaw(std::move(build_id.build_id)));
404 return base::OkStatus();
405 });
406
407 case feature::ID_GROUP_DESC: {
408 feature::HeaderGroupDesc group_desc;
409 RETURN_IF_ERROR(
410 feature::HeaderGroupDesc::Parse(std::move(data), group_desc));
411 // TODO(carlscab): Do someting
412 break;
413 }
414
415 case feature::ID_SIMPLEPERF_META_INFO: {
416 feature::SimpleperfMetaInfo meta_info;
417 RETURN_IF_ERROR(
418 feature::SimpleperfMetaInfo::Parse(std::move(data), meta_info));
419 for (auto it = meta_info.event_type_info.GetIterator(); it; ++it) {
420 perf_session_->SetEventName(it.key().type, it.key().config, it.value());
421 }
422 break;
423 }
424 case feature::ID_SIMPLEPERF_FILE2: {
425 RETURN_IF_ERROR(feature::ParseSimpleperfFile2(
426 std::move(data), [&](TraceBlobView blob) {
427 third_party::simpleperf::proto::pbzero::FileFeature::Decoder file(
428 blob.data(), blob.length());
429 DsoTracker::GetOrCreate(context_).AddSimpleperfFile2(file);
430 }));
431
432 break;
433 }
434 default:
435 context_->storage->IncrementIndexedStats(stats::perf_features_skipped,
436 feature_id);
437 }
438
439 return base::OkStatus();
440 }
441
NotifyEndOfFile()442 void PerfDataTokenizer::NotifyEndOfFile() {}
443
444 } // namespace perf_importer
445 } // namespace trace_processor
446 } // namespace perfetto
447