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