Merge "Use std::equal instead of memcmp in file_buffer_unittest.cc" into main
diff --git a/Android.bp b/Android.bp
index 1e7b327..ee7f589 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12241,9 +12241,11 @@
name: "perfetto_src_trace_processor_importers_perf_perf",
srcs: [
"src/trace_processor/importers/perf/attrs_section_reader.cc",
+ "src/trace_processor/importers/perf/features.cc",
+ "src/trace_processor/importers/perf/mmap_record.cc",
"src/trace_processor/importers/perf/perf_data_tokenizer.cc",
- "src/trace_processor/importers/perf/perf_data_tracker.cc",
"src/trace_processor/importers/perf/record_parser.cc",
+ "src/trace_processor/importers/perf/sample.cc",
],
}
@@ -12251,6 +12253,7 @@
filegroup {
name: "perfetto_src_trace_processor_importers_perf_record",
srcs: [
+ "src/trace_processor/importers/perf/perf_counter.cc",
"src/trace_processor/importers/perf/perf_event_attr.cc",
"src/trace_processor/importers/perf/perf_session.cc",
],
@@ -12260,7 +12263,6 @@
filegroup {
name: "perfetto_src_trace_processor_importers_perf_unittests",
srcs: [
- "src/trace_processor/importers/perf/perf_data_tracker_unittest.cc",
"src/trace_processor/importers/perf/perf_session_unittest.cc",
"src/trace_processor/importers/perf/reader_unittest.cc",
],
diff --git a/BUILD b/BUILD
index 1c2fe0e..b3ff934 100644
--- a/BUILD
+++ b/BUILD
@@ -1708,13 +1708,17 @@
srcs = [
"src/trace_processor/importers/perf/attrs_section_reader.cc",
"src/trace_processor/importers/perf/attrs_section_reader.h",
+ "src/trace_processor/importers/perf/features.cc",
+ "src/trace_processor/importers/perf/features.h",
+ "src/trace_processor/importers/perf/mmap_record.cc",
+ "src/trace_processor/importers/perf/mmap_record.h",
"src/trace_processor/importers/perf/perf_data_tokenizer.cc",
"src/trace_processor/importers/perf/perf_data_tokenizer.h",
- "src/trace_processor/importers/perf/perf_data_tracker.cc",
- "src/trace_processor/importers/perf/perf_data_tracker.h",
"src/trace_processor/importers/perf/perf_file.h",
"src/trace_processor/importers/perf/record_parser.cc",
"src/trace_processor/importers/perf/record_parser.h",
+ "src/trace_processor/importers/perf/sample.cc",
+ "src/trace_processor/importers/perf/sample.h",
],
)
@@ -1722,6 +1726,8 @@
perfetto_filegroup(
name = "src_trace_processor_importers_perf_record",
srcs = [
+ "src/trace_processor/importers/perf/perf_counter.cc",
+ "src/trace_processor/importers/perf/perf_counter.h",
"src/trace_processor/importers/perf/perf_event.h",
"src/trace_processor/importers/perf/perf_event_attr.cc",
"src/trace_processor/importers/perf/perf_event_attr.h",
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 9d84571..0e6a83d 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,4 +1,14 @@
{
+ "art-mainline-presubmit": [
+ {
+ "name": "CtsPerfettoTestCases",
+ "options": [
+ {
+ "include-filter": "HeapprofdJavaCtsTest*"
+ }
+ ]
+ }
+ ],
"presubmit": [
{
"name": "CtsPerfettoTestCases"
diff --git a/src/trace_processor/importers/common/mapping_tracker.cc b/src/trace_processor/importers/common/mapping_tracker.cc
index 0dec3e5..965c15f 100644
--- a/src/trace_processor/importers/common/mapping_tracker.cc
+++ b/src/trace_processor/importers/common/mapping_tracker.cc
@@ -164,5 +164,15 @@
});
}
+VirtualMemoryMapping* MappingTracker::GetDummyMapping() {
+ if (!dummy_mapping_) {
+ CreateMappingParams params;
+ params.memory_range =
+ AddressRange::FromStartAndSize(0, std::numeric_limits<uint64_t>::max());
+ dummy_mapping_ = &InternMemoryMapping(params);
+ }
+ return dummy_mapping_;
+}
+
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/importers/common/mapping_tracker.h b/src/trace_processor/importers/common/mapping_tracker.h
index bc45bef..08c767e 100644
--- a/src/trace_processor/importers/common/mapping_tracker.h
+++ b/src/trace_processor/importers/common/mapping_tracker.h
@@ -91,6 +91,10 @@
// Jitted ranges will only be applied to UserMemoryMappings
void AddJitRange(UniquePid upid, AddressRange range, JitCache* jit_cache);
+ // Sometimes we just need a mapping and we are lacking trace data to create a
+ // proper one. Use this mapping in those cases.
+ VirtualMemoryMapping* GetDummyMapping();
+
private:
template <typename MappingImpl>
MappingImpl& AddMapping(std::unique_ptr<MappingImpl> mapping);
@@ -136,6 +140,8 @@
KernelMemoryMapping* kernel_ = nullptr;
base::FlatHashMap<UniquePid, AddressRangeMap<JitCache*>> jit_caches_;
+
+ VirtualMemoryMapping* dummy_mapping_;
};
} // namespace trace_processor
diff --git a/src/trace_processor/importers/perf/BUILD.gn b/src/trace_processor/importers/perf/BUILD.gn
index f87eeda..7a3f2a3 100644
--- a/src/trace_processor/importers/perf/BUILD.gn
+++ b/src/trace_processor/importers/perf/BUILD.gn
@@ -16,6 +16,8 @@
source_set("record") {
sources = [
+ "perf_counter.cc",
+ "perf_counter.h",
"perf_event.h",
"perf_event_attr.cc",
"perf_event_attr.h",
@@ -27,7 +29,6 @@
deps = [
"../../../../gn:default_deps",
"../../../../include/perfetto/ext/base:base",
- "../../../../include/perfetto/trace_processor:storage",
"../../../../include/perfetto/trace_processor:trace_processor",
"../../../../protos/perfetto/trace/profiling:zero",
"../../storage",
@@ -40,13 +41,17 @@
sources = [
"attrs_section_reader.cc",
"attrs_section_reader.h",
+ "features.cc",
+ "features.h",
+ "mmap_record.cc",
+ "mmap_record.h",
"perf_data_tokenizer.cc",
"perf_data_tokenizer.h",
- "perf_data_tracker.cc",
- "perf_data_tracker.h",
"perf_file.h",
"record_parser.cc",
"record_parser.h",
+ "sample.cc",
+ "sample.h",
]
public_deps = [ ":record" ]
deps = [
@@ -57,9 +62,10 @@
"../../storage",
"../../tables:tables_python",
"../../types",
+ "../../util:build_id",
"../../util:file_buffer",
- "../common",
- "../common:parser_types",
+ "../../util:util",
+ "../common:common",
"../proto:minimal",
]
}
@@ -67,7 +73,6 @@
perfetto_unittest_source_set("unittests") {
testonly = true
sources = [
- "perf_data_tracker_unittest.cc",
"perf_session_unittest.cc",
"reader_unittest.cc",
]
diff --git a/src/trace_processor/importers/perf/attrs_section_reader.cc b/src/trace_processor/importers/perf/attrs_section_reader.cc
index ffea386..f19ea3e 100644
--- a/src/trace_processor/importers/perf/attrs_section_reader.cc
+++ b/src/trace_processor/importers/perf/attrs_section_reader.cc
@@ -56,20 +56,11 @@
}
const size_t attr_size = header.attr_size - kSectionSize;
- const size_t attr_bytes_to_read =
- std::min(attr_size, sizeof(PerfFile::AttrsEntry::attr));
- const size_t attr_bytes_to_skip = attr_size - attr_bytes_to_read;
-
- return AttrsSectionReader(std::move(section), num_attr, attr_size,
- attr_bytes_to_read, attr_bytes_to_skip);
+ return AttrsSectionReader(std::move(section), num_attr, attr_size);
}
base::Status AttrsSectionReader::ReadNext(PerfFile::AttrsEntry& entry) {
- static_assert(std::has_unique_object_representations_v<PerfFile::AttrsEntry>);
- memset(&entry, 0, sizeof(entry));
-
- PERFETTO_CHECK(reader_.Read(&entry.attr, attr_bytes_to_read_) &&
- reader_.Skip(attr_bytes_to_skip_));
+ PERFETTO_CHECK(reader_.ReadPerfEventAttr(entry.attr, attr_size_));
if (entry.attr.size != attr_size_) {
return base::ErrStatus(
diff --git a/src/trace_processor/importers/perf/attrs_section_reader.h b/src/trace_processor/importers/perf/attrs_section_reader.h
index ae71ae0..a7eaaa3 100644
--- a/src/trace_processor/importers/perf/attrs_section_reader.h
+++ b/src/trace_processor/importers/perf/attrs_section_reader.h
@@ -41,22 +41,14 @@
base::Status ReadNext(PerfFile::AttrsEntry& entry);
private:
- AttrsSectionReader(TraceBlobView section,
- size_t num_attr,
- size_t attr_size,
- size_t attr_bytes_to_read,
- size_t attr_bytes_to_skip)
+ AttrsSectionReader(TraceBlobView section, size_t num_attr, size_t attr_size)
: reader_(std::move(section)),
num_attr_(num_attr),
- attr_size_(attr_size),
- attr_bytes_to_read_(attr_bytes_to_read),
- attr_bytes_to_skip_(attr_bytes_to_skip) {}
+ attr_size_(attr_size) {}
Reader reader_;
size_t num_attr_;
const size_t attr_size_;
- const size_t attr_bytes_to_read_;
- const size_t attr_bytes_to_skip_;
};
} // namespace perfetto::trace_processor::perf_importer
diff --git a/src/trace_processor/importers/perf/features.cc b/src/trace_processor/importers/perf/features.cc
new file mode 100644
index 0000000..861a8bf
--- /dev/null
+++ b/src/trace_processor/importers/perf/features.cc
@@ -0,0 +1,258 @@
+/*
+ * 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
diff --git a/src/trace_processor/importers/perf/features.h b/src/trace_processor/importers/perf/features.h
new file mode 100644
index 0000000..2a6c22b
--- /dev/null
+++ b/src/trace_processor/importers/perf/features.h
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PERF_FEATURES_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PERF_FEATURES_H_
+
+#include <cstdint>
+#include <functional>
+#include <limits>
+#include <string>
+#include <vector>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/hash.h"
+#include "perfetto/trace_processor/status.h"
+#include "src/trace_processor/importers/perf/perf_event.h"
+
+namespace perfetto ::trace_processor {
+class TraceBlobView;
+
+namespace perf_importer::feature {
+
+enum Id : uint8_t {
+ ID_RESERVED = 0,
+ ID_TRACING_DATA = 1,
+ ID_BUILD_ID = 2,
+ ID_HOSTNAME = 3,
+ ID_OS_RELEASE = 4,
+ ID_VERSION = 5,
+ ID_ARCH = 6,
+ ID_NR_CPUS = 7,
+ ID_CPU_DESC = 8,
+ ID_CPU_ID = 9,
+ ID_TOTAL_MEM = 10,
+ ID_CMD_LINE = 11,
+ ID_EVENT_DESC = 12,
+ ID_CPU_TOPOLOGY = 13,
+ ID_NUMA_TOPOLOGY = 14,
+ ID_BRANCH_STACK = 15,
+ ID_PMU_MAPPINGS = 16,
+ ID_GROUP_DESC = 17,
+ ID_AUX_TRACE = 18,
+ ID_STAT = 19,
+ ID_CACHE = 20,
+ ID_SAMPLE_TIME = 21,
+ ID_SAMPLE_TOPOLOGY = 22,
+ ID_CLOCK_ID = 23,
+ ID_DIR_FORMAT = 24,
+ ID_BPF_PROG_INFO = 25,
+ ID_BPF_BTF = 26,
+ ID_COMPRESSED = 27,
+ ID_CPU_PUM_CAPS = 28,
+ ID_CLOCK_DATA = 29,
+ ID_HYBRID_TOPOLOGY = 30,
+ ID_PMU_CAPS = 31,
+ ID_SIMPLEPERF_FILE = 128,
+ ID_SIMPLEPERF_META_INFO = 129,
+ ID_SIMPLEPERF_FILE2 = 132,
+ ID_MAX = std::numeric_limits<uint8_t>::max(),
+};
+
+struct BuildId {
+ static util::Status Parse(TraceBlobView,
+ std::function<util::Status(BuildId)> cb);
+ int32_t pid;
+ std::string build_id;
+ std::string filename;
+};
+
+struct HeaderGroupDesc {
+ static util::Status Parse(TraceBlobView, HeaderGroupDesc& out);
+ struct Entry {
+ std::string string;
+ uint32_t leader_idx;
+ uint32_t nr_members;
+ };
+ std::vector<Entry> entries;
+};
+
+struct EventDescription {
+ static util::Status Parse(TraceBlobView,
+ std::function<util::Status(EventDescription)> cb);
+ perf_event_attr attr;
+ std::string event_string;
+ std::vector<uint64_t> ids;
+};
+
+struct SimpleperfMetaInfo {
+ static util::Status Parse(const TraceBlobView&, SimpleperfMetaInfo& out);
+ base::FlatHashMap<std::string, std::string> entries;
+ struct EventTypeAndConfig {
+ uint32_t type;
+ uint64_t config;
+ bool operator==(const EventTypeAndConfig& other) {
+ return type == other.type && config == other.config;
+ }
+ bool operator!=(const EventTypeAndConfig& other) {
+ return !(*this == other);
+ }
+ struct Hasher {
+ size_t operator()(const EventTypeAndConfig& o) const {
+ return static_cast<size_t>(base::Hasher::Combine(o.config, o.type));
+ }
+ };
+ };
+ using EventName = std::string;
+ base::FlatHashMap<EventTypeAndConfig, EventName, EventTypeAndConfig::Hasher>
+ event_type_info;
+};
+
+util::Status ParseSimpleperfFile2(
+ TraceBlobView,
+ std::function<util::Status(TraceBlobView)> cb);
+
+} // namespace perf_importer::feature
+
+} // namespace perfetto::trace_processor
+
+#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PERF_FEATURES_H_
diff --git a/src/trace_processor/importers/perf/mmap_record.cc b/src/trace_processor/importers/perf/mmap_record.cc
new file mode 100644
index 0000000..d11fdd5
--- /dev/null
+++ b/src/trace_processor/importers/perf/mmap_record.cc
@@ -0,0 +1,66 @@
+/*
+ * 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/mmap_record.h"
+
+#include <optional>
+
+#include "perfetto/base/status.h"
+#include "src/trace_processor/importers/perf/reader.h"
+#include "src/trace_processor/importers/perf/record.h"
+
+namespace perfetto::trace_processor::perf_importer {
+
+base::Status MmapRecord::Parse(const Record& record) {
+ Reader reader(record.payload.copy());
+ if (!reader.Read(*static_cast<CommonMmapRecordFields*>(this)) ||
+ !reader.ReadCString(filename)) {
+ return base::ErrStatus("Failed to parse MMAP record");
+ }
+ cpu_mode = record.GetCpuMode();
+ return base::OkStatus();
+}
+
+base::Status Mmap2Record::Parse(const Record& record) {
+ Reader reader(record.payload.copy());
+ if (!reader.Read(*static_cast<BaseMmap2Record*>(this)) ||
+ !reader.ReadCString(filename)) {
+ return base::ErrStatus("Failed to parse MMAP record");
+ }
+
+ has_build_id = record.mmap_has_build_id();
+
+ if (has_build_id && build_id.build_id_size >
+ BaseMmap2Record::BuildIdFields::kMaxBuildIdSize) {
+ return base::ErrStatus(
+ "Invalid build_id_size in MMAP2 record. Expected <= %zu but found "
+ "%" PRIu8,
+ BaseMmap2Record::BuildIdFields::kMaxBuildIdSize,
+ build_id.build_id_size);
+ }
+
+ cpu_mode = record.GetCpuMode();
+
+ return base::OkStatus();
+}
+
+std::optional<BuildId> Mmap2Record::GetBuildId() const {
+ return has_build_id ? std::make_optional(BuildId::FromRaw(std::string(
+ build_id.build_id_buf, build_id.build_id_size)))
+ : std::nullopt;
+}
+
+} // namespace perfetto::trace_processor::perf_importer
diff --git a/src/trace_processor/importers/perf/mmap_record.h b/src/trace_processor/importers/perf/mmap_record.h
new file mode 100644
index 0000000..37b3939
--- /dev/null
+++ b/src/trace_processor/importers/perf/mmap_record.h
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PERF_MMAP_RECORD_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PERF_MMAP_RECORD_H_
+
+#include <cstdint>
+#include <optional>
+#include <string>
+#include "perfetto/base/status.h"
+#include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
+#include "src/trace_processor/util/build_id.h"
+
+namespace perfetto::trace_processor::perf_importer {
+
+struct Record;
+
+struct CommonMmapRecordFields {
+ uint32_t pid;
+ uint32_t tid;
+ uint64_t addr;
+ uint64_t len;
+ uint64_t pgoff;
+};
+
+struct MmapRecord : public CommonMmapRecordFields {
+ std::string filename;
+ protos::pbzero::Profiling::CpuMode cpu_mode;
+
+ base::Status Parse(const Record& record);
+};
+
+struct BaseMmap2Record : public CommonMmapRecordFields {
+ struct BuildIdFields {
+ static constexpr size_t kMaxBuildIdSize = 20;
+ uint8_t build_id_size;
+ uint8_t reserved_1;
+ uint16_t reserved_2;
+ char build_id_buf[kMaxBuildIdSize];
+ };
+ struct InodeFields {
+ uint32_t maj;
+ uint32_t min;
+ int64_t ino;
+ uint64_t ino_generation;
+ };
+ static_assert(sizeof(BuildIdFields) == sizeof(InodeFields));
+
+ union {
+ BuildIdFields build_id;
+ InodeFields inode;
+ };
+ uint32_t prot;
+ uint32_t flags;
+};
+
+struct Mmap2Record : public BaseMmap2Record {
+ std::string filename;
+ protos::pbzero::Profiling::CpuMode cpu_mode;
+ bool has_build_id;
+
+ base::Status Parse(const Record& record);
+ std::optional<BuildId> GetBuildId() const;
+};
+
+} // namespace perfetto::trace_processor::perf_importer
+
+#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PERF_MMAP_RECORD_H_
diff --git a/src/trace_processor/importers/perf/perf_counter.cc b/src/trace_processor/importers/perf/perf_counter.cc
new file mode 100644
index 0000000..685e940
--- /dev/null
+++ b/src/trace_processor/importers/perf/perf_counter.cc
@@ -0,0 +1,37 @@
+/*
+ * 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/perf_counter.h"
+
+#include <cstdint>
+
+#include "perfetto/base/logging.h"
+#include "src/trace_processor/tables/counter_tables_py.h"
+
+namespace perfetto::trace_processor::perf_importer {
+
+void PerfCounter::AddDelta(int64_t ts, double delta) {
+ last_count_ += delta;
+ counter_table_.Insert({ts, track_id_, last_count_});
+}
+
+void PerfCounter::AddCount(int64_t ts, double count) {
+ PERFETTO_CHECK(count >= last_count_);
+ last_count_ = count;
+ counter_table_.Insert({ts, track_id_, last_count_});
+}
+
+} // namespace perfetto::trace_processor::perf_importer
diff --git a/src/trace_processor/importers/perf/perf_counter.h b/src/trace_processor/importers/perf/perf_counter.h
new file mode 100644
index 0000000..fb7a28c
--- /dev/null
+++ b/src/trace_processor/importers/perf/perf_counter.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PERF_PERF_COUNTER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PERF_PERF_COUNTER_H_
+
+#include <cstdint>
+
+#include "src/trace_processor/tables/counter_tables_py.h"
+#include "src/trace_processor/tables/track_tables_py.h"
+
+namespace perfetto::trace_processor::perf_importer {
+
+// Helper class to keep track of perf counters and convert delta values found in
+// perf files to absolute values needed for the perfetto counter table.
+class PerfCounter {
+ public:
+ PerfCounter(tables::CounterTable* counter_table,
+ const tables::PerfCounterTrackTable::ConstRowReference& track)
+ : counter_table_(*counter_table),
+ track_id_(track.id()),
+ is_timebase_(track.is_timebase()) {}
+
+ bool is_timebase() const { return is_timebase_; }
+
+ void AddDelta(int64_t ts, double delta);
+ void AddCount(int64_t ts, double count);
+
+ private:
+ tables::CounterTable& counter_table_;
+ tables::PerfCounterTrackTable::Id track_id_;
+ const bool is_timebase_;
+ double last_count_{0};
+};
+
+} // namespace perfetto::trace_processor::perf_importer
+
+#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PERF_PERF_COUNTER_H_
diff --git a/src/trace_processor/importers/perf/perf_data_tokenizer.cc b/src/trace_processor/importers/perf/perf_data_tokenizer.cc
index dd1d604..25cfb46 100644
--- a/src/trace_processor/importers/perf/perf_data_tokenizer.cc
+++ b/src/trace_processor/importers/perf/perf_data_tokenizer.cc
@@ -33,6 +33,7 @@
#include "src/trace_processor/importers/common/clock_tracker.h"
#include "src/trace_processor/importers/common/slice_tracker.h"
#include "src/trace_processor/importers/perf/attrs_section_reader.h"
+#include "src/trace_processor/importers/perf/features.h"
#include "src/trace_processor/importers/perf/perf_event.h"
#include "src/trace_processor/importers/perf/perf_file.h"
#include "src/trace_processor/importers/perf/perf_session.h"
@@ -40,6 +41,7 @@
#include "src/trace_processor/importers/perf/record.h"
#include "src/trace_processor/importers/proto/perf_sample_tracker.h"
#include "src/trace_processor/sorter/trace_sorter.h"
+#include "src/trace_processor/storage/stats.h"
#include "src/trace_processor/util/status_macros.h"
namespace perfetto {
@@ -194,7 +196,7 @@
AttrsSectionReader::Create(header_, std::move(*tbv)));
PerfSession::Builder builder(
- context_->perf_sample_tracker->CreatePerfSession());
+ context_, context_->perf_sample_tracker->CreatePerfSession());
while (attr_reader.CanReadNext()) {
PerfFile::AttrsEntry entry;
RETURN_IF_ERROR(attr_reader.ReadNext(entry));
@@ -371,7 +373,45 @@
return ParsingResult::kSuccess;
}
-base::Status PerfDataTokenizer::ParseFeature(uint8_t, TraceBlobView) {
+base::Status PerfDataTokenizer::ParseFeature(uint8_t feature_id,
+ TraceBlobView data) {
+ switch (feature_id) {
+ case feature::ID_EVENT_DESC: {
+ RETURN_IF_ERROR(feature::EventDescription::Parse(
+ std::move(data),
+ [](feature::EventDescription) { return base::OkStatus(); }));
+ break;
+ }
+
+ case feature::ID_BUILD_ID:
+ return feature::BuildId::Parse(
+ std::move(data), [](feature::BuildId) { return base::OkStatus(); });
+
+ case feature::ID_GROUP_DESC: {
+ feature::HeaderGroupDesc group_desc;
+ RETURN_IF_ERROR(
+ feature::HeaderGroupDesc::Parse(std::move(data), group_desc));
+ // TODO(carlscab): Do someting
+ break;
+ }
+
+ case feature::ID_SIMPLEPERF_META_INFO: {
+ feature::SimpleperfMetaInfo meta_info;
+ RETURN_IF_ERROR(
+ feature::SimpleperfMetaInfo::Parse(std::move(data), meta_info));
+ break;
+ }
+ case feature::ID_SIMPLEPERF_FILE2: {
+ RETURN_IF_ERROR(feature::ParseSimpleperfFile2(
+ std::move(data), [&](TraceBlobView) { return util::OkStatus(); }));
+
+ break;
+ }
+ default:
+ context_->storage->IncrementIndexedStats(stats::perf_features_skipped,
+ feature_id);
+ }
+
return base::OkStatus();
}
diff --git a/src/trace_processor/importers/perf/perf_data_tracker.cc b/src/trace_processor/importers/perf/perf_data_tracker.cc
deleted file mode 100644
index 67557ee..0000000
--- a/src/trace_processor/importers/perf/perf_data_tracker.cc
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2023 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/perf_data_tracker.h"
-
-#include <cstdint>
-#include <optional>
-
-#include "perfetto/base/status.h"
-#include "src/trace_processor/importers/common/address_range.h"
-#include "src/trace_processor/importers/common/mapping_tracker.h"
-#include "src/trace_processor/importers/common/process_tracker.h"
-#include "src/trace_processor/importers/perf/reader.h"
-#include "src/trace_processor/storage/stats.h"
-#include "src/trace_processor/storage/trace_storage.h"
-
-#include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace perf_importer {
-namespace {
-
-bool IsInKernel(protos::pbzero::Profiling::CpuMode cpu_mode) {
- switch (cpu_mode) {
- case protos::pbzero::Profiling::MODE_UNKNOWN:
- PERFETTO_CHECK(false);
- case protos::pbzero::Profiling::MODE_GUEST_KERNEL:
- case protos::pbzero::Profiling::MODE_KERNEL:
- return true;
- case protos::pbzero::Profiling::MODE_USER:
- case protos::pbzero::Profiling::MODE_HYPERVISOR:
- case protos::pbzero::Profiling::MODE_GUEST_USER:
- return false;
- }
- PERFETTO_CHECK(false);
-}
-
-CreateMappingParams BuildCreateMappingParams(
- PerfDataTracker::Mmap2Record record) {
- return {AddressRange::FromStartAndSize(record.num.addr, record.num.len),
- record.num.pgoff,
- // start_offset: This is the offset into the file where the ELF header
- // starts. We assume all file mappings are ELF files an thus this
- // offset is 0.
- 0,
- // load_bias: This can only be read out of the actual ELF file, which
- // we do not have here, so we set it to 0. When symbolizing we will
- // hopefully have the real load bias and we can compensate there for a
- // possible mismatch.
- 0, record.filename, std::nullopt};
-}
-} // namespace
-
-PerfDataTracker::~PerfDataTracker() = default;
-
-void PerfDataTracker::PushMmap2Record(Mmap2Record record) {
- if (IsInKernel(record.cpu_mode)) {
- context_->mapping_tracker->CreateKernelMemoryMapping(
- BuildCreateMappingParams(std::move(record)));
- } else {
- UniquePid upid =
- context_->process_tracker->GetOrCreateProcess(record.num.pid);
- context_->mapping_tracker->CreateUserMemoryMapping(
- upid, BuildCreateMappingParams(std::move(record)));
- }
-}
-
-base::StatusOr<PerfDataTracker::PerfSample> PerfDataTracker::ParseSample(
- Reader& reader,
- uint64_t sample_type) {
- PerfDataTracker::PerfSample sample;
-
- if (sample_type & PERF_SAMPLE_IDENTIFIER) {
- reader.ReadOptional(sample.id);
- }
-
- if (sample_type & PERF_SAMPLE_IP) {
- reader.Skip<uint64_t>();
- }
-
- if (sample_type & PERF_SAMPLE_TID) {
- reader.ReadOptional(sample.pid);
- reader.ReadOptional(sample.tid);
- }
-
- if (sample_type & PERF_SAMPLE_TIME) {
- reader.ReadOptional(sample.ts);
- }
-
- // Ignored. Checked because we need to access later parts of sample.
- if (sample_type & PERF_SAMPLE_ADDR) {
- reader.Skip<uint64_t>();
- }
-
- // The same value as PERF_SAMPLE_IDENTIFIER, so should be ignored.
- if (sample_type & PERF_SAMPLE_ID) {
- reader.Skip<uint64_t>();
- }
-
- // Ignored. Checked because we need to access later parts of sample.
- if (sample_type & PERF_SAMPLE_STREAM_ID) {
- reader.Skip<uint64_t>();
- }
-
- if (sample_type & PERF_SAMPLE_CPU) {
- reader.ReadOptional(sample.cpu);
- // Ignore next uint32_t res.
- reader.Skip<uint32_t>();
- }
-
- // Ignored. Checked because we need to access later parts of sample.
- if (sample_type & PERF_SAMPLE_PERIOD) {
- reader.Skip<uint64_t>();
- }
-
- // Ignored.
- // TODO(mayzner): Implement.
- if (sample_type & PERF_SAMPLE_READ) {
- context_->storage->IncrementStats(stats::perf_samples_skipped);
- return base::ErrStatus("PERF_SAMPLE_READ is not supported");
- }
-
- if (sample_type & PERF_SAMPLE_CALLCHAIN) {
- uint64_t vec_size;
- reader.Read(vec_size);
-
- sample.callchain.resize(static_cast<size_t>(vec_size));
- reader.ReadVector(sample.callchain);
- }
-
- return sample;
-}
-
-PerfDataTracker* PerfDataTracker::GetOrCreate(TraceProcessorContext* context) {
- if (!context->perf_data_tracker) {
- context->perf_data_tracker.reset(new PerfDataTracker(context));
- }
- return static_cast<PerfDataTracker*>(context->perf_data_tracker.get());
-}
-} // namespace perf_importer
-} // namespace trace_processor
-} // namespace perfetto
diff --git a/src/trace_processor/importers/perf/perf_data_tracker.h b/src/trace_processor/importers/perf/perf_data_tracker.h
deleted file mode 100644
index 2ed24ed..0000000
--- a/src/trace_processor/importers/perf/perf_data_tracker.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2022 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.
- */
-
-#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PERF_PERF_DATA_TRACKER_H_
-#define SRC_TRACE_PROCESSOR_IMPORTERS_PERF_PERF_DATA_TRACKER_H_
-
-#include <cstdint>
-#include <string>
-#include <vector>
-
-#include "perfetto/ext/base/status_or.h"
-#include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
-#include "src/trace_processor/importers/perf/perf_event.h"
-#include "src/trace_processor/tables/profiler_tables_py.h"
-#include "src/trace_processor/types/destructible.h"
-#include "src/trace_processor/types/trace_processor_context.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace perf_importer {
-
-class Reader;
-using MappingTable = tables::StackProfileMappingTable;
-
-class PerfDataTracker : public Destructible {
- public:
- struct AttrAndIds {
- perf_event_attr attr;
- std::vector<uint64_t> ids;
- };
- struct PerfSample {
- std::optional<uint64_t> id = 0;
- std::optional<uint32_t> pid = 0;
- std::optional<uint32_t> tid = 0;
- std::optional<uint64_t> ts = 0;
- std::optional<uint32_t> cpu = 0;
- std::vector<uint64_t> callchain;
- };
- struct Mmap2Record {
- struct Numeric {
- uint32_t pid;
- uint32_t tid;
- uint64_t addr;
- uint64_t len;
- uint64_t pgoff;
- uint32_t maj;
- uint32_t min;
- uint64_t ino;
- uint64_t ino_generation;
- uint32_t prot;
- uint32_t flags;
- };
- protos::pbzero::Profiling::CpuMode cpu_mode;
- Numeric num;
- std::string filename;
- };
-
- PerfDataTracker(const PerfDataTracker&) = delete;
- PerfDataTracker& operator=(const PerfDataTracker&) = delete;
- explicit PerfDataTracker(TraceProcessorContext* context)
- : context_(context) {}
- ~PerfDataTracker() override;
- static PerfDataTracker* GetOrCreate(TraceProcessorContext* context);
-
- void PushMmap2Record(Mmap2Record record);
-
- base::StatusOr<PerfSample> ParseSample(Reader&, uint64_t sample_type);
-
- private:
- TraceProcessorContext* context_;
-};
-} // namespace perf_importer
-} // namespace trace_processor
-} // namespace perfetto
-
-#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PERF_PERF_DATA_TRACKER_H_
diff --git a/src/trace_processor/importers/perf/perf_data_tracker_unittest.cc b/src/trace_processor/importers/perf/perf_data_tracker_unittest.cc
deleted file mode 100644
index 02f58bb..0000000
--- a/src/trace_processor/importers/perf/perf_data_tracker_unittest.cc
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright (C) 2023 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/perf_data_tracker.h"
-
-#include <stddef.h>
-#include <cstdint>
-#include <cstring>
-#include <memory>
-#include <vector>
-
-#include "perfetto/base/build_config.h"
-#include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
-#include "src/trace_processor/importers/common/address_range.h"
-#include "src/trace_processor/importers/common/mapping_tracker.h"
-#include "src/trace_processor/importers/common/process_tracker.h"
-#include "src/trace_processor/importers/common/stack_profile_tracker.h"
-#include "src/trace_processor/importers/perf/perf_event.h"
-#include "src/trace_processor/importers/perf/reader.h"
-#include "test/gtest_and_gmock.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace perf_importer {
-namespace {
-
-class PerfDataTrackerUnittest : public testing::Test {
- public:
- PerfDataTrackerUnittest() {
- context_.storage = std::make_unique<TraceStorage>();
- context_.process_tracker = std::make_unique<ProcessTracker>(&context_);
- context_.stack_profile_tracker =
- std::make_unique<StackProfileTracker>(&context_);
- context_.mapping_tracker = std::make_unique<MappingTracker>(&context_);
- }
-
- protected:
- TraceProcessorContext context_;
-};
-
-TEST_F(PerfDataTrackerUnittest, FindMapping) {
- PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context_);
-
- PerfDataTracker::Mmap2Record rec;
- rec.cpu_mode = protos::pbzero::Profiling::MODE_USER;
- rec.filename = "file1";
- rec.num.addr = 1000;
- rec.num.len = 100;
- rec.num.pid = 1;
- rec.cpu_mode = protos::pbzero::Profiling::MODE_USER;
- tracker->PushMmap2Record(rec);
-
- rec.num.addr = 2000;
- tracker->PushMmap2Record(rec);
-
- rec.num.addr = 3000;
- tracker->PushMmap2Record(rec);
-
- UserMemoryMapping* mapping =
- context_.mapping_tracker->FindUserMappingForAddress(
- context_.process_tracker->GetOrCreateProcess(1), 2050);
- ASSERT_NE(mapping, nullptr);
- EXPECT_EQ(mapping->memory_range().start(), 2000u);
- EXPECT_EQ(mapping->memory_range().end(), 2100u);
-}
-
-TEST_F(PerfDataTrackerUnittest, FindMappingFalse) {
- PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context_);
-
- PerfDataTracker::Mmap2Record rec;
- rec.cpu_mode = protos::pbzero::Profiling::MODE_USER;
- rec.filename = "file1";
- rec.num.addr = 1000;
- rec.num.len = 100;
- rec.num.pid = 1;
- rec.cpu_mode = protos::pbzero::Profiling::MODE_USER;
- tracker->PushMmap2Record(rec);
-
- UserMemoryMapping* mapping =
- context_.mapping_tracker->FindUserMappingForAddress(
- context_.process_tracker->GetOrCreateProcess(2), 2050);
- EXPECT_EQ(mapping, nullptr);
-}
-
-TEST_F(PerfDataTrackerUnittest, ParseSampleTrivial) {
- PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context_);
-
- uint64_t sample_type = PERF_SAMPLE_TIME;
-
- uint64_t ts = 100;
-
- TraceBlob blob =
- TraceBlob::CopyFrom(static_cast<const void*>(&ts), sizeof(uint64_t));
- Reader reader(TraceBlobView(std::move(blob)));
-
- auto parsed_sample = tracker->ParseSample(reader, sample_type);
- EXPECT_TRUE(parsed_sample.ok());
- EXPECT_EQ(parsed_sample->ts, 100u);
-}
-
-TEST_F(PerfDataTrackerUnittest, ParseSampleCallchain) {
- PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context_);
-
- uint64_t sample_type = PERF_SAMPLE_CALLCHAIN;
-
- struct Sample {
- uint64_t callchain_size; /* if PERF_SAMPLE_CALLCHAIN */
- std::vector<uint64_t> callchain; /* if PERF_SAMPLE_CALLCHAIN */
- };
-
- Sample sample;
- sample.callchain_size = 3;
- sample.callchain = std::vector<uint64_t>{1, 2, 3};
-
- TraceBlob blob = TraceBlob::Allocate(4 * sizeof(uint64_t));
- memcpy(blob.data(), &sample.callchain_size, sizeof(uint64_t));
- memcpy(blob.data() + sizeof(uint64_t), sample.callchain.data(),
- sizeof(uint64_t) * 3);
- Reader reader(TraceBlobView(std::move(blob)));
-
- auto parsed_sample = tracker->ParseSample(reader, sample_type);
- EXPECT_TRUE(parsed_sample.ok());
- EXPECT_EQ(parsed_sample->callchain.size(), 3u);
-}
-
-TEST_F(PerfDataTrackerUnittest, ParseSampleWithoutId) {
- PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context_);
-
- uint64_t sample_type = PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU |
- PERF_SAMPLE_CALLCHAIN;
-
- struct Sample {
- uint32_t pid; /* if PERF_SAMPLE_TID */
- uint32_t tid; /* if PERF_SAMPLE_TID */
- uint64_t ts; /* if PERF_SAMPLE_TIME */
- uint32_t cpu; /* if PERF_SAMPLE_CPU */
- uint32_t res_ignore; /* if PERF_SAMPLE_CPU */
- uint64_t callchain_size; /* if PERF_SAMPLE_CALLCHAIN */
- };
-
- Sample sample;
- sample.pid = 2;
- sample.ts = 100;
- sample.cpu = 1;
- sample.callchain_size = 3;
- std::vector<uint64_t> callchain{1, 2, 3};
-
- TraceBlob blob = TraceBlob::Allocate(sizeof(Sample) + sizeof(uint64_t) * 3);
- memcpy(blob.data(), &sample, sizeof(Sample));
- memcpy(blob.data() + sizeof(Sample), callchain.data(), sizeof(uint64_t) * 3);
-
- Reader reader(TraceBlobView(std::move(blob)));
- EXPECT_TRUE(reader.size_left() >= sizeof(Sample));
-
- auto parsed_sample = tracker->ParseSample(reader, sample_type);
- EXPECT_TRUE(parsed_sample.ok());
- EXPECT_EQ(parsed_sample->callchain.size(), 3u);
- EXPECT_EQ(sample.ts, parsed_sample->ts);
-}
-
-TEST_F(PerfDataTrackerUnittest, ParseSampleWithId) {
- PerfDataTracker* tracker = PerfDataTracker::GetOrCreate(&context_);
-
- uint64_t sample_type = PERF_SAMPLE_CPU | PERF_SAMPLE_TID |
- PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_ID |
- PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_TIME;
-
- struct Sample {
- uint64_t identifier; /* if PERF_SAMPLE_IDENTIFIER */
- uint32_t pid; /* if PERF_SAMPLE_TID */
- uint32_t tid; /* if PERF_SAMPLE_TID */
- uint64_t ts; /* if PERF_SAMPLE_TIME */
- uint64_t id; /* if PERF_SAMPLE_ID */
- uint32_t cpu; /* if PERF_SAMPLE_CPU */
- uint32_t res_ignore; /* if PERF_SAMPLE_CPU */
- uint64_t callchain_size; /* if PERF_SAMPLE_CALLCHAIN */
- };
-
- Sample sample;
- sample.id = 10;
- sample.identifier = 10;
- sample.cpu = 1;
- sample.pid = 2;
- sample.ts = 100;
- sample.callchain_size = 3;
- std::vector<uint64_t> callchain{1, 2, 3};
-
- TraceBlob blob = TraceBlob::Allocate(sizeof(Sample) + sizeof(uint64_t) * 3);
- memcpy(blob.data(), &sample, sizeof(Sample));
- memcpy(blob.data() + sizeof(Sample), callchain.data(), sizeof(uint64_t) * 3);
-
- Reader reader(TraceBlobView(std::move(blob)));
-
- auto parsed_sample = tracker->ParseSample(reader, sample_type);
- EXPECT_TRUE(parsed_sample.ok());
- EXPECT_EQ(parsed_sample->callchain.size(), 3u);
- EXPECT_EQ(100u, parsed_sample->ts);
-}
-
-} // namespace
-} // namespace perf_importer
-} // namespace trace_processor
-} // namespace perfetto
diff --git a/src/trace_processor/importers/perf/perf_event.h b/src/trace_processor/importers/perf/perf_event.h
index 4763e23..c45f4de 100644
--- a/src/trace_processor/importers/perf/perf_event.h
+++ b/src/trace_processor/importers/perf/perf_event.h
@@ -238,6 +238,7 @@
PERF_RECORD_MISC_GUEST_USER = 5,
PERF_RECORD_MISC_MMAP_BUILD_ID = 1U << 14,
+ PERF_RECORD_MISC_EXT_RESERVED = 1U << 15,
};
enum perf_event_read_format {
diff --git a/src/trace_processor/importers/perf/perf_event_attr.cc b/src/trace_processor/importers/perf/perf_event_attr.cc
index 1abf8d0..f00f4eb 100644
--- a/src/trace_processor/importers/perf/perf_event_attr.cc
+++ b/src/trace_processor/importers/perf/perf_event_attr.cc
@@ -21,7 +21,10 @@
#include <cstring>
#include <optional>
+#include "src/trace_processor/importers/perf/perf_counter.h"
#include "src/trace_processor/importers/perf/perf_event.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
namespace perfetto::trace_processor::perf_importer {
@@ -90,11 +93,36 @@
}
} // namespace
-PerfEventAttr::PerfEventAttr(perf_event_attr attr)
- : attr_(std::move(attr)),
+PerfEventAttr::PerfEventAttr(TraceProcessorContext* context,
+ uint32_t perf_session_id,
+ perf_event_attr attr)
+ : context_(context),
+ perf_session_id_(perf_session_id),
+ attr_(std::move(attr)),
time_offset_from_start_(TimeOffsetFromStartOfSampleRecord(attr_)),
time_offset_from_end_(TimeOffsetFromEndOfNonSampleRecord(attr_)),
id_offset_from_start_(IdOffsetFromStartOfSampleRecord(attr_)),
id_offset_from_end_(IdOffsetFromEndOfNonSampleRecord(attr_)) {}
+PerfEventAttr::~PerfEventAttr() = default;
+
+PerfCounter& PerfEventAttr::GetOrCreateCounter(uint32_t cpu) const {
+ auto it = counters_.find(cpu);
+ if (it == counters_.end()) {
+ it = counters_.emplace(cpu, CreateCounter(cpu)).first;
+ }
+ return it->second;
+}
+
+PerfCounter PerfEventAttr::CreateCounter(uint32_t cpu) const {
+ return PerfCounter(context_->storage->mutable_counter_table(),
+ context_->storage->mutable_perf_counter_track_table()
+ ->Insert({context_->storage->InternString(""),
+ std::nullopt, std::nullopt, std::nullopt,
+ context_->storage->InternString(""),
+ context_->storage->InternString(""),
+ perf_session_id_, cpu, is_timebase()})
+ .row_reference);
+}
+
} // namespace perfetto::trace_processor::perf_importer
diff --git a/src/trace_processor/importers/perf/perf_event_attr.h b/src/trace_processor/importers/perf/perf_event_attr.h
index 4f219aa..77e52f4 100644
--- a/src/trace_processor/importers/perf/perf_event_attr.h
+++ b/src/trace_processor/importers/perf/perf_event_attr.h
@@ -21,17 +21,26 @@
#include <cstddef>
#include <cstdint>
#include <optional>
+#include <unordered_map>
#include "perfetto/ext/base/string_view.h"
#include "perfetto/trace_processor/ref_counted.h"
+#include "src/trace_processor/importers/perf/perf_counter.h"
#include "src/trace_processor/importers/perf/perf_event.h"
-namespace perfetto::trace_processor::perf_importer {
+namespace perfetto::trace_processor {
+
+class TraceProcessorContext;
+
+namespace perf_importer {
// Wrapper around a `perf_event_attr` object that add some helper methods.
class PerfEventAttr : public RefCounted {
public:
- explicit PerfEventAttr(perf_event_attr attr);
+ PerfEventAttr(TraceProcessorContext* context,
+ uint32_t perf_session_id_,
+ perf_event_attr attr);
+ ~PerfEventAttr();
uint64_t sample_type() const { return attr_.sample_type; }
uint64_t read_format() const { return attr_.read_format; }
bool sample_id_all() const { return !!attr_.sample_id_all; }
@@ -48,12 +57,6 @@
return attr_.freq ? std::make_optional(attr_.sample_freq) : std::nullopt;
}
- bool is_timebase() const {
- // This is what simpleperf uses for events that are not supposed to sample
- // TODO(b/334978369): Determine if there is a better way to figure this out.
- return attr_.sample_period < (1ull << 62);
- }
-
// Offset from the end of a record's payload to the time filed (if present).
// To be used with non `PERF_RECORD_SAMPLE` records
std::optional<size_t> time_offset_from_end() const {
@@ -81,14 +84,28 @@
return id_offset_from_end_;
}
+ PerfCounter& GetOrCreateCounter(uint32_t cpu) const;
+
private:
+ bool is_timebase() const {
+ // This is what simpleperf uses for events that are not supposed to sample
+ // TODO(b/334978369): Determine if there is a better way to figure this out.
+ return attr_.sample_period < (1ull << 62);
+ }
+
+ PerfCounter CreateCounter(uint32_t cpu) const;
+
+ TraceProcessorContext* const context_;
+ uint32_t perf_session_id_;
perf_event_attr attr_;
std::optional<size_t> time_offset_from_start_;
std::optional<size_t> time_offset_from_end_;
std::optional<size_t> id_offset_from_start_;
std::optional<size_t> id_offset_from_end_;
+ mutable std::unordered_map<uint32_t, PerfCounter> counters_;
};
-} // namespace perfetto::trace_processor::perf_importer
+} // namespace perf_importer
+} // namespace perfetto::trace_processor
#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PERF_PERF_EVENT_ATTR_H_
diff --git a/src/trace_processor/importers/perf/perf_session.cc b/src/trace_processor/importers/perf/perf_session.cc
index 86d983d..7619066 100644
--- a/src/trace_processor/importers/perf/perf_session.cc
+++ b/src/trace_processor/importers/perf/perf_session.cc
@@ -46,11 +46,13 @@
return base::ErrStatus("No perf_event_attr");
}
- const PerfEventAttr base_attr(attr_with_ids_[0].attr);
+ const PerfEventAttr base_attr(context_, perf_session_id_,
+ attr_with_ids_[0].attr);
base::FlatHashMap<uint64_t, RefPtr<PerfEventAttr>> attrs_by_id;
for (const auto& entry : attr_with_ids_) {
- RefPtr<PerfEventAttr> attr(new PerfEventAttr(entry.attr));
+ RefPtr<PerfEventAttr> attr(
+ new PerfEventAttr(context_, perf_session_id_, entry.attr));
if (base_attr.sample_id_all() != attr->sample_id_all()) {
return base::ErrStatus(
"perf_event_attr with different sample_id_all values");
diff --git a/src/trace_processor/importers/perf/perf_session.h b/src/trace_processor/importers/perf/perf_session.h
index abea41b..eb509d0 100644
--- a/src/trace_processor/importers/perf/perf_session.h
+++ b/src/trace_processor/importers/perf/perf_session.h
@@ -20,7 +20,6 @@
#include <sys/types.h>
#include <cstddef>
#include <cstdint>
-#include <optional>
#include "perfetto/ext/base/flat_hash_map.h"
#include "perfetto/ext/base/status_or.h"
@@ -29,15 +28,19 @@
#include "src/trace_processor/importers/perf/perf_event.h"
#include "src/trace_processor/importers/perf/perf_event_attr.h"
-namespace perfetto::trace_processor::perf_importer {
+namespace perfetto::trace_processor {
+
+class TraceProcessorContext;
+
+namespace perf_importer {
// Helper to deal with perf_event_attr instances in a perf file.
class PerfSession : public RefCounted {
public:
class Builder {
public:
- explicit Builder(uint32_t perf_session_id)
- : perf_session_id_(perf_session_id) {}
+ Builder(TraceProcessorContext* context, uint32_t perf_session_id)
+ : context_(context), perf_session_id_(perf_session_id) {}
base::StatusOr<RefPtr<PerfSession>> Build();
Builder& AddAttrAndIds(perf_event_attr attr, std::vector<uint64_t> ids) {
attr_with_ids_.push_back({std::move(attr), std::move(ids)});
@@ -50,6 +53,7 @@
std::vector<uint64_t> ids;
};
+ TraceProcessorContext* const context_;
uint32_t perf_session_id_;
std::vector<PerfEventAttrWithIds> attr_with_ids_;
};
@@ -83,6 +87,7 @@
bool has_single_perf_event_attr_;
};
-} // namespace perfetto::trace_processor::perf_importer
+} // namespace perf_importer
+} // namespace perfetto::trace_processor
#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PERF_PERF_SESSION_H_
diff --git a/src/trace_processor/importers/perf/perf_session_unittest.cc b/src/trace_processor/importers/perf/perf_session_unittest.cc
index 0ab85f2..fd6bfa3 100644
--- a/src/trace_processor/importers/perf/perf_session_unittest.cc
+++ b/src/trace_processor/importers/perf/perf_session_unittest.cc
@@ -41,12 +41,12 @@
}
TEST(PerfSessionTest, NoAttrBuildFails) {
- PerfSession::Builder builder(0);
+ PerfSession::Builder builder(nullptr, 0);
EXPECT_FALSE(builder.Build().ok());
}
TEST(PerfSessionTest, OneAttrAndNoIdBuildSucceeds) {
- PerfSession::Builder builder(0);
+ PerfSession::Builder builder(nullptr, 0);
perf_event_attr attr;
attr.sample_id_all = false;
attr.sample_type = PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_CPU | PERF_SAMPLE_TIME;
@@ -61,7 +61,7 @@
}
TEST(PerfSessionTest, MultipleAttrsAndNoIdBuildFails) {
- PerfSession::Builder builder(0);
+ PerfSession::Builder builder(nullptr, 0);
perf_event_attr attr;
attr.sample_id_all = true;
attr.sample_type = PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_CPU | PERF_SAMPLE_TIME;
@@ -71,7 +71,7 @@
}
TEST(PerfSessionTest, MultipleIdsSameAttrAndNoIdCanExtractAttrFromRecord) {
- PerfSession::Builder builder(0);
+ PerfSession::Builder builder(nullptr, 0);
perf_event_attr attr;
attr.sample_id_all = true;
attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_CPU | PERF_SAMPLE_TIME;
@@ -95,7 +95,7 @@
}
TEST(PerfSessionTest, NoCommonSampleIdAllBuildFails) {
- PerfSession::Builder builder(0);
+ PerfSession::Builder builder(nullptr, 0);
perf_event_attr attr;
attr.sample_id_all = true;
attr.sample_type = PERF_SAMPLE_IDENTIFIER;
@@ -111,7 +111,7 @@
}
TEST(PerfSessionTest, NoCommonOffsetForSampleBuildFails) {
- PerfSession::Builder builder(0);
+ PerfSession::Builder builder(nullptr, 0);
perf_event_attr attr;
attr.sample_id_all = true;
attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_ID;
@@ -122,7 +122,7 @@
}
TEST(PerfSessionTest, NoCommonOffsetForNonSampleBuildFails) {
- PerfSession::Builder builder(0);
+ PerfSession::Builder builder(nullptr, 0);
perf_event_attr attr;
attr.sample_id_all = true;
attr.sample_type = PERF_SAMPLE_ID | PERF_SAMPLE_TID;
@@ -138,7 +138,7 @@
}
TEST(PerfSessionTest, NoCommonOffsetForNonSampleAndNoSampleIdAllBuildSucceeds) {
- PerfSession::Builder builder(0);
+ PerfSession::Builder builder(nullptr, 0);
perf_event_attr attr;
attr.sample_id_all = false;
attr.sample_type = PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_TID;
@@ -149,7 +149,7 @@
}
TEST(PerfSessionTest, MultiplesessionBuildSucceeds) {
- PerfSession::Builder builder(0);
+ PerfSession::Builder builder(nullptr, 0);
perf_event_attr attr;
attr.sample_id_all = true;
attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_ID;
@@ -159,7 +159,7 @@
}
TEST(PerfSessionTest, FindAttrInRecordWithId) {
- PerfSession::Builder builder(0);
+ PerfSession::Builder builder(nullptr, 0);
perf_event_attr attr;
attr.sample_id_all = true;
attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_ID;
@@ -194,7 +194,7 @@
}
TEST(PerfSessionTest, FindAttrInRecordWithIdentifier) {
- PerfSession::Builder builder(0);
+ PerfSession::Builder builder(nullptr, 0);
perf_event_attr attr;
attr.sample_id_all = true;
attr.sample_type = PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_IP;
diff --git a/src/trace_processor/importers/perf/reader.h b/src/trace_processor/importers/perf/reader.h
index faf31a2..91a4253 100644
--- a/src/trace_processor/importers/perf/reader.h
+++ b/src/trace_processor/importers/perf/reader.h
@@ -26,7 +26,9 @@
#include <type_traits>
#include <vector>
+#include "perfetto/ext/base/string_view.h"
#include "perfetto/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/importers/perf/perf_event.h"
namespace perfetto::trace_processor::perf_importer {
@@ -45,6 +47,49 @@
// methods are called.
size_t size_left() const { return static_cast<size_t>(end_ - current_); }
+ bool ReadStringView(base::StringView& str, size_t size) {
+ if (size_left() < size) {
+ return false;
+ }
+ str = base::StringView(reinterpret_cast<const char*>(current_), size);
+ current_ += size;
+ return true;
+ }
+
+ bool ReadPerfEventAttr(perf_event_attr& attr, size_t attr_size) {
+ const size_t bytes_to_read = std::min(attr_size, sizeof(attr));
+ const size_t bytes_to_skip = attr_size - bytes_to_read;
+ static_assert(std::has_unique_object_representations_v<perf_event_attr>);
+
+ if (size_left() < bytes_to_read + bytes_to_skip) {
+ return false;
+ }
+
+ memset(&attr, 0, sizeof(attr));
+
+ return Read(&attr, bytes_to_read) && Skip(bytes_to_skip);
+ }
+
+ bool ReadBlob(TraceBlobView& blob, uint32_t size) {
+ if (size_left() < size) {
+ return false;
+ }
+ blob = TraceBlobView(buffer_, static_cast<size_t>(end_ - current_), size);
+ current_ += size;
+ return true;
+ }
+
+ bool ReadStringUntilEndOrNull(std::string& out) {
+ const uint8_t* ptr = current_;
+ while (ptr != end_ && *ptr != 0) {
+ ++ptr;
+ }
+ out = std::string(reinterpret_cast<const char*>(current_),
+ static_cast<size_t>(ptr - current_));
+ current_ = ptr;
+ return true;
+ }
+
template <typename T>
bool Read(T& obj) {
static_assert(std::has_unique_object_representations_v<T>);
diff --git a/src/trace_processor/importers/perf/record_parser.cc b/src/trace_processor/importers/perf/record_parser.cc
index 847adb7..7371adf 100644
--- a/src/trace_processor/importers/perf/record_parser.cc
+++ b/src/trace_processor/importers/perf/record_parser.cc
@@ -16,45 +16,98 @@
#include "src/trace_processor/importers/perf/record_parser.h"
+#include <cstdint>
#include <optional>
#include <string>
#include <vector>
+
#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/public/compiler.h"
+#include "perfetto/trace_processor/ref_counted.h"
#include "src/trace_processor/importers/common/mapping_tracker.h"
#include "src/trace_processor/importers/common/process_tracker.h"
-#include "src/trace_processor/importers/perf/perf_data_tracker.h"
+#include "src/trace_processor/importers/common/stack_profile_tracker.h"
+#include "src/trace_processor/importers/perf/perf_counter.h"
+#include "src/trace_processor/importers/perf/perf_event.h"
+#include "src/trace_processor/importers/perf/perf_event_attr.h"
#include "src/trace_processor/importers/perf/reader.h"
#include "src/trace_processor/importers/perf/record.h"
+#include "src/trace_processor/importers/perf/sample.h"
+#include "src/trace_processor/importers/proto/perf_sample_tracker.h"
+#include "src/trace_processor/importers/proto/profile_packet_utils.h"
#include "src/trace_processor/storage/stats.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/tables/profiler_tables_py.h"
+#include "src/trace_processor/util/build_id.h"
#include "src/trace_processor/util/status_macros.h"
namespace perfetto {
namespace trace_processor {
namespace perf_importer {
+namespace {
+
+CreateMappingParams BuildCreateMappingParams(
+ const CommonMmapRecordFields& fields,
+ std::string filename,
+ std::optional<BuildId> build_id = std::nullopt) {
+ return {AddressRange::FromStartAndSize(fields.addr, fields.len), fields.pgoff,
+ // start_offset: This is the offset into the file where the ELF header
+ // starts. We assume all file mappings are ELF files an thus this
+ // offset is 0.
+ 0,
+ // load_bias: This can only be read out of the actual ELF file, which
+ // we do not have here, so we set it to 0. When symbolizing we will
+ // hopefully have the real load bias and we can compensate there for a
+ // possible mismatch.
+ 0, std::move(filename), std::move(build_id)};
+}
+
+bool IsInKernel(protos::pbzero::Profiling::CpuMode cpu_mode) {
+ switch (cpu_mode) {
+ case protos::pbzero::Profiling::MODE_UNKNOWN:
+ PERFETTO_FATAL("Unknown CPU mode");
+ case protos::pbzero::Profiling::MODE_GUEST_KERNEL:
+ case protos::pbzero::Profiling::MODE_KERNEL:
+ return true;
+ case protos::pbzero::Profiling::MODE_USER:
+ case protos::pbzero::Profiling::MODE_HYPERVISOR:
+ case protos::pbzero::Profiling::MODE_GUEST_USER:
+ return false;
+ }
+ PERFETTO_FATAL("For GCC.");
+}
+
+} // namespace
using FramesTable = tables::StackProfileFrameTable;
using CallsitesTable = tables::StackProfileCallsiteTable;
RecordParser::RecordParser(TraceProcessorContext* context)
- : context_(context), tracker_(PerfDataTracker::GetOrCreate(context_)) {}
+ : context_(context) {}
RecordParser::~RecordParser() = default;
void RecordParser::ParsePerfRecord(int64_t ts, Record record) {
if (base::Status status = ParseRecord(ts, std::move(record)); !status.ok()) {
- context_->storage->IncrementStats(stats::perf_record_skipped);
+ context_->storage->IncrementStats(record.header.type == PERF_RECORD_SAMPLE
+ ? stats::perf_samples_skipped
+ : stats::perf_record_skipped);
}
}
base::Status RecordParser::ParseRecord(int64_t ts, Record record) {
switch (record.header.type) {
+ case PERF_RECORD_COMM:
+ return ParseComm(std::move(record));
+
case PERF_RECORD_SAMPLE:
return ParseSample(ts, std::move(record));
+ case PERF_RECORD_MMAP:
+ return ParseMmap(std::move(record));
+
case PERF_RECORD_MMAP2:
return ParseMmap2(std::move(record));
@@ -62,115 +115,193 @@
case PERF_RECORD_AUXTRACE:
case PERF_RECORD_AUXTRACE_INFO:
// These should be dealt with at tokenization time
- PERFETTO_CHECK(false);
+ PERFETTO_FATAL("Unexpected record type at parsing time: %" PRIu32,
+ record.header.type);
default:
- PERFETTO_ELOG("Unknown PERF_RECORD with type %" PRIu32,
- record.header.type);
+ context_->storage->IncrementIndexedStats(
+ stats::perf_unknown_record_type,
+ static_cast<int>(record.header.type));
+ return base::ErrStatus("Unknown PERF_RECORD with type %" PRIu32,
+ record.header.type);
}
- return base::OkStatus();
}
base::Status RecordParser::ParseSample(int64_t ts, Record record) {
- PERFETTO_CHECK(record.attr);
+ Sample sample;
+ RETURN_IF_ERROR(sample.Parse(ts, record));
- Reader reader(record.payload.copy());
- ASSIGN_OR_RETURN(PerfDataTracker::PerfSample sample,
- tracker_->ParseSample(reader, record.attr->sample_type()));
-
- // The sample has been validated in tokenizer so callchain shouldn't be empty.
- PERFETTO_CHECK(!sample.callchain.empty());
-
- // First instruction pointer in the callchain should be from kernel space, so
- // it shouldn't be available in mappings.
- UniquePid upid = context_->process_tracker->GetOrCreateProcess(*sample.pid);
- if (context_->mapping_tracker->FindUserMappingForAddress(
- upid, sample.callchain.front())) {
- context_->storage->IncrementStats(stats::perf_samples_skipped);
- return base::ErrStatus(
- "Expected kernel mapping for first instruction pointer, but user space "
- "found.");
+ if (!sample.period.has_value() && record.attr != nullptr) {
+ sample.period = record.attr->sample_period();
}
- if (sample.callchain.size() == 1) {
- context_->storage->IncrementStats(stats::perf_samples_skipped);
- return base::ErrStatus("Invalid callchain size of 1.");
+ return InternSample(std::move(sample));
+}
+
+base::Status RecordParser::InternSample(Sample sample) {
+ if (!sample.time.has_value()) {
+ // We do not really use this TS as this is using the perf clock, but we need
+ // it to be present so that we can compute the trace_ts done during
+ // tokenization. (Actually at tokenization time we do estimate a trace_ts if
+ // no perf ts is present, but for samples we want this to be as accurate as
+ // possible)
+ base::ErrStatus("Can not parse samples with no PERF_SAMPLE_TIME field");
}
- std::vector<FramesTable::Row> frame_rows;
- for (uint32_t i = 1; i < sample.callchain.size(); i++) {
- UserMemoryMapping* mapping =
- context_->mapping_tracker->FindUserMappingForAddress(
- upid, sample.callchain[i]);
- if (!mapping) {
- context_->storage->IncrementStats(stats::perf_samples_skipped);
- return base::ErrStatus("Did not find mapping for address %" PRIu64
- " in process with upid %" PRIu32,
- sample.callchain[i], upid);
+ if (!sample.pid_tid.has_value()) {
+ base::ErrStatus("Can not parse samples with no PERF_SAMPLE_TID field");
+ }
+
+ if (!sample.cpu.has_value()) {
+ base::ErrStatus("Can not parse samples with no PERF_SAMPLE_CPU field");
+ }
+
+ UniqueTid utid = context_->process_tracker->UpdateThread(sample.pid_tid->tid,
+ sample.pid_tid->pid);
+ const auto upid = *context_->storage->thread_table()
+ .FindById(tables::ThreadTable::Id(utid))
+ ->upid();
+
+ if (sample.callchain.empty() && sample.ip.has_value()) {
+ sample.callchain.push_back(Sample::Frame{sample.cpu_mode, *sample.ip});
+ }
+ std::optional<CallsiteId> callsite_id =
+ InternCallchain(upid, sample.callchain);
+
+ context_->storage->mutable_perf_sample_table()->Insert(
+ {sample.trace_ts, utid, *sample.cpu,
+ context_->storage->InternString(
+ ProfilePacketUtils::StringifyCpuMode(sample.cpu_mode)),
+ callsite_id, std::nullopt, sample.perf_session->perf_session_id()});
+
+ return UpdateCounters(sample);
+}
+
+std::optional<CallsiteId> RecordParser::InternCallchain(
+ UniquePid upid,
+ const std::vector<Sample::Frame>& callchain) {
+ if (callchain.empty()) {
+ return std::nullopt;
+ }
+
+ auto& stack_profile_tracker = *context_->stack_profile_tracker;
+ auto& mapping_tracker = *context_->mapping_tracker;
+
+ std::optional<CallsiteId> parent;
+ uint32_t depth = 0;
+ for (auto it = callchain.rbegin(); it != callchain.rend(); ++it) {
+ VirtualMemoryMapping* mapping;
+ if (IsInKernel(it->cpu_mode)) {
+ mapping = mapping_tracker.FindKernelMappingForAddress(it->ip);
+ } else {
+ mapping = mapping_tracker.FindUserMappingForAddress(upid, it->ip);
}
- FramesTable::Row new_row;
- std::string mock_name =
- base::StackString<1024>(
- "%" PRIu64, sample.callchain[i] - mapping->memory_range().start())
- .ToStdString();
- new_row.name = context_->storage->InternString(mock_name.c_str());
- new_row.mapping = mapping->mapping_id();
- new_row.rel_pc =
- static_cast<int64_t>(mapping->ToRelativePc(sample.callchain[i]));
- frame_rows.push_back(new_row);
+
+ if (!mapping) {
+ context_->storage->IncrementStats(stats::perf_dummy_mapping_used);
+ // Simpleperf will not create mappings for anonymous executable mappings
+ // which are used by JITted code (e.g. V8 JavaScript).
+ mapping = mapping_tracker.GetDummyMapping();
+ }
+
+ const FrameId frame_id =
+ mapping->InternFrame(mapping->ToRelativePc(it->ip), "");
+
+ parent = stack_profile_tracker.InternCallsite(parent, frame_id, depth);
+ depth++;
+ }
+ return parent;
+}
+
+base::Status RecordParser::ParseComm(Record record) {
+ Reader reader(record.payload.copy());
+ uint32_t pid;
+ uint32_t tid;
+ std::string comm;
+ if (!reader.Read(pid) || !reader.Read(tid) || !reader.ReadCString(comm)) {
+ return base::ErrStatus("Failed to parse PERF_RECORD_COMM");
}
- // Insert frames. We couldn't do it before as no frames should be added if the
- // mapping couldn't be found for any of them.
- const auto& frames = context_->storage->mutable_stack_profile_frame_table();
- std::vector<FramesTable::Id> frame_ids;
- for (const auto& row : frame_rows) {
- frame_ids.push_back(frames->Insert(row).id);
- }
-
- // Insert callsites.
- const auto& callsites =
- context_->storage->mutable_stack_profile_callsite_table();
-
- std::optional<CallsitesTable::Id> parent_callsite_id;
- for (uint32_t i = 0; i < frame_ids.size(); i++) {
- CallsitesTable::Row callsite_row;
- callsite_row.frame_id = frame_ids[i];
- callsite_row.depth = i;
- callsite_row.parent_id = parent_callsite_id;
- parent_callsite_id = callsites->Insert(callsite_row).id;
- }
-
- // Insert stack sample.
- tables::PerfSampleTable::Row perf_sample_row;
- perf_sample_row.callsite_id = parent_callsite_id;
- perf_sample_row.ts = ts;
- if (sample.cpu) {
- perf_sample_row.cpu = *sample.cpu;
- }
- if (sample.tid) {
- auto utid = context_->process_tracker->GetOrCreateThread(*sample.tid);
- perf_sample_row.utid = utid;
- }
- context_->storage->mutable_perf_sample_table()->Insert(perf_sample_row);
+ context_->process_tracker->UpdateThread(tid, pid);
+ context_->process_tracker->UpdateThreadName(
+ tid, context_->storage->InternString(base::StringView(comm)),
+ ThreadNamePriority::kFtrace);
return base::OkStatus();
}
-base::Status RecordParser::ParseMmap2(Record record) {
- Reader reader(record.payload.copy());
- PerfDataTracker::Mmap2Record mmap2;
- reader.Read(mmap2.num);
- std::vector<char> filename_buffer(reader.size_left());
- reader.ReadVector(filename_buffer);
- if (filename_buffer.back() != '\0') {
- return base::ErrStatus(
- "Invalid MMAP2 record: filename is not null terminated.");
+base::Status RecordParser::ParseMmap(Record record) {
+ MmapRecord mmap;
+ RETURN_IF_ERROR(mmap.Parse(record));
+ if (IsInKernel(record.GetCpuMode())) {
+ context_->mapping_tracker->CreateKernelMemoryMapping(
+ BuildCreateMappingParams(mmap, std::move(mmap.filename)));
+ return base::OkStatus();
}
- mmap2.filename = std::string(filename_buffer.begin(), filename_buffer.end());
- PERFETTO_CHECK(reader.size_left() == 0);
- mmap2.cpu_mode = record.GetCpuMode();
- tracker_->PushMmap2Record(std::move(mmap2));
+
+ context_->mapping_tracker->CreateUserMemoryMapping(
+ GetUpid(mmap), BuildCreateMappingParams(mmap, std::move(mmap.filename)));
+
+ return base::OkStatus();
+}
+
+util::Status RecordParser::ParseMmap2(Record record) {
+ Mmap2Record mmap2;
+ RETURN_IF_ERROR(mmap2.Parse(record));
+ if (IsInKernel(record.GetCpuMode())) {
+ context_->mapping_tracker->CreateKernelMemoryMapping(
+ BuildCreateMappingParams(mmap2, std::move(mmap2.filename)));
+ return base::OkStatus();
+ }
+
+ context_->mapping_tracker->CreateUserMemoryMapping(
+ GetUpid(mmap2), BuildCreateMappingParams(mmap2, std::move(mmap2.filename),
+ mmap2.GetBuildId()));
+
+ return base::OkStatus();
+}
+
+UniquePid RecordParser::GetUpid(const CommonMmapRecordFields& fields) const {
+ UniqueTid utid =
+ context_->process_tracker->UpdateThread(fields.tid, fields.pid);
+ auto upid = context_->storage->thread_table()
+ .FindById(tables::ThreadTable::Id(utid))
+ ->upid();
+ PERFETTO_CHECK(upid.has_value());
+ return *upid;
+}
+
+base::Status RecordParser::UpdateCounters(const Sample& sample) {
+ if (!sample.read_groups.empty()) {
+ return UpdateCountersInReadGroups(sample);
+ }
+
+ if (!sample.period.has_value() && !sample.attr->sample_period().has_value()) {
+ return base::ErrStatus("No period for sample");
+ }
+
+ uint64_t period = sample.period.has_value() ? *sample.period
+ : *sample.attr->sample_period();
+ sample.attr->GetOrCreateCounter(*sample.cpu)
+ .AddDelta(sample.trace_ts, static_cast<double>(period));
+ return base::OkStatus();
+}
+
+base::Status RecordParser::UpdateCountersInReadGroups(const Sample& sample) {
+ if (!sample.cpu.has_value()) {
+ return base::ErrStatus("No cpu for sample");
+ }
+
+ for (const auto& entry : sample.read_groups) {
+ RefPtr<const PerfEventAttr> attr =
+ sample.perf_session->FindAttrForEventId(*entry.event_id);
+ if (PERFETTO_UNLIKELY(!attr)) {
+ return base::ErrStatus("No perf_event_attr for id %" PRIu64,
+ *entry.event_id);
+ }
+ attr->GetOrCreateCounter(*sample.cpu)
+ .AddCount(sample.trace_ts, static_cast<double>(entry.value));
+ }
return base::OkStatus();
}
diff --git a/src/trace_processor/importers/perf/record_parser.h b/src/trace_processor/importers/perf/record_parser.h
index bae837a..0a71b49 100644
--- a/src/trace_processor/importers/perf/record_parser.h
+++ b/src/trace_processor/importers/perf/record_parser.h
@@ -18,14 +18,26 @@
#define SRC_TRACE_PROCESSOR_IMPORTERS_PERF_RECORD_PARSER_H_
#include <stdint.h>
+#include <cstdint>
+#include <vector>
+#include "perfetto/base/status.h"
#include "src/trace_processor/importers/common/trace_parser.h"
-#include "src/trace_processor/importers/perf/perf_data_tracker.h"
+#include "src/trace_processor/importers/perf/mmap_record.h"
+#include "src/trace_processor/importers/perf/record.h"
+#include "src/trace_processor/importers/perf/sample.h"
+#include "src/trace_processor/storage/trace_storage.h"
namespace perfetto {
namespace trace_processor {
+
+class TraceProcessorContext;
+
namespace perf_importer {
+class PerfDataTracker;
+class Reader;
+
// Parses samples from perf.data files.
class RecordParser : public PerfRecordParser {
public:
@@ -37,10 +49,22 @@
private:
base::Status ParseRecord(int64_t timestamp, Record record);
base::Status ParseSample(int64_t ts, Record record);
+ base::Status ParseComm(Record record);
+ base::Status ParseMmap(Record record);
base::Status ParseMmap2(Record record);
+ base::Status InternSample(Sample sample);
+
+ base::Status UpdateCounters(const Sample& sample);
+ base::Status UpdateCountersInReadGroups(const Sample& sample);
+
+ std::optional<CallsiteId> InternCallchain(
+ UniquePid upid,
+ const std::vector<Sample::Frame>& callchain);
+
+ UniquePid GetUpid(const CommonMmapRecordFields& fields) const;
+
TraceProcessorContext* context_ = nullptr;
- PerfDataTracker* tracker_ = nullptr;
};
} // namespace perf_importer
diff --git a/src/trace_processor/importers/perf/sample.cc b/src/trace_processor/importers/perf/sample.cc
new file mode 100644
index 0000000..63ded53
--- /dev/null
+++ b/src/trace_processor/importers/perf/sample.cc
@@ -0,0 +1,252 @@
+/*
+ * 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/sample.h"
+
+#include <cstdint>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/base/status.h"
+#include "perfetto/public/compiler.h"
+#include "src/trace_processor/importers/perf/reader.h"
+#include "src/trace_processor/importers/perf/record.h"
+
+namespace perfetto::trace_processor::perf_importer {
+namespace {
+
+bool ParseSampleReadGroup(Reader& reader,
+ uint64_t read_format,
+ uint64_t num_records,
+ std::vector<Sample::ReadGroup>& out) {
+ out.resize(num_records);
+ for (auto& read : out) {
+ if (PERFETTO_UNLIKELY(!reader.Read(read.value))) {
+ return false;
+ }
+
+ if (read_format & PERF_FORMAT_ID) {
+ if (PERFETTO_UNLIKELY(!reader.ReadOptional(read.event_id))) {
+ return false;
+ }
+ }
+
+ if (read_format & PERF_FORMAT_LOST) {
+ uint64_t lost;
+ if (PERFETTO_UNLIKELY(!reader.Read(lost))) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool ParseSampleRead(Reader& reader,
+ uint64_t read_format,
+ std::vector<Sample::ReadGroup>& out) {
+ uint64_t value_or_nr;
+
+ if (PERFETTO_UNLIKELY(!reader.Read(value_or_nr))) {
+ return false;
+ }
+
+ if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
+ uint64_t total_time_enabled;
+ if (PERFETTO_UNLIKELY(!reader.Read(total_time_enabled))) {
+ return false;
+ }
+ }
+
+ if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) {
+ uint64_t total_time_running;
+ if (PERFETTO_UNLIKELY(!reader.Read(total_time_running))) {
+ return false;
+ }
+ }
+
+ if (read_format & PERF_FORMAT_GROUP) {
+ return ParseSampleReadGroup(reader, read_format, value_or_nr, out);
+ }
+
+ std::optional<uint64_t> event_id;
+ if (read_format & PERF_FORMAT_ID) {
+ event_id.emplace(0);
+ if (PERFETTO_UNLIKELY(!reader.ReadOptional(event_id))) {
+ return false;
+ }
+ }
+
+ if (read_format & PERF_FORMAT_LOST) {
+ uint64_t lost;
+ if (PERFETTO_UNLIKELY(!reader.Read(lost))) {
+ return false;
+ }
+ }
+
+ out.push_back({event_id, value_or_nr});
+
+ return true;
+}
+
+protos::pbzero::Profiling::CpuMode PerfCallchainContextToCpuMode(uint64_t ip) {
+ switch (ip) {
+ case PERF_CONTEXT_HV:
+ return protos::pbzero::Profiling::MODE_HYPERVISOR;
+ case PERF_CONTEXT_KERNEL:
+ return protos::pbzero::Profiling::MODE_KERNEL;
+ case PERF_CONTEXT_USER:
+ return protos::pbzero::Profiling::MODE_USER;
+ case PERF_CONTEXT_GUEST_KERNEL:
+ return protos::pbzero::Profiling::MODE_GUEST_KERNEL;
+ case PERF_CONTEXT_GUEST_USER:
+ return protos::pbzero::Profiling::MODE_GUEST_USER;
+ case PERF_CONTEXT_GUEST:
+ default:
+ return protos::pbzero::Profiling::MODE_UNKNOWN;
+ }
+ PERFETTO_FATAL("For GCC");
+}
+
+bool IsPerfContextMark(uint64_t ip) {
+ return ip >= PERF_CONTEXT_MAX;
+}
+
+bool ParseSampleCallchain(Reader& reader,
+ protos::pbzero::Profiling::CpuMode cpu_mode,
+ std::vector<Sample::Frame>& out) {
+ uint64_t nr;
+ if (PERFETTO_UNLIKELY(!reader.Read(nr))) {
+ return false;
+ }
+
+ std::vector<Sample::Frame> frames;
+ frames.reserve(nr);
+ for (; nr != 0; --nr) {
+ uint64_t ip;
+ if (PERFETTO_UNLIKELY(!reader.Read(ip))) {
+ return false;
+ }
+ if (PERFETTO_UNLIKELY(IsPerfContextMark(ip))) {
+ cpu_mode = PerfCallchainContextToCpuMode(ip);
+ continue;
+ }
+ frames.push_back({cpu_mode, ip});
+ }
+
+ out = std::move(frames);
+ return true;
+}
+} // namespace
+
+base::Status Sample::Parse(int64_t in_trace_ts, const Record& record) {
+ PERFETTO_CHECK(record.attr);
+ const uint64_t sample_type = record.attr->sample_type();
+
+ trace_ts = in_trace_ts;
+ cpu_mode = record.GetCpuMode();
+ perf_session = record.session;
+ attr = record.attr;
+
+ Reader reader(record.payload.copy());
+
+ std::optional<uint64_t> identifier;
+ if (sample_type & PERF_SAMPLE_IDENTIFIER) {
+ if (PERFETTO_UNLIKELY(!reader.ReadOptional(identifier))) {
+ return base ::ErrStatus("Not enough data to read PERF_SAMPLE_IDENTIFIER");
+ }
+ }
+
+ if (sample_type & PERF_SAMPLE_IP) {
+ if (PERFETTO_UNLIKELY(!reader.ReadOptional(ip))) {
+ return base ::ErrStatus("Not enough data to read PERF_SAMPLE_IP");
+ }
+ }
+
+ if (sample_type & PERF_SAMPLE_TID) {
+ if (PERFETTO_UNLIKELY(!reader.ReadOptional(pid_tid))) {
+ return base ::ErrStatus("Not enough data to read PERF_SAMPLE_TID");
+ }
+ }
+
+ if (sample_type & PERF_SAMPLE_TIME) {
+ if (PERFETTO_UNLIKELY(!reader.ReadOptional(time))) {
+ return base ::ErrStatus("Not enough data to read PERF_SAMPLE_TIME");
+ }
+ }
+
+ if (sample_type & PERF_SAMPLE_ADDR) {
+ if (PERFETTO_UNLIKELY(!reader.ReadOptional(addr))) {
+ return base ::ErrStatus("Not enough data to read PERF_SAMPLE_ADDR");
+ }
+ }
+
+ if (sample_type & PERF_SAMPLE_ID) {
+ if (PERFETTO_UNLIKELY(!reader.ReadOptional(id))) {
+ return base ::ErrStatus("Not enough data to read PERF_SAMPLE_ID");
+ }
+ }
+
+ if (identifier.has_value()) {
+ if (!id.has_value()) {
+ id = identifier;
+ } else if (PERFETTO_UNLIKELY(*identifier != *id)) {
+ return base::ErrStatus("ID and IDENTIFIER mismatch");
+ }
+ }
+
+ if (sample_type & PERF_SAMPLE_STREAM_ID) {
+ if (PERFETTO_UNLIKELY(!reader.ReadOptional(stream_id))) {
+ return base ::ErrStatus("Not enough data to read PERF_SAMPLE_STREAM_ID");
+ }
+ }
+
+ if (sample_type & PERF_SAMPLE_CPU) {
+ struct {
+ int32_t cpu;
+ int32_t unused;
+ } tmp;
+ if (PERFETTO_UNLIKELY(!reader.Read(tmp))) {
+ return base ::ErrStatus("Not enough data to read PERF_SAMPLE_CPU");
+ }
+ cpu = tmp.cpu;
+ }
+
+ if (sample_type & PERF_SAMPLE_PERIOD) {
+ if (PERFETTO_UNLIKELY(!reader.ReadOptional(period))) {
+ return base ::ErrStatus("Not enough data to read PERF_SAMPLE_PERIOD");
+ }
+ }
+
+ if (sample_type & PERF_SAMPLE_READ) {
+ if (PERFETTO_UNLIKELY(
+ !ParseSampleRead(reader, attr->read_format(), read_groups))) {
+ return base::ErrStatus("Failed to read PERF_SAMPLE_READ field");
+ }
+ if (read_groups.empty()) {
+ return base::ErrStatus("No data in PERF_SAMPLE_READ field");
+ }
+ }
+
+ if (sample_type & PERF_SAMPLE_CALLCHAIN) {
+ if (PERFETTO_UNLIKELY(!ParseSampleCallchain(reader, cpu_mode, callchain))) {
+ return base::ErrStatus("Failed to read PERF_SAMPLE_CALLCHAIN field");
+ }
+ }
+
+ return base::OkStatus();
+}
+
+} // namespace perfetto::trace_processor::perf_importer
diff --git a/src/trace_processor/importers/perf/sample.h b/src/trace_processor/importers/perf/sample.h
new file mode 100644
index 0000000..532b613
--- /dev/null
+++ b/src/trace_processor/importers/perf/sample.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PERF_SAMPLE_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PERF_SAMPLE_H_
+
+#include <cstdint>
+#include <optional>
+#include <vector>
+
+#include "perfetto/base/status.h"
+#include "perfetto/trace_processor/ref_counted.h"
+#include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
+#include "src/trace_processor/importers/perf/perf_event_attr.h"
+#include "src/trace_processor/importers/perf/perf_session.h"
+
+namespace perfetto::trace_processor::perf_importer {
+
+struct Record;
+
+struct Sample {
+ struct Frame {
+ protos::pbzero::Profiling::CpuMode cpu_mode;
+ uint64_t ip;
+ };
+
+ struct PidTid {
+ uint32_t pid;
+ uint32_t tid;
+ };
+
+ struct ReadGroup {
+ std::optional<uint64_t> event_id;
+ uint64_t value;
+ };
+
+ int64_t trace_ts;
+ protos::pbzero::Profiling::CpuMode cpu_mode;
+ RefPtr<PerfSession> perf_session;
+ RefPtr<const PerfEventAttr> attr;
+
+ std::optional<uint64_t> ip;
+ std::optional<PidTid> pid_tid;
+ std::optional<uint64_t> time;
+ std::optional<uint64_t> addr;
+ std::optional<uint64_t> id;
+ std::optional<uint64_t> stream_id;
+ std::optional<uint32_t> cpu;
+ std::optional<uint64_t> period;
+ std::vector<ReadGroup> read_groups;
+ std::vector<Frame> callchain;
+
+ base::Status Parse(int64_t trace_ts, const Record& record);
+};
+
+} // namespace perfetto::trace_processor::perf_importer
+
+#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PERF_SAMPLE_H_
diff --git a/src/trace_processor/sqlite/module_lifecycle_manager.h b/src/trace_processor/sqlite/module_lifecycle_manager.h
index 66054d5..f1f93e8 100644
--- a/src/trace_processor/sqlite/module_lifecycle_manager.h
+++ b/src/trace_processor/sqlite/module_lifecycle_manager.h
@@ -67,6 +67,11 @@
public:
// Per-vtab state. The pointer to this class should be stored in the Vtab.
struct PerVtabState {
+ private:
+ // The below fields should only be accessed by the manager, use GetState to
+ // access the state from outside this class.
+ friend class ModuleStateManager<Module>;
+
ModuleStateManager* manager;
bool disconnected = false;
std::string table_name;
diff --git a/src/trace_processor/storage/stats.h b/src/trace_processor/storage/stats.h
index 493561b..52f0c20 100644
--- a/src/trace_processor/storage/stats.h
+++ b/src/trace_processor/storage/stats.h
@@ -262,9 +262,13 @@
F(perf_process_shard_count, kIndexed, kInfo, kTrace, ""), \
F(perf_chosen_process_shard, kIndexed, kInfo, kTrace, ""), \
F(perf_guardrail_stop_ts, kIndexed, kDataLoss, kTrace, ""), \
- F(perf_record_skipped, kSingle, kInfo, kTrace, ""), \
- F(perf_samples_skipped, kSingle, kInfo, kTrace, ""), \
+ F(perf_unknown_record_type, kIndexed, kInfo, kAnalysis, ""), \
+ F(perf_record_skipped, kSingle, kError, kAnalysis, ""), \
+ F(perf_samples_skipped, kSingle, kError, kAnalysis, ""), \
+ F(perf_features_skipped, kIndexed, kInfo, kAnalysis, ""), \
F(perf_samples_skipped_dataloss, kSingle, kDataLoss, kTrace, ""), \
+ F(perf_dummy_mapping_used, kSingle, kInfo, kAnalysis, ""), \
+ F(perf_invalid_event_id, kSingle, kError, kTrace, ""), \
F(memory_snapshot_parser_failure, kSingle, kError, kAnalysis, ""), \
F(thread_time_in_state_out_of_order, kSingle, kError, kAnalysis, ""), \
F(thread_time_in_state_unknown_cpu_freq, \