blob: 861a8bf387b16bd95e597b27611920be5cfe8e1f [file] [log] [blame]
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "src/trace_processor/importers/perf/features.h"
#include <cstdint>
#include <utility>
#include "perfetto/base/logging.h"
#include "perfetto/base/status.h"
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/base/string_view.h"
#include "perfetto/trace_processor/status.h"
#include "perfetto/trace_processor/trace_blob_view.h"
#include "src/trace_processor/importers/perf/perf_event.h"
#include "src/trace_processor/importers/perf/reader.h"
#include "src/trace_processor/util/status_macros.h"
namespace perfetto::trace_processor::perf_importer::feature {
namespace {
bool ParseString(Reader& reader, std::string& out) {
uint32_t len;
base::StringView str;
if (!reader.Read(len) || len == 0 || !reader.ReadStringView(str, len)) {
return false;
}
if (str.at(len - 1) != '\0') {
return false;
}
out = std::string(str.data(), len - 1);
return true;
}
bool ParseBuildId(const perf_event_header& header,
TraceBlobView blob,
BuildId& out) {
Reader reader(std::move(blob));
struct {
char data[20];
uint8_t size;
uint8_t reserved[3];
} build_id;
if (!reader.Read(out.pid) || !reader.Read(build_id) ||
!reader.ReadStringUntilEndOrNull(out.filename)) {
return false;
}
if (header.misc & PERF_RECORD_MISC_EXT_RESERVED) {
if (build_id.size > sizeof(build_id.data)) {
return false;
}
} else {
// Probably a simpleperf trace. Simpleperf fills build_ids with zeros up
// to a length of 20 and leaves the rest uninitialized :( so we can not read
// build_id.size or build_id.reserved to do any checks.
// TODO(b/334978369): We should be able to tell for sure whether this is
// simpleperf or not by checking the existence of SimpleperfMetaInfo.
build_id.size = 20;
// BuildIds are usually SHA-1 hashes (20 bytes), sometimes MD5 (16 bites).
// Simpleperf adds trailing zeros. But zeros could be in the MD5 hash.
// But it the last 4 bytes are zeros there is a high chance this was an
// MD5.
if (build_id.data[16] == 0 && build_id.data[17] == 0 &&
build_id.data[18] == 0 && build_id.data[19] == 0) {
build_id.size = 16;
}
}
out.build_id = std::string(build_id.data, build_id.size);
return true;
}
util::Status ParseEventTypeInfo(std::string value, SimpleperfMetaInfo& out) {
for (const auto& line : base::SplitString(value, "\n")) {
auto tokens = base::SplitString(line, ",");
if (tokens.size() != 3) {
return util::ErrStatus("Invalid event_type_info: '%s'", line.c_str());
}
auto type = base::StringToUInt32(tokens[1]);
if (!type) {
return util::ErrStatus("Could not parse type in event_type_info: '%s'",
tokens[1].c_str());
}
auto config = base::StringToUInt64(tokens[2]);
if (!config) {
return util::ErrStatus("Could not parse config in event_type_info: '%s'",
tokens[2].c_str());
}
out.event_type_info.Insert({*type, *config}, std::move(tokens[0]));
}
return util::OkStatus();
}
util::Status ParseSimpleperfMetaInfoEntry(
std::pair<std::string, std::string> entry,
SimpleperfMetaInfo& out) {
static constexpr char kEventTypeInfoKey[] = "event_type_info";
if (entry.first == kEventTypeInfoKey) {
return ParseEventTypeInfo(std::move(entry.second), out);
}
PERFETTO_CHECK(
out.entries.Insert(std::move(entry.first), std::move(entry.second))
.second);
return util::OkStatus();
}
} // namespace
// static
util::Status BuildId::Parse(TraceBlobView bytes,
std::function<util::Status(BuildId)> cb) {
Reader reader(std::move(bytes));
while (reader.size_left() != 0) {
perf_event_header header;
TraceBlobView payload;
if (!reader.Read(header)) {
return base::ErrStatus(
"Failed to parse feature BuildId. Could not read header.");
}
if (header.size < sizeof(header)) {
return base::ErrStatus(
"Failed to parse feature BuildId. Invalid size in header.");
}
if (!reader.ReadBlob(payload, header.size - sizeof(header))) {
return base::ErrStatus(
"Failed to parse feature BuildId. Could not read payload.");
}
BuildId build_id;
if (!ParseBuildId(header, std::move(payload), build_id)) {
return base::ErrStatus(
"Failed to parse feature BuildId. Could not read entry.");
}
RETURN_IF_ERROR(cb(std::move(build_id)));
}
return util::OkStatus();
}
// static
util::Status SimpleperfMetaInfo::Parse(const TraceBlobView& bytes,
SimpleperfMetaInfo& out) {
auto* it_end = reinterpret_cast<const char*>(bytes.data() + bytes.size());
for (auto* it = reinterpret_cast<const char*>(bytes.data()); it != it_end;) {
auto end = std::find(it, it_end, '\0');
if (end == it_end) {
return util::ErrStatus("Failed to read key from Simpleperf MetaInfo");
}
std::string key(it, end);
it = end;
++it;
if (it == it_end) {
return util::ErrStatus("Missing value in Simpleperf MetaInfo");
}
end = std::find(it, it_end, '\0');
if (end == it_end) {
return util::ErrStatus("Failed to read value from Simpleperf MetaInfo");
}
std::string value(it, end);
it = end;
++it;
RETURN_IF_ERROR(ParseSimpleperfMetaInfoEntry(
std::make_pair(std::move(key), std::move(value)), out));
}
return util::OkStatus();
}
// static
util::Status EventDescription::Parse(
TraceBlobView bytes,
std::function<util::Status(EventDescription)> cb) {
Reader reader(std::move(bytes));
uint32_t nr;
uint32_t attr_size;
if (!reader.Read(nr) || !reader.Read(attr_size)) {
return util::ErrStatus("Failed to parse header for PERF_EVENT_DESC");
}
for (; nr != 0; --nr) {
EventDescription desc;
uint32_t nr_ids;
if (!reader.ReadPerfEventAttr(desc.attr, attr_size) ||
!reader.Read(nr_ids) || !ParseString(reader, desc.event_string)) {
return util::ErrStatus("Failed to parse record for PERF_EVENT_DESC");
}
desc.ids.resize(nr_ids);
for (uint64_t& id : desc.ids) {
if (!reader.Read(id)) {
return util::ErrStatus("Failed to parse ids for PERF_EVENT_DESC");
}
}
RETURN_IF_ERROR(cb(std::move(desc)));
}
return util::OkStatus();
}
util::Status ParseSimpleperfFile2(
TraceBlobView bytes,
std::function<util::Status(TraceBlobView)> cb) {
Reader reader(std::move(bytes));
while (reader.size_left() != 0) {
uint32_t len;
if (!reader.Read(len)) {
return base::ErrStatus("Failed to parse len in FEATURE_SIMPLEPERF_FILE2");
}
TraceBlobView payload;
if (!reader.ReadBlob(payload, len)) {
return base::ErrStatus(
"Failed to parse payload in FEATURE_SIMPLEPERF_FILE2");
}
RETURN_IF_ERROR(cb(std::move(payload)));
}
return util::OkStatus();
}
// static
util::Status HeaderGroupDesc::Parse(TraceBlobView bytes, HeaderGroupDesc& out) {
Reader reader(std::move(bytes));
uint32_t nr;
if (!reader.Read(nr)) {
return util::ErrStatus("Failed to parse header for HEADER_GROUP_DESC");
}
HeaderGroupDesc group_desc;
group_desc.entries.resize(nr);
for (auto& e : group_desc.entries) {
if (!ParseString(reader, e.string) || !reader.Read(e.leader_idx) ||
!reader.Read(e.nr_members)) {
return util::ErrStatus("Failed to parse HEADER_GROUP_DESC entry");
}
}
out = std::move(group_desc);
return base::OkStatus();
}
} // namespace perfetto::trace_processor::perf_importer::feature