xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/perf/features.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/features.h"
18 
19 #include <cstdint>
20 #include <utility>
21 
22 #include "perfetto/base/logging.h"
23 #include "perfetto/base/status.h"
24 #include "perfetto/ext/base/string_utils.h"
25 #include "perfetto/ext/base/string_view.h"
26 #include "perfetto/trace_processor/status.h"
27 #include "perfetto/trace_processor/trace_blob_view.h"
28 #include "src/trace_processor/importers/perf/perf_event.h"
29 #include "src/trace_processor/importers/perf/reader.h"
30 #include "src/trace_processor/util/status_macros.h"
31 
32 namespace perfetto::trace_processor::perf_importer::feature {
33 namespace {
34 
35 struct BuildIdRecord {
36   static constexpr uint8_t kMaxSize = 20;
37   char data[kMaxSize];
38   uint8_t size;
39   uint8_t reserved[3];
40 };
41 
CountTrailingZeros(const BuildIdRecord & build_id)42 uint8_t CountTrailingZeros(const BuildIdRecord& build_id) {
43   for (uint8_t i = 0; i < BuildIdRecord::kMaxSize; ++i) {
44     if (build_id.data[BuildIdRecord::kMaxSize - i - 1] != 0) {
45       return i;
46     }
47   }
48   return sizeof(build_id.data);
49 }
50 
51 // BuildIds are usually SHA-1 hashes (20 bytes), sometimes MD5 (16 bytes),
52 // sometimes 8 bytes long. Simpleperf adds trailing zeros up to 20. Do a best
53 // guess based on the number of trailing zeros.
GuessBuildIdSize(const BuildIdRecord & build_id)54 uint8_t GuessBuildIdSize(const BuildIdRecord& build_id) {
55   static_assert(BuildIdRecord::kMaxSize == 20);
56   uint8_t len = BuildIdRecord::kMaxSize - CountTrailingZeros(build_id);
57   if (len > 16) {
58     return BuildIdRecord::kMaxSize;
59   }
60   if (len > 8) {
61     return 16;
62   }
63   return 8;
64 }
65 
ParseString(Reader & reader,std::string & out)66 bool ParseString(Reader& reader, std::string& out) {
67   uint32_t len;
68   base::StringView str;
69   if (!reader.Read(len) || len == 0 || !reader.ReadStringView(str, len)) {
70     return false;
71   }
72 
73   if (str.at(len - 1) != '\0') {
74     return false;
75   }
76 
77   // Strings are padded with null values, stop at first null
78   out = std::string(str.data());
79   return true;
80 }
81 
ParseBuildId(const perf_event_header & header,TraceBlobView blob,BuildId & out)82 bool ParseBuildId(const perf_event_header& header,
83                   TraceBlobView blob,
84                   BuildId& out) {
85   Reader reader(std::move(blob));
86 
87   BuildIdRecord build_id;
88 
89   if (!reader.Read(out.pid) || !reader.Read(build_id) ||
90       !reader.ReadStringUntilEndOrNull(out.filename)) {
91     return false;
92   }
93 
94   if (header.misc & PERF_RECORD_MISC_EXT_RESERVED) {
95     if (build_id.size > BuildIdRecord::kMaxSize) {
96       return false;
97     }
98   } else {
99     // Probably a simpleperf trace. Simpleperf fills build_ids with zeros up
100     // to a length of 20 and leaves the rest uninitialized :( so we can not read
101     // build_id.size or build_id.reserved to do any checks.
102     // TODO(b/334978369): We should be able to tell for sure whether this is
103     // simpleperf or not by checking the existence of SimpleperfMetaInfo.
104     build_id.size = GuessBuildIdSize(build_id);
105   }
106   out.build_id = std::string(build_id.data, build_id.size);
107   return true;
108 }
109 
ParseEventTypeInfo(std::string value,SimpleperfMetaInfo & out)110 util::Status ParseEventTypeInfo(std::string value, SimpleperfMetaInfo& out) {
111   for (const auto& line : base::SplitString(value, "\n")) {
112     auto tokens = base::SplitString(line, ",");
113     if (tokens.size() != 3) {
114       return util::ErrStatus("Invalid event_type_info: '%s'", line.c_str());
115     }
116 
117     auto type = base::StringToUInt32(tokens[1]);
118     if (!type) {
119       return util::ErrStatus("Could not parse type in event_type_info: '%s'",
120                              tokens[1].c_str());
121     }
122     auto config = base::StringToUInt64(tokens[2]);
123     if (!config) {
124       return util::ErrStatus("Could not parse config in event_type_info: '%s'",
125                              tokens[2].c_str());
126     }
127 
128     out.event_type_info.Insert({*type, *config}, std::move(tokens[0]));
129   }
130 
131   return util::OkStatus();
132 }
133 
ParseSimpleperfMetaInfoEntry(std::pair<std::string,std::string> entry,SimpleperfMetaInfo & out)134 util::Status ParseSimpleperfMetaInfoEntry(
135     std::pair<std::string, std::string> entry,
136     SimpleperfMetaInfo& out) {
137   static constexpr char kEventTypeInfoKey[] = "event_type_info";
138   if (entry.first == kEventTypeInfoKey) {
139     return ParseEventTypeInfo(std::move(entry.second), out);
140   }
141 
142   PERFETTO_CHECK(
143       out.entries.Insert(std::move(entry.first), std::move(entry.second))
144           .second);
145   return util::OkStatus();
146 }
147 
148 }  // namespace
149 
150 // static
Parse(TraceBlobView bytes,std::function<util::Status (BuildId)> cb)151 util::Status BuildId::Parse(TraceBlobView bytes,
152                             std::function<util::Status(BuildId)> cb) {
153   Reader reader(std::move(bytes));
154   while (reader.size_left() != 0) {
155     perf_event_header header;
156     TraceBlobView payload;
157     if (!reader.Read(header)) {
158       return base::ErrStatus(
159           "Failed to parse feature BuildId. Could not read header.");
160     }
161     if (header.size < sizeof(header)) {
162       return base::ErrStatus(
163           "Failed to parse feature BuildId. Invalid size in header.");
164     }
165     if (!reader.ReadBlob(payload, header.size - sizeof(header))) {
166       return base::ErrStatus(
167           "Failed to parse feature BuildId. Could not read payload.");
168     }
169 
170     BuildId build_id;
171     if (!ParseBuildId(header, std::move(payload), build_id)) {
172       return base::ErrStatus(
173           "Failed to parse feature BuildId. Could not read entry.");
174     }
175 
176     RETURN_IF_ERROR(cb(std::move(build_id)));
177   }
178   return util::OkStatus();
179 }
180 
181 // static
Parse(const TraceBlobView & bytes,SimpleperfMetaInfo & out)182 util::Status SimpleperfMetaInfo::Parse(const TraceBlobView& bytes,
183                                        SimpleperfMetaInfo& out) {
184   auto* it_end = reinterpret_cast<const char*>(bytes.data() + bytes.size());
185   for (auto* it = reinterpret_cast<const char*>(bytes.data()); it != it_end;) {
186     auto end = std::find(it, it_end, '\0');
187     if (end == it_end) {
188       return util::ErrStatus("Failed to read key from Simpleperf MetaInfo");
189     }
190     std::string key(it, end);
191     it = end;
192     ++it;
193     if (it == it_end) {
194       return util::ErrStatus("Missing value in Simpleperf MetaInfo");
195     }
196     end = std::find(it, it_end, '\0');
197     if (end == it_end) {
198       return util::ErrStatus("Failed to read value from Simpleperf MetaInfo");
199     }
200     std::string value(it, end);
201     it = end;
202     ++it;
203 
204     RETURN_IF_ERROR(ParseSimpleperfMetaInfoEntry(
205         std::make_pair(std::move(key), std::move(value)), out));
206   }
207   return util::OkStatus();
208 }
209 
210 // static
Parse(TraceBlobView bytes,std::function<util::Status (EventDescription)> cb)211 util::Status EventDescription::Parse(
212     TraceBlobView bytes,
213     std::function<util::Status(EventDescription)> cb) {
214   Reader reader(std::move(bytes));
215   uint32_t nr;
216   uint32_t attr_size;
217   if (!reader.Read(nr) || !reader.Read(attr_size)) {
218     return util::ErrStatus("Failed to parse header for PERF_EVENT_DESC");
219   }
220 
221   for (; nr != 0; --nr) {
222     EventDescription desc;
223     uint32_t nr_ids;
224     if (!reader.ReadPerfEventAttr(desc.attr, attr_size) ||
225         !reader.Read(nr_ids) || !ParseString(reader, desc.event_string)) {
226       return util::ErrStatus("Failed to parse record for PERF_EVENT_DESC");
227     }
228 
229     desc.ids.resize(nr_ids);
230     for (uint64_t& id : desc.ids) {
231       if (!reader.Read(id)) {
232         return util::ErrStatus("Failed to parse ids for PERF_EVENT_DESC");
233       }
234     }
235     RETURN_IF_ERROR(cb(std::move(desc)));
236   }
237   return util::OkStatus();
238 }
239 
ParseSimpleperfFile2(TraceBlobView bytes,std::function<void (TraceBlobView)> cb)240 util::Status ParseSimpleperfFile2(TraceBlobView bytes,
241                                   std::function<void(TraceBlobView)> cb) {
242   Reader reader(std::move(bytes));
243   while (reader.size_left() != 0) {
244     uint32_t len;
245     if (!reader.Read(len)) {
246       return base::ErrStatus("Failed to parse len in FEATURE_SIMPLEPERF_FILE2");
247     }
248     TraceBlobView payload;
249     if (!reader.ReadBlob(payload, len)) {
250       return base::ErrStatus(
251           "Failed to parse payload in FEATURE_SIMPLEPERF_FILE2");
252     }
253     cb(std::move(payload));
254   }
255   return util::OkStatus();
256 }
257 
258 // static
Parse(TraceBlobView bytes,HeaderGroupDesc & out)259 util::Status HeaderGroupDesc::Parse(TraceBlobView bytes, HeaderGroupDesc& out) {
260   Reader reader(std::move(bytes));
261   uint32_t nr;
262   if (!reader.Read(nr)) {
263     return util::ErrStatus("Failed to parse header for HEADER_GROUP_DESC");
264   }
265 
266   HeaderGroupDesc group_desc;
267   group_desc.entries.resize(nr);
268   for (auto& e : group_desc.entries) {
269     if (!ParseString(reader, e.string) || !reader.Read(e.leader_idx) ||
270         !reader.Read(e.nr_members)) {
271       return util::ErrStatus("Failed to parse HEADER_GROUP_DESC entry");
272     }
273   }
274   out = std::move(group_desc);
275   return base::OkStatus();
276 }
277 
ParseCmdline(TraceBlobView bytes)278 base::StatusOr<std::vector<std::string>> ParseCmdline(TraceBlobView bytes) {
279   Reader reader(std::move(bytes));
280   uint32_t nr;
281   if (!reader.Read(nr)) {
282     return util::ErrStatus("Failed to parse nr for CMDLINE");
283   }
284 
285   std::vector<std::string> args;
286   args.reserve(nr);
287   for (; nr != 0; --nr) {
288     args.emplace_back();
289     if (!ParseString(reader, args.back())) {
290       return base::ErrStatus("Failed to parse string for CMDLINE");
291     }
292   }
293   return std::move(args);
294 }
295 
296 }  // namespace perfetto::trace_processor::perf_importer::feature
297