• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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