From a6833d5d85388accd9d6fb0966886bc735aa12e9 Mon Sep 17 00:00:00 2001 From: xliuprof Date: Tue, 21 Oct 2025 09:38:58 -0700 Subject: [PATCH 1/4] Update the profile.proto to include some cosmetic changes. PiperOrigin-RevId: 822155822 --- src/profile.proto | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/profile.proto b/src/profile.proto index 81050c2..c652118 100644 --- a/src/profile.proto +++ b/src/profile.proto @@ -4,7 +4,7 @@ // 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 +// 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, @@ -93,11 +93,11 @@ message Profile { // Index into the string table of the type of the preferred sample // value. If unset, clients should default to the last sample value. int64 default_sample_type = 14; - // Documentation link for this profile. The URL must be absolute, + // Documentation link for this profile type. The URL must be absolute, // e.g., http://pprof.example.com/cpu-profile.html // - // The URL may be missing if the profile was generated by older code or code - // that did not bother to supply a link. + // The URL may be missing if the profile was generated by code that did not + // supply a link. int64 doc_url = 15; // Index into string table. } From ca499bdb629988cffb368f3743dc46026399eb25 Mon Sep 17 00:00:00 2001 From: blakejones Date: Fri, 31 Oct 2025 16:11:54 -0700 Subject: [PATCH 2/4] Add quipper support for PERF_RECORD_BPF_METADATA. PiperOrigin-RevId: 826663971 --- src/quipper/BUILD | 4 ++- src/quipper/kernel/perf_internals.h | 37 +++++++++++++++----- src/quipper/perf_data.proto | 21 +++++++++-- src/quipper/perf_data_utils.cc | 25 ++++++++++++- src/quipper/perf_parser.cc | 8 +++++ src/quipper/perf_parser_test.cc | 53 ++++++++++++++++++++++++++++ src/quipper/perf_reader.cc | 7 ++++ src/quipper/perf_serializer.cc | 54 ++++++++++++++++++++++++++++- src/quipper/perf_serializer.h | 7 ++++ src/quipper/perf_serializer_test.cc | 52 +++++++++++++++++++++++++++ src/quipper/test_perf_data.cc | 33 ++++++++++++++++++ src/quipper/test_perf_data.h | 17 +++++++++ 12 files changed, 305 insertions(+), 13 deletions(-) diff --git a/src/quipper/BUILD b/src/quipper/BUILD index 69e4e05..65e3d03 100644 --- a/src/quipper/BUILD +++ b/src/quipper/BUILD @@ -356,8 +356,8 @@ cc_library( visibility = ["//visibility:public"], deps = [ ":binary_data_utils", - ":compat", ":kernel", + ":perf_data_cc_proto", ":base", ], ) @@ -724,12 +724,14 @@ cc_test( "not_run:arm", ], deps = [ + ":binary_data_utils", ":compat", ":compat_gunit", ":file_utils", ":kernel", ":perf_buildid", ":perf_data_utils", + ":perf_parser", ":perf_protobuf_io", ":perf_reader", ":perf_serializer", diff --git a/src/quipper/kernel/perf_internals.h b/src/quipper/kernel/perf_internals.h index 33c8e7e..00941e8 100644 --- a/src/quipper/kernel/perf_internals.h +++ b/src/quipper/kernel/perf_internals.h @@ -71,7 +71,7 @@ struct perf_file_attr { }; #define BITS_PER_BYTE 8 -#define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d)) +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) #define BITS_TO_LONGS(nr) \ DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) @@ -177,6 +177,22 @@ struct ksymbol_event { char name[kMaxKsymNameLen]; }; +const u16 kMaxBPFMetadataKeyLen = 64; +const u16 kMaxBPFMetadataValueLen = 256; +const u16 kMaxBPFProgNameLen = kMaxKsymNameLen; + +struct bpf_metadata_entry { + char key[kMaxBPFMetadataKeyLen]; + char value[kMaxBPFMetadataValueLen]; +}; + +struct bpf_metadata_event { + struct perf_event_header header; + char prog_name[kMaxBPFProgNameLen]; + u64 nr_entries; + struct bpf_metadata_entry entries[]; +}; + struct fork_event { struct perf_event_header header; u32 pid, ppid; @@ -248,7 +264,7 @@ struct regs_dump { struct stack_dump { u16 offset; u64 size; - char *data; + char* data; }; struct sample_read_value { @@ -263,7 +279,7 @@ struct sample_read { struct { struct { u64 nr; - struct sample_read_value *values; + struct sample_read_value* values; } group; struct sample_read_value one; }; @@ -330,9 +346,9 @@ struct perf_sample { u32 flags; u16 insn_len; bool no_hw_idx; /* No hw_idx collected in branch_stack */ - void *raw_data; - struct ip_callchain *callchain; - struct branch_stack *branch_stack; + void* raw_data; + struct ip_callchain* callchain; + struct branch_stack* branch_stack; // struct regs_dump user_regs; // See struct regs_dump above. struct stack_dump user_stack; struct sample_read read; @@ -371,7 +387,7 @@ struct perf_sample { ~perf_sample() { delete[] callchain; delete[] branch_stack; - delete[] reinterpret_cast(raw_data); + delete[] reinterpret_cast(raw_data); } }; @@ -416,7 +432,11 @@ enum perf_user_event_type { PERF_RECORD_EVENT_UPDATE = 78, PERF_RECORD_TIME_CONV = 79, PERF_RECORD_HEADER_FEATURE = 80, - PERF_RECORD_HEADER_MAX = 81, + PERF_RECORD_COMPRESSED = 81, + PERF_RECORD_FINISHED_INIT = 82, + PERF_RECORD_COMPRESSED2 = 83, + PERF_RECORD_BPF_METADATA = 84, + PERF_RECORD_HEADER_MAX = 85, }; enum auxtrace_error_type { @@ -617,6 +637,7 @@ union perf_event { struct namespaces_event namespaces; struct cgroup_event cgroup; struct ksymbol_event ksymbol; + struct bpf_metadata_event bpf_metadata; struct fork_event fork; struct lost_event lost; struct lost_samples_event lost_samples; diff --git a/src/quipper/perf_data.proto b/src/quipper/perf_data.proto index 88a05ed..5036407 100644 --- a/src/quipper/perf_data.proto +++ b/src/quipper/perf_data.proto @@ -18,7 +18,7 @@ message PerfDataProto { // Perf event attribute. Stores the event description. // This data structure is defined in the linux kernel: // $kernel/include/uapi/linux/perf_event.h. - // Next tag: 48 + // Next tag: 49 message PerfEventAttr { // Type of the event. Type is an enumeration and can be one of the values // described at: $kernel/include/linux/perf_event.h. @@ -142,6 +142,9 @@ message PerfDataProto { // Include ksymbol data. optional bool ksymbol = 47; + // Include BPF metadata. + optional bool bpf_metadata = 48; + // Contains the number of events after which we wake up. optional uint32 wakeup_events = 28; @@ -325,6 +328,19 @@ message PerfDataProto { optional SampleInfo sample_info = 6; } + // Next tag: 4 + message BpfMetadataEvent { + // The name of the BPF program. + optional string prog_name = 1; + + // The BPF program's metadata. The map key is the type of metadata, + // and the value is the metadata value. + map metadata = 2; + + // Info about the perf sample containing this event. + optional SampleInfo sample_info = 3; + } + // Next tag: 4 message ReadInfo { optional uint64 time_enabled = 1; @@ -846,7 +862,7 @@ message PerfDataProto { optional uint32 size = 3; } - // Next tag: 27 + // Next tag: 28 message PerfEvent { optional EventHeader header = 1; oneof event_type { @@ -878,6 +894,7 @@ message PerfDataProto { StatRoundEvent stat_round_event = 23; CgroupEvent cgroup_event = 24; KsymbolEvent ksymbol_event = 25; + BpfMetadataEvent bpf_metadata_event = 27; } // Time after boot in nanoseconds corresponding to the event. optional uint64 timestamp = 10; diff --git a/src/quipper/perf_data_utils.cc b/src/quipper/perf_data_utils.cc index cb26b01..4bdbeac 100644 --- a/src/quipper/perf_data_utils.cc +++ b/src/quipper/perf_data_utils.cc @@ -7,12 +7,14 @@ #include #include #include +#include #include #include "base/logging.h" -#include "compat/proto.h" #include "kernel/perf_event.h" #include "kernel/perf_internals.h" +#include "src/quipper/perf_data.pb.h" + namespace quipper { namespace { @@ -282,6 +284,8 @@ std::string GetEventName(uint32_t type) { return "PERF_RECORD_CGROUP"; case PERF_RECORD_KSYMBOL: return "PERF_RECORD_KSYMBOL"; + case PERF_RECORD_BPF_METADATA: + return "PERF_RECORD_BPF_METADATA"; } return "UNKNOWN_EVENT_" + std::to_string(type); } @@ -365,6 +369,9 @@ bool GetEventDataFixedPayloadSize(uint32_t type, size_t* size) { case PERF_RECORD_KSYMBOL: *size = offsetof(struct ksymbol_event, name); return true; + case PERF_RECORD_BPF_METADATA: + *size = offsetof(struct bpf_metadata_event, entries); + return true; default: LOG(ERROR) << "Unsupported event " << GetEventName(type); } @@ -539,6 +546,18 @@ bool GetEventDataVariablePayloadSize(const event_t& event, } break; } + case PERF_RECORD_BPF_METADATA: { + size_t actual_entries = event.bpf_metadata.nr_entries; + if (actual_entries * sizeof(struct bpf_metadata_entry) > + remaining_event_size) { + LOG(ERROR) << "Total bpf metadata entries size " << *size + << " extends beyond the remaining event size " + << remaining_event_size; + return false; + } + *size = remaining_event_size; + break; + } // The below events changed size between kernel versions, // so ensure the variable payload size completely exhausts // the remaining_event_size. @@ -625,6 +644,10 @@ bool GetEventDataVariablePayloadSize(const PerfDataProto_PerfEvent& event, case PERF_RECORD_KSYMBOL: *size = GetUint64AlignedStringLength(event.ksymbol_event().name().size()); break; + case PERF_RECORD_BPF_METADATA: + *size = event.bpf_metadata_event().metadata_size() * + sizeof(struct bpf_metadata_entry); + break; // The below event gained new fields in new kernel versions. Return the size // difference if any of the new fields are present. case PERF_RECORD_TIME_CONV: diff --git a/src/quipper/perf_parser.cc b/src/quipper/perf_parser.cc index 4a06a53..659a751 100644 --- a/src/quipper/perf_parser.cc +++ b/src/quipper/perf_parser.cc @@ -12,11 +12,18 @@ #include #include +#include #include +#include +#include +#include #include #include #include +#include +#include #include +#include #include "base/logging.h" #include "address_mapper.h" @@ -140,6 +147,7 @@ bool PerfParser::ProcessUserEvents(PerfEvent& event) { case PERF_RECORD_STAT: case PERF_RECORD_STAT_ROUND: case PERF_RECORD_TIME_CONV: + case PERF_RECORD_BPF_METADATA: VLOG(1) << "Parsed event: " << GetEventName(event.header().type()) << ". Doing nothing."; break; diff --git a/src/quipper/perf_parser_test.cc b/src/quipper/perf_parser_test.cc index 9efc95b..b2bc4ba 100644 --- a/src/quipper/perf_parser_test.cc +++ b/src/quipper/perf_parser_test.cc @@ -23,6 +23,7 @@ #include "compat/thread.h" #include "dso_test_utils.h" #include "kernel/perf_event.h" +#include "kernel/perf_internals.h" #include "perf_buildid.h" #include "perf_data_utils.h" #include "perf_reader.h" @@ -1894,6 +1895,58 @@ TEST(PerfParserTest, PipedTimeConvEventsLarge) { events[0].event_ptr->time_conv_event().cap_user_time_short()); } +TEST(PerfParserTest, BpfMetadataEvents) { + std::stringstream input; + + // PERF_RECORD_BPF_METADATA + testing::ExampleBpfMetadataEvent event( + "test_prog", + { + {.key = "my_key", .value = "my_value"}, + {.key = "longer_key", .value = "slightly_different_value"}, + }); + + size_t data_size = event.GetSize(); + + // header + testing::ExamplePerfDataFileHeader file_header(0); + file_header.WithAttrCount(1).WithDataSize(data_size).WriteTo(&input); + + // attrs + ASSERT_EQ(file_header.header().attrs.offset, static_cast(input.tellp())); + testing::ExamplePerfFileAttr_Hardware(PERF_SAMPLE_TID, /*sample_id_all=*/true) + .WriteTo(&input); + + // data + ASSERT_EQ(file_header.header().data.offset, static_cast(input.tellp())); + event.WriteTo(&input); + ASSERT_EQ(file_header.header().data.offset + data_size, + static_cast(input.tellp())); + + // + // Parse input. + // + PerfReader reader; + ASSERT_TRUE(reader.ReadFromString(input.str())); + + PerfParserOptions options; + options.sample_mapping_percentage_threshold = 0; + options.do_remap = true; + PerfParser parser(&reader, options); + EXPECT_TRUE(parser.ParseRawEvents()); + + const std::vector& events = parser.parsed_events(); + ASSERT_EQ(1, events.size()); + + EXPECT_EQ(PERF_RECORD_BPF_METADATA, events[0].event_ptr->header().type()); + const PerfDataProto::BpfMetadataEvent& parsed_event = + events[0].event_ptr->bpf_metadata_event(); + EXPECT_EQ("test_prog", parsed_event.prog_name()); + EXPECT_EQ("my_value", parsed_event.metadata().at("my_key")); + EXPECT_EQ("slightly_different_value", + parsed_event.metadata().at("longer_key")); +} + TEST(PerfParserTest, CgroupEvents) { std::stringstream input; diff --git a/src/quipper/perf_reader.cc b/src/quipper/perf_reader.cc index 1355c16..c245ea8 100644 --- a/src/quipper/perf_reader.cc +++ b/src/quipper/perf_reader.cc @@ -12,10 +12,13 @@ #include #include #include +#include #include #include +#include #include #include +#include #include #include "base/logging.h" @@ -258,6 +261,9 @@ bool ByteSwapEventDataFixedPayloadFields(event_t* event) { ByteSwap(&event->ksymbol.len); ByteSwap(&event->ksymbol.flags); return true; + case PERF_RECORD_BPF_METADATA: + ByteSwap(&event->bpf_metadata.nr_entries); + return true; case PERF_RECORD_FORK: case PERF_RECORD_EXIT: ByteSwap(&event->fork.pid); @@ -412,6 +418,7 @@ bool ByteSwapEventDataVariablePayloadFields(event_t* event) { case PERF_RECORD_MMAP: case PERF_RECORD_MMAP2: case PERF_RECORD_KSYMBOL: + case PERF_RECORD_BPF_METADATA: case PERF_RECORD_FORK: case PERF_RECORD_EXIT: case PERF_RECORD_COMM: diff --git a/src/quipper/perf_serializer.cc b/src/quipper/perf_serializer.cc index 1136385..94f635a 100644 --- a/src/quipper/perf_serializer.cc +++ b/src/quipper/perf_serializer.cc @@ -8,7 +8,13 @@ #include #include -#include // for std::copy +#include +#include +#include +#include +#include +#include +#include #include "base/logging.h" #include "binary_data_utils.h" @@ -70,6 +76,7 @@ bool PerfSerializer::IsSupportedUserEventType(uint32_t type) { case PERF_RECORD_STAT: case PERF_RECORD_STAT_ROUND: case PERF_RECORD_TIME_CONV: + case PERF_RECORD_BPF_METADATA: return true; } return false; @@ -407,6 +414,9 @@ bool PerfSerializer::SerializeUserEvent( case PERF_RECORD_TIME_CONV: return SerializeTimeConvEvent(event, event_proto->mutable_time_conv_event()); + case PERF_RECORD_BPF_METADATA: + return SerializeBpfMetadataEvent( + event, event_proto->mutable_bpf_metadata_event()); default: LOG(ERROR) << "Unsupported event " << GetEventName(event.header.type); } @@ -522,6 +532,15 @@ bool PerfSerializer::DeserializeUserEvent( return DeserializeStatRoundEvent(event_proto.stat_round_event(), event); case PERF_RECORD_TIME_CONV: return DeserializeTimeConvEvent(event_proto.time_conv_event(), event); + case PERF_RECORD_BPF_METADATA: + return DeserializeBpfMetadataEvent(event_proto.bpf_metadata_event(), + event); + case PERF_RECORD_COMPRESSED: + case PERF_RECORD_FINISHED_INIT: + case PERF_RECORD_COMPRESSED2: + // These were propagated from tools/lib/perf/include/perf/event.h + // to quipper/kernel/perf_internals.h, but are not currently supported. + return false; default: // User type events are marked as deserialized because they don't // have non-header data in perf.data proto. @@ -998,6 +1017,39 @@ bool PerfSerializer::DeserializeKsymbolEvent( return DeserializeSampleInfo(sample.sample_info(), event); } +bool PerfSerializer::SerializeBpfMetadataEvent( + const event_t& event, PerfDataProto_BpfMetadataEvent* sample) const { + const struct bpf_metadata_event& bpf_metadata = event.bpf_metadata; + char prog_name[kMaxBPFProgNameLen]; + snprintf(prog_name, kMaxBPFProgNameLen, "%s", bpf_metadata.prog_name); + sample->set_prog_name(prog_name); + for (u64 i = 0; i < bpf_metadata.nr_entries; ++i) { + char key[kMaxBPFMetadataKeyLen], value[kMaxBPFMetadataValueLen]; + snprintf(key, kMaxBPFMetadataKeyLen, "%s", bpf_metadata.entries[i].key); + snprintf(value, kMaxBPFMetadataValueLen, "%s", + bpf_metadata.entries[i].value); + sample->mutable_metadata()->insert({key, value}); + } + return SerializeSampleInfo(event, sample->mutable_sample_info()); +} + +bool PerfSerializer::DeserializeBpfMetadataEvent( + const PerfDataProto_BpfMetadataEvent& sample, event_t* event) const { + struct bpf_metadata_event& bpf_metadata = event->bpf_metadata; + snprintf(bpf_metadata.prog_name, kMaxBPFProgNameLen, "%s", + sample.prog_name().c_str()); + bpf_metadata.nr_entries = sample.metadata_size(); + u32 nr_entries = 0; + for (const auto& [key, value] : sample.metadata()) { + snprintf(bpf_metadata.entries[nr_entries].key, kMaxBPFMetadataKeyLen, "%s", + key.c_str()); + snprintf(bpf_metadata.entries[nr_entries].value, kMaxBPFMetadataValueLen, + "%s", value.c_str()); + nr_entries++; + } + return DeserializeSampleInfo(sample.sample_info(), event); +} + bool PerfSerializer::SerializeSampleInfo( const event_t& event, PerfDataProto_SampleInfo* sample) const { if (!ContainsSampleInfo(event.header.type)) return true; diff --git a/src/quipper/perf_serializer.h b/src/quipper/perf_serializer.h index 3195051..2ccab66 100644 --- a/src/quipper/perf_serializer.h +++ b/src/quipper/perf_serializer.h @@ -8,11 +8,14 @@ #include #include +#include #include #include #include #include "compat/proto.h" +#include "kernel/perf_event.h" +#include "kernel/perf_internals.h" #include "perf_data_utils.h" struct perf_event_attr; @@ -213,6 +216,10 @@ class PerfSerializer { PerfDataProto_KsymbolEvent* sample) const; bool DeserializeKsymbolEvent(const PerfDataProto_KsymbolEvent& sample, event_t* event) const; + bool SerializeBpfMetadataEvent(const event_t& event, + PerfDataProto_BpfMetadataEvent* sample) const; + bool DeserializeBpfMetadataEvent(const PerfDataProto_BpfMetadataEvent& sample, + event_t* event) const; bool SerializeSingleUint32Metadata( const PerfUint32Metadata& metadata, diff --git a/src/quipper/perf_serializer_test.cc b/src/quipper/perf_serializer_test.cc index 66b9860..4caf512 100644 --- a/src/quipper/perf_serializer_test.cc +++ b/src/quipper/perf_serializer_test.cc @@ -6,12 +6,15 @@ #include +#include #include #include #include #include +#include #include "base/logging.h" +#include "binary_data_utils.h" #include "compat/proto.h" #include "compat/test.h" #include "file_utils.h" @@ -20,6 +23,7 @@ #include "perf_buildid.h" #include "perf_data_structures.h" #include "perf_data_utils.h" +#include "perf_parser.h" #include "perf_protobuf_io.h" #include "perf_reader.h" #include "perf_test_files.h" @@ -1852,6 +1856,54 @@ TEST(PerfSerializerTest, SerializesAndDeserializesIdIndexEvents) { } } +TEST(PerfSerializerTest, SerializesAndDeserializesBpfEvents) { + std::stringstream input; + + testing::ExamplePipedPerfDataFileHeader().WriteTo(&input); + testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_TID, + true /*sample_id_all*/) + .WriteTo(&input); + testing::ExampleBpfMetadataEvent( + "test_prog", + { + {.key = "my_key", .value = "my_value"}, + {.key = "longer_key", .value = "slightly_different_value"}, + }) + .WriteTo(&input); + + // Serialize. + PerfReader reader; + ASSERT_TRUE(reader.ReadFromString(input.str())); + PerfDataProto perf_data_proto; + ASSERT_TRUE(reader.Serialize(&perf_data_proto)); + EXPECT_EQ(1, perf_data_proto.events().size()); + { + const PerfDataProto::PerfEvent& perf_event = perf_data_proto.events(0); + EXPECT_EQ(PERF_RECORD_BPF_METADATA, perf_event.header().type()); + EXPECT_TRUE(perf_event.has_bpf_metadata_event()); + const PerfDataProto::BpfMetadataEvent& event = + perf_event.bpf_metadata_event(); + EXPECT_EQ("test_prog", event.prog_name()); + EXPECT_EQ("my_value", event.metadata().at("my_key")); + EXPECT_EQ("slightly_different_value", event.metadata().at("longer_key")); + } + + // Deserialize. + PerfReader out_reader; + ASSERT_TRUE(out_reader.Deserialize(perf_data_proto)); + EXPECT_EQ(1, out_reader.events().size()); + { + const PerfEvent& perf_event = out_reader.events().Get(0); + EXPECT_EQ(PERF_RECORD_BPF_METADATA, perf_event.header().type()); + EXPECT_TRUE(perf_event.has_bpf_metadata_event()); + const PerfDataProto::BpfMetadataEvent& event = + perf_event.bpf_metadata_event(); + EXPECT_EQ("test_prog", event.prog_name()); + EXPECT_EQ("my_value", event.metadata().at("my_key")); + EXPECT_EQ("slightly_different_value", event.metadata().at("longer_key")); + } +} + namespace { std::vector AllPerfData() { const auto& files = perf_test_files::GetPerfDataFiles(); diff --git a/src/quipper/test_perf_data.cc b/src/quipper/test_perf_data.cc index fb81b41..248a4a1 100644 --- a/src/quipper/test_perf_data.cc +++ b/src/quipper/test_perf_data.cc @@ -7,7 +7,12 @@ #include #include +#include +#include +#include +#include #include +#include #include #include "base/logging.h" @@ -933,5 +938,33 @@ void ExampleKsymbolEvent::WriteTo(std::ostream* out) const { CHECK_EQ(event.header.size, static_cast(written_event_size)); } +size_t ExampleBpfMetadataEvent::GetSize() const { + return offsetof(struct bpf_metadata_event, entries) + + entries_.size() * sizeof(struct bpf_metadata_entry); +} + +void ExampleBpfMetadataEvent::WriteTo(std::ostream* out) const { + const size_t event_size = GetSize(); + malloced_unique_ptr event( + reinterpret_cast(calloc(1, event_size))); + event->header.type = MaybeSwap32(PERF_RECORD_BPF_METADATA); + event->header.misc = 0; + event->header.size = MaybeSwap16(static_cast(event_size)); + + snprintf(event->prog_name, sizeof(event->prog_name), "%s", + prog_name_.c_str()); + event->nr_entries = MaybeSwap64(entries_.size()); + + for (u64 i = 0; i < entries_.size(); ++i) { + memcpy(&event->entries[i], &entries_[i], sizeof(event->entries[i])); + } + + const size_t previous_offset = out->tellp(); + out->write(reinterpret_cast(event.get()), event_size); + const size_t written_event_size = + static_cast(out->tellp()) - previous_offset; + CHECK_EQ(event_size, static_cast(written_event_size)); +} + } // namespace testing } // namespace quipper diff --git a/src/quipper/test_perf_data.h b/src/quipper/test_perf_data.h index 617c523..2561a0a 100644 --- a/src/quipper/test_perf_data.h +++ b/src/quipper/test_perf_data.h @@ -7,13 +7,16 @@ #include +#include #include +#include #include #include #include #include #include "binary_data_utils.h" +#include "kernel/perf_event.h" #include "kernel/perf_internals.h" namespace quipper { @@ -951,6 +954,20 @@ class ExampleKsymbolEvent : public StreamWriteable { const SampleInfo sample_id_; }; +// Produces PERF_RECORD_BPF_METADATA event. +class ExampleBpfMetadataEvent : public StreamWriteable { + public: + ExampleBpfMetadataEvent(std::string prog_name, + std::vector entries) + : prog_name_(prog_name), entries_(std::move(entries)) {} + size_t GetSize() const; + void WriteTo(std::ostream* out) const override; + + private: + const std::string prog_name_; + const std::vector entries_; +}; + } // namespace testing } // namespace quipper From 7798aebf7e1155e0e39a5db25773deebad7dd511 Mon Sep 17 00:00:00 2001 From: blakejones Date: Mon, 3 Nov 2025 18:46:04 -0800 Subject: [PATCH 3/4] Internal changes. PiperOrigin-RevId: 827733626 --- src/perf_data_converter.cc | 65 +++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/src/perf_data_converter.cc b/src/perf_data_converter.cc index c1368e8..3f71926 100644 --- a/src/perf_data_converter.cc +++ b/src/perf_data_converter.cc @@ -165,6 +165,9 @@ typedef std::unordered_map LocationMap; +// Map from a function name and source file to a profile function ID. +typedef std::map, uint64_t> FunctionMap; + // Map from the handler mapping object to profile mapping ID. The mappings // the handler creates are immutable and reasonably shared (as in no new mapping // object is created per, say, each sample), so using the pointers is OK. @@ -246,12 +249,23 @@ class PerfDataConverter : public PerfDataHandler { const PerfDataHandler::Mapping* mapping, ProfileBuilder* builder); + // Adds a new function to the profile if such function is not present in the + // profile, returning the ID of the function. + uint64_t AddOrGetFunction(const Pid& pid, const std::string& name, + const std::string& source_file, + ProfileBuilder* builder); + // Adds a new mapping to the profile if such mapping is not present in the // profile, returning the ID of the mapping. It returns 0 to indicate that the // mapping was not added (only happens if smap == 0 currently). uint64_t AddOrGetMapping(const Pid& pid, const PerfDataHandler::Mapping* smap, ProfileBuilder* builder); + // Sets the has_functions flag in the specified mapping. + void SetMappingHasFunctions(const Pid& pid, + const PerfDataHandler::Mapping* smap, + ProfileBuilder* builder); + // Returns whether pid labels were requested for inclusion in the // profile.proto's Sample.Label field. bool IncludePidLabels() const { return (sample_labels_ & kPidLabel); } @@ -339,6 +353,7 @@ class PerfDataConverter : public PerfDataHandler { ProfileBuilder* builder = nullptr; ProcessMeta* process_meta = nullptr; LocationMap location_map; + FunctionMap function_map; MappingMap mapping_map; std::unordered_map tid_to_comm_map; SampleMap sample_map; @@ -346,6 +361,7 @@ class PerfDataConverter : public PerfDataHandler { builder = nullptr; process_meta = nullptr; location_map.clear(); + function_map.clear(); mapping_map.clear(); tid_to_comm_map.clear(); sample_map.clear(); @@ -569,6 +585,21 @@ ProfileBuilder* PerfDataConverter::GetOrCreateBuilder( return per_pid.builder; } +void PerfDataConverter::SetMappingHasFunctions( + const Pid& pid, const PerfDataHandler::Mapping* smap, + ProfileBuilder* builder) { + CHECK(builder != nullptr) << "Cannot modify mapping in null builder"; + if (smap == nullptr) return; + + MappingMap& mapmap = per_pid_[pid].mapping_map; + auto it = mapmap.find(smap); + CHECK(it != mapmap.end()) << "Mapping not found: " << smap->filename; + + Profile* profile = builder->mutable_profile(); + uint64_t mapping_id = it->second - 1; // Mapping IDs start at 1. + profile->mutable_mapping(mapping_id)->set_has_functions(true); +} + uint64_t PerfDataConverter::AddOrGetMapping( const Pid& pid, const PerfDataHandler::Mapping* smap, ProfileBuilder* builder) { @@ -591,11 +622,13 @@ uint64_t PerfDataConverter::AddOrGetMapping( mapping->set_memory_start(smap->start); mapping->set_memory_limit(smap->limit); mapping->set_file_offset(smap->file_offset); - if (!smap->build_id.value.empty()) { - mapping->set_build_id(UTF8StringId(smap->build_id.value, builder)); - } - std::string mapping_filename = MappingFilename(smap); + std::string mapping_filename; + if (!smap->build_id.value.empty()) { + mapping->set_build_id(UTF8StringId(smap->build_id.value, builder)); + } + mapping_filename = MappingFilename(smap); mapping->set_filename(UTF8StringId(mapping_filename, builder)); + mapping->set_has_filenames(true); CHECK_LE(mapping->memory_start(), mapping->memory_limit()) << "Mapping start must be strictly less than its limit: " << mapping->filename(); @@ -746,6 +779,30 @@ void PerfDataConverter::AddOrUpdateSample( sample->value(2 * event_index + 1) + weight); } +uint64_t PerfDataConverter::AddOrGetFunction(const Pid& pid, + const std::string& name, + const std::string& source_file, + ProfileBuilder* builder) { + FunctionMap& func_map = per_pid_[pid].function_map; + const auto& key = std::make_pair(name, source_file); + auto func_it = func_map.find(key); + if (func_it != func_map.end()) { + return func_it->second; + } + + Profile* profile = builder->mutable_profile(); + perftools::profiles::Function* func = profile->add_function(); + uint64_t func_id = profile->function_size(); + func->set_id(func_id); + func->set_name(UTF8StringId(name, builder)); + func->set_system_name(UTF8StringId(name, builder)); + func->set_filename(UTF8StringId(source_file, builder)); + VLOG(2) << "Added func ID=" << func_id << ", name=" << name + << ", filename=" << source_file; + func_map[key] = func_id; + return func_id; +} + uint64_t PerfDataConverter::AddOrGetLocation( const Pid& pid, uint64_t addr, const PerfDataHandler::Mapping* mapping, ProfileBuilder* builder) { From 10da715463212b681707f733f705a38a5dc617eb Mon Sep 17 00:00:00 2001 From: blakejones Date: Tue, 4 Nov 2025 18:16:03 -0800 Subject: [PATCH 4/4] Clean up AddressMapper public interfaces. PiperOrigin-RevId: 828217426 --- src/quipper/BUILD | 4 +- src/quipper/address_mapper.cc | 84 ++++++++-------- src/quipper/address_mapper.h | 97 ++++++++---------- src/quipper/address_mapper_test.cc | 152 +++++++++++++++++------------ src/quipper/perf_parser.cc | 16 ++- 5 files changed, 179 insertions(+), 174 deletions(-) diff --git a/src/quipper/BUILD b/src/quipper/BUILD index 65e3d03..4e03ad3 100644 --- a/src/quipper/BUILD +++ b/src/quipper/BUILD @@ -244,7 +244,9 @@ cc_library( name = "address_mapper", srcs = ["address_mapper.cc"], hdrs = ["address_mapper.h"], - deps = [":base"], + deps = [ + ":base", + ], ) cc_library( diff --git a/src/quipper/address_mapper.cc b/src/quipper/address_mapper.cc index d90adff..f24d73c 100644 --- a/src/quipper/address_mapper.cc +++ b/src/quipper/address_mapper.cc @@ -4,9 +4,10 @@ #include "address_mapper.h" -#include - +#include +#include #include +#include #include #include "base/logging.h" @@ -24,17 +25,18 @@ AddressMapper::AddressMapper(const AddressMapper& other) { } } -bool AddressMapper::MapWithID(const uint64_t real_addr, const uint64_t size, - const uint64_t id, const uint64_t offset_base, - bool remove_existing_mappings, - bool allow_unaligned_jit_mappings) { +bool AddressMapper::MapWithIDInternal(uint64_t real_addr, uint64_t size, + uint64_t id, uint64_t offset_base, + bool remove_existing_mappings, + bool allow_unaligned_mappings) { if (size == 0) { LOG(ERROR) << "Must allocate a nonzero-length address range."; return false; } - if (allow_unaligned_jit_mappings && !remove_existing_mappings) { - LOG(ERROR) << "JIT events have to be able to overwite existing mappings."; + if (allow_unaligned_mappings && !remove_existing_mappings) { + LOG(ERROR) + << "Unaligned mappings have to be able to overwite existing mappings."; return false; } @@ -100,7 +102,7 @@ bool AddressMapper::MapWithID(const uint64_t real_addr, const uint64_t size, // or end, it will require the end of the old mapping range to be moved, // which is not allowed. Fake JIT events can map non-page-aligned addresses // for dynamically generated code. - if (!allow_unaligned_jit_mappings && page_alignment_) { + if (!allow_unaligned_mappings && page_alignment_) { uint64_t page_offset_start = GetAlignedOffset(range.real_addr); uint64_t page_offset_end = GetAlignedOffset(range.real_addr + range.size); if ((gap_before && page_offset_start) || (gap_after && page_offset_end)) { @@ -111,8 +113,8 @@ bool AddressMapper::MapWithID(const uint64_t real_addr, const uint64_t size, // Split the existing old range and insert the new range: if (gap_before) { - if (!MapWithID(old_range.real_addr, gap_before, old_range.id, - old_range.offset_base, false, false)) { + if (!MapWithIDInternal(old_range.real_addr, gap_before, old_range.id, + old_range.offset_base, false, false)) { LOG(ERROR) << "Could not map old range from " << std::hex << old_range.real_addr << " to " << old_range.real_addr + gap_before; @@ -120,17 +122,17 @@ bool AddressMapper::MapWithID(const uint64_t real_addr, const uint64_t size, } } - if (!MapWithID(range.real_addr, range.size, id, offset_base, false, - false)) { + if (!MapWithIDInternal(range.real_addr, range.size, id, offset_base, false, + false)) { LOG(ERROR) << "Could not map new range at " << std::hex << range.real_addr << " to " << range.real_addr + range.size << " over old range"; return false; } if (gap_after) { - if (!MapWithID(range.real_addr + range.size, gap_after, old_range.id, - old_range.offset_base + gap_before + range.size, false, - false)) { + if (!MapWithIDInternal( + range.real_addr + range.size, gap_after, old_range.id, + old_range.offset_base + gap_before + range.size, false, false)) { LOG(ERROR) << "Could not map old range from " << std::hex << old_range.real_addr << " to " << old_range.real_addr + gap_before; @@ -256,39 +258,31 @@ void AddressMapper::DumpToLog() const { } } -bool AddressMapper::GetMappedAddressAndListIterator( - const uint64_t real_addr, uint64_t* mapped_addr, - MappingList::const_iterator* iter) const { - CHECK(mapped_addr); - CHECK(iter); +// Looks up |real_addr| and returns the mapped address. +bool AddressMapper::GetMappedAddress(uint64_t real_addr, + uint64_t* mapped_addr) const { + AddressMapper::MappingList::const_iterator iter = + GetRangeContainingAddress(real_addr); + if (iter == mappings_.end()) return false; - *iter = GetRangeContainingAddress(real_addr); - if (*iter == mappings_.end()) return false; - *mapped_addr = (*iter)->mapped_addr + real_addr - (*iter)->real_addr; + *mapped_addr = iter->mapped_addr + real_addr - iter->real_addr; return true; } -void AddressMapper::GetMappedIDAndOffset( - const uint64_t real_addr, MappingList::const_iterator real_addr_iter, - uint64_t* id, uint64_t* offset) const { - CHECK(real_addr_iter != mappings_.end()); - CHECK(id); - CHECK(offset); - - *id = real_addr_iter->id; - *offset = real_addr - real_addr_iter->real_addr + real_addr_iter->offset_base; -} - -uint64_t AddressMapper::GetMaxMappedLength() const { - if (IsEmpty()) return 0; - - uint64_t min = mappings_.begin()->mapped_addr; - - MappingList::const_iterator iter = mappings_.end(); - --iter; - uint64_t max = iter->mapped_addr + iter->size; - - return max - min; +// Looks up |real_addr| and returns the mapping's ID and offset from the +// start of the mapped space. +bool AddressMapper::GetMappedAddressIDAndOffset(uint64_t real_addr, + uint64_t* mapped_addr, + uint64_t* id, + uint64_t* offset) const { + AddressMapper::MappingList::const_iterator iter = + GetRangeContainingAddress(real_addr); + if (iter == mappings_.end()) return false; + + *mapped_addr = real_addr - iter->real_addr + iter->mapped_addr; + *id = iter->id; + *offset = real_addr - iter->real_addr + iter->offset_base; + return true; } void AddressMapper::Unmap(MappingList::iterator mapping_iter) { diff --git a/src/quipper/address_mapper.h b/src/quipper/address_mapper.h index ac5cfe7..ec4b689 100644 --- a/src/quipper/address_mapper.h +++ b/src/quipper/address_mapper.h @@ -11,85 +11,57 @@ #include #include +#include "base/logging.h" + namespace quipper { class AddressMapper { - private: - struct MappedRange; - public: - AddressMapper() : page_alignment_(0) {} + // The "alignment" argument sets the page alignment size. + // It must be either zero or a power of 2. + explicit AddressMapper(uint64_t alignment = 0) : page_alignment_(alignment) { + CHECK(alignment == 0 || (alignment & (alignment - 1)) == 0) + << "Alignment must be 0 or a power of 2."; + } - // Copy constructor: copies mappings from |source| to this AddressMapper. This - // is useful for copying mappings from parent to child process upon fork(). It - // is also useful to copy kernel mappings to any process that is created. AddressMapper(const AddressMapper& other); - typedef std::list MappingList; - // Maps a new address range [real_addr, real_addr + length) to quipper space. + // // |id| is an identifier value to be stored along with the mapping. // AddressMapper does not care whether it is unique compared to all other IDs // passed in. That is up to the caller to keep track of. + // // |offset_base| represents the offset within the original region at which the // mapping begins. The original region can be much larger than the mapped // region. - // e.g. Given a mapped region with base/real_addr=0x4000 and size=0x2000 + // e.g. Given a mapped region with real_addr=0x4000 and size=0x2000 // mapped with offset_base=0x10000, then the address 0x5000 maps to an offset // of 0x11000 = (0x5000 - 0x4000 + 0x10000). - // |remove_existing_mappings| indicates whether to remove old mappings that - // collide with the new range in real address space, indicating it has been - // unmapped. - // |allow_unaligned_split_mappings| indicates whether the newly mapped address - // originates from a dynamically created code object created by a JIT. These - // mapped ranges are not necessary page-aligned since they typically - // correspond to single small functions. - // Returns true if mapping was successful. - bool MapWithID(const uint64_t real_addr, const uint64_t size, - const uint64_t id, const uint64_t offset_base, - bool remove_existing_mappings, - bool allow_unaligned_jit_mappings); - - // Looks up |real_addr| and returns the mapped address and MappingList - // iterator. - bool GetMappedAddressAndListIterator(const uint64_t real_addr, - uint64_t* mapped_addr, - MappingList::const_iterator* iter) const; - - // Uses MappingList iterator to fetch and return the mapping's ID and offset - // from the start of the mapped space. - // |real_addr_iter| must be valid and not at the end of the list. - void GetMappedIDAndOffset(const uint64_t real_addr, - MappingList::const_iterator real_addr_iter, - uint64_t* id, uint64_t* offset) const; + // + // If |allow_unaligned_mappings| is true, it is acceptable for "real_addr" + // to not be aligned to the alignment set in the constructor. If it is false, + // and "real_addr" is not aligned, the mapping will fail. + // + // Returns true if the mapping was successful, and false otherwise. + bool MapWithID(uint64_t real_addr, uint64_t size, uint64_t id, + uint64_t offset_base, bool allow_unaligned_mappings) { + return MapWithIDInternal(real_addr, size, id, offset_base, true, + allow_unaligned_mappings); + } - // Returns true if there are no mappings. - bool IsEmpty() const { return mappings_.empty(); } + // Looks up |real_addr| and returns the mapped address. + bool GetMappedAddress(uint64_t real_addr, uint64_t* mapped_addr) const; - // Returns the number of address ranges that are currently mapped. - size_t GetNumMappedRanges() const { return mappings_.size(); } - - // Returns the maximum length of quipper space containing mapped areas. - // There may be gaps in between blocks. - // If the result is 2^64 (all of quipper space), this returns 0. Call - // IsEmpty() to distinguish this from actual emptiness. - uint64_t GetMaxMappedLength() const; - - // Sets the page alignment size. Set to 0 to disable page alignment. - // The alignment value must be a power of two. Any other value passed in will - // have no effect. Changing this value in between mappings results in - // undefined behavior. - void set_page_alignment(uint64_t alignment) { - // This also includes the case of 0. - if ((alignment & (alignment - 1)) == 0) page_alignment_ = alignment; - } + // Looks up |real_addr| and returns the mapped address, the mapping's ID, and + // the offset from the start of the mapped space. + bool GetMappedAddressIDAndOffset(uint64_t real_addr, uint64_t* mapped_addr, + uint64_t* id, uint64_t* offset) const; // Dumps the state of the address mapper to logs. Useful for debugging. void DumpToLog() const; private: - typedef std::map MappingMap; - struct MappedRange { uint64_t real_addr; uint64_t mapped_addr; @@ -126,6 +98,19 @@ class AddressMapper { } }; + typedef std::list MappingList; + + typedef std::map MappingMap; + + friend class AddressMapperTestPeer; + + bool MapWithIDInternal(uint64_t real_addr, uint64_t size, uint64_t id, + uint64_t offset_base, bool remove_existing_mappings, + bool allow_unaligned_mappings); + + // Returns true if there are no mappings. + bool IsEmpty() const { return mappings_.empty(); } + // Returns an iterator to a MappedRange in |mappings_| that contains // |real_addr|. Returns |mappings_.end()| if no range contains |real_addr|. MappingList::const_iterator GetRangeContainingAddress( diff --git a/src/quipper/address_mapper_test.cc b/src/quipper/address_mapper_test.cc index c976e67..a482bb8 100644 --- a/src/quipper/address_mapper_test.cc +++ b/src/quipper/address_mapper_test.cc @@ -4,7 +4,9 @@ #include "address_mapper.h" -#include +#include +#include +#include #include #include "base/logging.h" @@ -67,6 +69,40 @@ uint64_t GetMappedAddressFromRanges(const Range* ranges, } // namespace +// A helper class that can access private members of AddressMapper for testing. +class AddressMapperTestPeer { + public: + // Returns the number of address ranges that are currently mapped. + static size_t GetNumMappedRanges(const AddressMapper& mapper) { + return mapper.mappings_.size(); + } + + // Returns the maximum length of quipper space containing mapped areas. + // There may be gaps in between blocks. + // If the result is 2^64 (all of quipper space), this returns 0. + static uint64_t GetMaxMappedLength(const AddressMapper& mapper) { + if (mapper.IsEmpty()) return 0; + + uint64_t min = mapper.mappings_.begin()->mapped_addr; + + AddressMapper::MappingList::const_iterator iter = mapper.mappings_.end(); + --iter; + uint64_t max = iter->mapped_addr + iter->size; + + return max - min; + } + + static bool MapWithIDInternal(AddressMapper& mapper, uint64_t real_addr, + uint64_t size, uint64_t id, + uint64_t offset_base, + bool remove_existing_mappings, + bool allow_unaligned_mappings) { + return mapper.MapWithIDInternal(real_addr, size, id, offset_base, + remove_existing_mappings, + allow_unaligned_mappings); + } +}; + // The unit test class for AddressMapper. class AddressMapperTest : public ::testing::Test { public: @@ -82,9 +118,9 @@ class AddressMapperTest : public ::testing::Test { bool allow_unaligned_jit_mappings) { VLOG(1) << "Mapping range at " << std::hex << range.addr << " with length of " << range.size << " and id " << range.id; - return mapper_->MapWithID(range.addr, range.size, range.id, - range.base_offset, remove_old_mappings, - allow_unaligned_jit_mappings); + return AddressMapperTestPeer::MapWithIDInternal( + *mapper_, range.addr, range.size, range.id, range.base_offset, + remove_old_mappings, allow_unaligned_jit_mappings); } // Tests a range that has been mapped. |expected_mapped_addr| is the starting @@ -93,7 +129,6 @@ class AddressMapperTest : public ::testing::Test { // Also checks lookup of ID and offset. void TestMappedRange(const Range& range, uint64_t expected_mapped_addr) { uint64_t mapped_addr = UINT64_MAX; - AddressMapper::MappingList::const_iterator addr_iter; VLOG(1) << "Testing range at " << std::hex << range.addr << " with length of " << std::hex << range.size; @@ -102,21 +137,21 @@ class AddressMapperTest : public ::testing::Test { for (int i = 0; i < kNumRangeTestIntervals; ++i) { const uint64_t offset = i * (range.size / kNumRangeTestIntervals); uint64_t addr = range.addr + offset; - EXPECT_TRUE(mapper_->GetMappedAddressAndListIterator(addr, &mapped_addr, - &addr_iter)); + EXPECT_TRUE(mapper_->GetMappedAddress(addr, &mapped_addr)); EXPECT_EQ(expected_mapped_addr + offset, mapped_addr); + uint64_t mapped_addr; uint64_t mapped_offset; uint64_t mapped_id; - mapper_->GetMappedIDAndOffset(addr, addr_iter, &mapped_id, - &mapped_offset); + EXPECT_TRUE(mapper_->GetMappedAddressIDAndOffset( + addr, &mapped_addr, &mapped_id, &mapped_offset)); EXPECT_EQ(range.base_offset + offset, mapped_offset); EXPECT_EQ(range.id, mapped_id); } // Check address at end of the range. - EXPECT_TRUE(mapper_->GetMappedAddressAndListIterator( - range.addr + range.size - 1, &mapped_addr, &addr_iter)); + EXPECT_TRUE( + mapper_->GetMappedAddress(range.addr + range.size - 1, &mapped_addr)); EXPECT_EQ(expected_mapped_addr + range.size - 1, mapped_addr); } @@ -128,21 +163,18 @@ TEST_F(AddressMapperTest, MapSingle) { for (const Range& range : kMapRanges) { mapper_.reset(new AddressMapper); ASSERT_TRUE(MapRange(range, false, false)); - EXPECT_EQ(1U, mapper_->GetNumMappedRanges()); + EXPECT_EQ(1U, AddressMapperTestPeer::GetNumMappedRanges(*mapper_)); TestMappedRange(range, 0); // Check addresses before the mapped range, should be invalid. uint64_t mapped_addr; - AddressMapper::MappingList::const_iterator iter; - EXPECT_FALSE(mapper_->GetMappedAddressAndListIterator(range.addr - 1, - &mapped_addr, &iter)); - EXPECT_FALSE(mapper_->GetMappedAddressAndListIterator(range.addr - 0x100, - &mapped_addr, &iter)); - EXPECT_FALSE(mapper_->GetMappedAddressAndListIterator( - range.addr + range.size, &mapped_addr, &iter)); - EXPECT_FALSE(mapper_->GetMappedAddressAndListIterator( - range.addr + range.size + 0x100, &mapped_addr, &iter)); - EXPECT_EQ(range.size, mapper_->GetMaxMappedLength()); + EXPECT_FALSE(mapper_->GetMappedAddress(range.addr - 1, &mapped_addr)); + EXPECT_FALSE(mapper_->GetMappedAddress(range.addr - 0x100, &mapped_addr)); + EXPECT_FALSE( + mapper_->GetMappedAddress(range.addr + range.size, &mapped_addr)); + EXPECT_FALSE(mapper_->GetMappedAddress(range.addr + range.size + 0x100, + &mapped_addr)); + EXPECT_EQ(range.size, AddressMapperTestPeer::GetMaxMappedLength(*mapper_)); } } @@ -153,35 +185,32 @@ TEST_F(AddressMapperTest, MapAll) { ASSERT_TRUE(MapRange(range, false, false)); size_mapped += range.size; } - EXPECT_EQ(arraysize(kMapRanges), mapper_->GetNumMappedRanges()); + EXPECT_EQ(arraysize(kMapRanges), + AddressMapperTestPeer::GetNumMappedRanges(*mapper_)); // Check the max mapped length in quipper space. - EXPECT_EQ(size_mapped, mapper_->GetMaxMappedLength()); + EXPECT_EQ(size_mapped, AddressMapperTestPeer::GetMaxMappedLength(*mapper_)); // For each mapped range, test addresses at the start, middle, and end. // Also test the address right before and after each range. uint64_t mapped_addr; - AddressMapper::MappingList::const_iterator iter; for (const Range& range : kMapRanges) { TestMappedRange(range, GetMappedAddressFromRanges( kMapRanges, arraysize(kMapRanges), range.addr)); // Check addresses before and after the mapped range, should be invalid. - EXPECT_FALSE(mapper_->GetMappedAddressAndListIterator(range.addr - 1, - &mapped_addr, &iter)); - EXPECT_FALSE(mapper_->GetMappedAddressAndListIterator(range.addr - 0x100, - &mapped_addr, &iter)); - EXPECT_FALSE(mapper_->GetMappedAddressAndListIterator( - range.addr + range.size, &mapped_addr, &iter)); - EXPECT_FALSE(mapper_->GetMappedAddressAndListIterator( - range.addr + range.size + 0x100, &mapped_addr, &iter)); + EXPECT_FALSE(mapper_->GetMappedAddress(range.addr - 1, &mapped_addr)); + EXPECT_FALSE(mapper_->GetMappedAddress(range.addr - 0x100, &mapped_addr)); + EXPECT_FALSE( + mapper_->GetMappedAddress(range.addr + range.size, &mapped_addr)); + EXPECT_FALSE(mapper_->GetMappedAddress(range.addr + range.size + 0x100, + &mapped_addr)); } // Test some addresses that are out of these ranges, should not be able to // get mapped addresses. for (uint64_t addr : kAddressesNotInRanges) - EXPECT_FALSE( - mapper_->GetMappedAddressAndListIterator(addr, &mapped_addr, &iter)); + EXPECT_FALSE(mapper_->GetMappedAddress(addr, &mapped_addr)); } // Map all the ranges at once and test looking up IDs and offsets. @@ -189,10 +218,10 @@ TEST_F(AddressMapperTest, MapAllWithIDsAndOffsets) { for (const Range& range : kMapRanges) { LOG(INFO) << "Mapping range at " << std::hex << range.addr << " with length of " << std::hex << range.size; - ASSERT_TRUE( - mapper_->MapWithID(range.addr, range.size, range.id, 0, false, false)); + ASSERT_TRUE(mapper_->MapWithID(range.addr, range.size, range.id, 0, false)); } - EXPECT_EQ(arraysize(kMapRanges), mapper_->GetNumMappedRanges()); + EXPECT_EQ(arraysize(kMapRanges), + AddressMapperTestPeer::GetNumMappedRanges(*mapper_)); // For each mapped range, test addresses at the start, middle, and end. // Also test the address right before and after each range. @@ -225,7 +254,8 @@ TEST_F(AddressMapperTest, OverlapSimple) { new_range.size = range.size; EXPECT_TRUE(MapRange(new_range, true, false)); // Make sure the number of ranges is unchanged (one deleted, one added). - EXPECT_EQ(arraysize(kMapRanges), mapper_->GetNumMappedRanges()); + EXPECT_EQ(arraysize(kMapRanges), + AddressMapperTestPeer::GetNumMappedRanges(*mapper_)); // The range is shifted in real space but should still be the same in // quipper space. @@ -247,7 +277,7 @@ TEST_F(AddressMapperTest, OverlapBig) { // Make sure overlap is detected before removing old ranges. ASSERT_FALSE(MapRange(kBigRegion, false, false)); ASSERT_TRUE(MapRange(kBigRegion, true, false)); - EXPECT_EQ(1U, mapper_->GetNumMappedRanges()); + EXPECT_EQ(1U, AddressMapperTestPeer::GetNumMappedRanges(*mapper_)); TestMappedRange(kBigRegion, 0); @@ -256,9 +286,7 @@ TEST_F(AddressMapperTest, OverlapBig) { // not mapped. for (uint64_t addr : kAddressesNotInRanges) { uint64_t mapped_addr = UINT64_MAX; - AddressMapper::MappingList::const_iterator addr_iter; - bool map_success = mapper_->GetMappedAddressAndListIterator( - addr, &mapped_addr, &addr_iter); + bool map_success = mapper_->GetMappedAddress(addr, &mapped_addr); if (kBigRegion.contains(addr)) { EXPECT_TRUE(map_success); EXPECT_EQ(addr - kBigRegion.addr, mapped_addr); @@ -274,9 +302,7 @@ TEST_F(AddressMapperTest, OverlapBig) { for (uint64_t addr = range.addr; addr < range.addr + range.size; addr += range.size / kNumRangeTestIntervals) { uint64_t mapped_addr = UINT64_MAX; - AddressMapper::MappingList::const_iterator addr_iter; - bool map_success = mapper_->GetMappedAddressAndListIterator( - addr, &mapped_addr, &addr_iter); + bool map_success = mapper_->GetMappedAddress(addr, &mapped_addr); if (kBigRegion.contains(addr)) { EXPECT_TRUE(map_success); EXPECT_EQ(addr - kBigRegion.addr, mapped_addr); @@ -286,7 +312,8 @@ TEST_F(AddressMapperTest, OverlapBig) { } } - EXPECT_EQ(kBigRegion.size, mapper_->GetMaxMappedLength()); + EXPECT_EQ(kBigRegion.size, + AddressMapperTestPeer::GetMaxMappedLength(*mapper_)); } // Test a mapping at the end of memory space. @@ -295,7 +322,7 @@ TEST_F(AddressMapperTest, EndOfMemory) { const Range kEndRegion(0xffffffff00000000, 0x100000000, 0x3456, 0); ASSERT_TRUE(MapRange(kEndRegion, true, false)); - EXPECT_EQ(1U, mapper_->GetNumMappedRanges()); + EXPECT_EQ(1U, AddressMapperTestPeer::GetNumMappedRanges(*mapper_)); TestMappedRange(kEndRegion, 0); } @@ -307,11 +334,10 @@ TEST_F(AddressMapperTest, OutOfBounds) { ASSERT_FALSE(MapRange(kOutOfBoundsRegion, false, false)); ASSERT_FALSE(MapRange(kOutOfBoundsRegion, true, false)); - EXPECT_EQ(0, mapper_->GetNumMappedRanges()); + EXPECT_EQ(0, AddressMapperTestPeer::GetNumMappedRanges(*mapper_)); uint64_t mapped_addr; - AddressMapper::MappingList::const_iterator iter; - EXPECT_FALSE(mapper_->GetMappedAddressAndListIterator( - kOutOfBoundsRegion.addr + 0x100, &mapped_addr, &iter)); + EXPECT_FALSE( + mapper_->GetMappedAddress(kOutOfBoundsRegion.addr + 0x100, &mapped_addr)); } // Test mapping of a region that covers the entire memory space. Then map other @@ -322,7 +348,8 @@ TEST_F(AddressMapperTest, FullRange) { ASSERT_TRUE(MapRange(kFullRegion, false, false)); size_t num_expected_ranges = 1; - EXPECT_EQ(num_expected_ranges, mapper_->GetNumMappedRanges()); + EXPECT_EQ(num_expected_ranges, + AddressMapperTestPeer::GetNumMappedRanges(*mapper_)); TestMappedRange(kFullRegion, 0); @@ -335,7 +362,8 @@ TEST_F(AddressMapperTest, FullRange) { // Make sure the number of mapped ranges has increased by two. The mapping // should have split an existing range. num_expected_ranges += 2; - EXPECT_EQ(num_expected_ranges, mapper_->GetNumMappedRanges()); + EXPECT_EQ(num_expected_ranges, + AddressMapperTestPeer::GetNumMappedRanges(*mapper_)); } } @@ -359,7 +387,7 @@ TEST_F(AddressMapperTest, SplitRangeWithOffsetBase) { // The first range should have been split into two parts to make way for the // second range. There should be a total of three ranges. - EXPECT_EQ(3U, mapper_->GetNumMappedRanges()); + EXPECT_EQ(3U, AddressMapperTestPeer::GetNumMappedRanges(*mapper_)); // Now make sure the mappings are correct. @@ -377,7 +405,7 @@ TEST_F(AddressMapperTest, SplitRangeWithOffsetBase) { // Test mappings that are not aligned to mmap page boundaries. TEST_F(AddressMapperTest, NotPageAligned) { - mapper_->set_page_alignment(0x1000); + mapper_.reset(new AddressMapper(0x1000)); // Some address ranges that do not begin on a page boundary. const Range kUnalignedRanges[] = { @@ -391,7 +419,7 @@ TEST_F(AddressMapperTest, NotPageAligned) { for (const Range& range : kUnalignedRanges) ASSERT_TRUE(MapRange(range, true, false)); - EXPECT_EQ(4U, mapper_->GetNumMappedRanges()); + EXPECT_EQ(4U, AddressMapperTestPeer::GetNumMappedRanges(*mapper_)); // Now make sure the mappings are correct. @@ -415,7 +443,7 @@ TEST_F(AddressMapperTest, NotPageAligned) { // Have one mapping in the middle of another, with a nonzero page alignment // parameter. TEST_F(AddressMapperTest, SplitRangeWithPageAlignment) { - mapper_->set_page_alignment(0x1000); + mapper_.reset(new AddressMapper(0x1000)); // These should map just fine. const Range kRange0(0x3000, 0x8000, 0xdeadbeef, 0); @@ -424,7 +452,7 @@ TEST_F(AddressMapperTest, SplitRangeWithPageAlignment) { EXPECT_TRUE(MapRange(kRange0, true, false)); EXPECT_TRUE(MapRange(kRange1, true, false)); - EXPECT_EQ(3U, mapper_->GetNumMappedRanges()); + EXPECT_EQ(3U, AddressMapperTestPeer::GetNumMappedRanges(*mapper_)); // Determine the expected split mappings. const Range kRange0Head(0x3000, 0x2000, kRange0.id, 0); @@ -439,7 +467,7 @@ TEST_F(AddressMapperTest, SplitRangeWithPageAlignment) { // Have one mapping in the middle of another, with a nonzero page alignment // parameter. The overlapping region will not be aligned to page boundaries. TEST_F(AddressMapperTest, MisalignedSplitRangeWithPageAlignment) { - mapper_->set_page_alignment(0x1000); + mapper_.reset(new AddressMapper(0x1000)); const Range kRange0(0x3000, 0x8000, 0xdeadbeef, 0); const Range kMisalignedRange(0x4800, 0x2000, 0xfeedbabe, 0); @@ -453,7 +481,7 @@ TEST_F(AddressMapperTest, MisalignedSplitRangeWithPageAlignment) { // Have one mapping in the middle of another, with a nonzero page alignment // parameter. The overlapping region will not be aligned to page boundaries. TEST_F(AddressMapperTest, MisalignedSplitRangeWithJitSupport) { - mapper_->set_page_alignment(0x1000); + mapper_.reset(new AddressMapper(0x1000)); const Range kRange0(0x3000, 0x8000, 0xdeadbeef, 0x0); const Range kJittedRange(0x4800, 0x200, 0xffffffff, 0x10000); @@ -462,7 +490,7 @@ TEST_F(AddressMapperTest, MisalignedSplitRangeWithJitSupport) { // With JIT support enabled, even unaligned ranges can split existing ranges. EXPECT_TRUE(MapRange(kJittedRange, true, true)); - EXPECT_EQ(3U, mapper_->GetNumMappedRanges()); + EXPECT_EQ(3U, AddressMapperTestPeer::GetNumMappedRanges(*mapper_)); // Everything should be mapped and split as usual. const Range kRange0Head(0x3000, 0x1800, kRange0.id, 0x0); diff --git a/src/quipper/perf_parser.cc b/src/quipper/perf_parser.cc index 659a751..3980a8f 100644 --- a/src/quipper/perf_parser.cc +++ b/src/quipper/perf_parser.cc @@ -631,12 +631,10 @@ bool PerfParser::MapIPAndPidAndGetNameAndOffset( // Sometimes the first event we see is a SAMPLE event and we don't have the // time to create an address mapper for a process. Example, for pid 0. AddressMapper* mapper = GetOrCreateProcessMapper(pidtid.first).first; - AddressMapper::MappingList::const_iterator ip_iter; + uint64_t id, offset; bool mapped = - mapper->GetMappedAddressAndListIterator(ip, &mapped_addr, &ip_iter); + mapper->GetMappedAddressIDAndOffset(ip, &mapped_addr, &id, &offset); if (mapped) { - uint64_t id = UINT64_MAX; - mapper->GetMappedIDAndOffset(ip, ip_iter, &id, &dso_and_offset->offset_); // Make sure the ID points to a valid event. CHECK_LE(id, parsed_events_.size()); ParsedEvent& parsed_event = parsed_events_[id]; @@ -647,6 +645,7 @@ bool PerfParser::MapIPAndPidAndGetNameAndOffset( auto dso_iter = name_to_dso_.find(event->mmap_event().filename()); CHECK(dso_iter != name_to_dso_.end()); dso_and_offset->dso_info_ = &dso_iter->second; + dso_and_offset->offset_ = offset; dso_iter->second.hit = true; dso_iter->second.threads.insert(mergeTwoU32(pidtid.first, pidtid.second)); @@ -736,16 +735,14 @@ bool PerfParser::MapMmapEvent(PerfDataProto_MMapEvent* event, uint64_t id, if (options_.allow_unaligned_jit_mappings) { is_jit_event = event->filename().find("jitted-") != std::string::npos; } - if (!mapper->MapWithID(start, len, id, pgoff, true, is_jit_event)) { + if (!mapper->MapWithID(start, len, id, pgoff, is_jit_event)) { mapper->DumpToLog(); return false; } if (options_.do_remap) { uint64_t mapped_addr; - AddressMapper::MappingList::const_iterator start_iter; - if (!mapper->GetMappedAddressAndListIterator(start, &mapped_addr, - &start_iter)) { + if (!mapper->GetMappedAddress(start, &mapped_addr)) { LOG(ERROR) << "Failed to map starting address " << std::hex << start; return false; } @@ -810,8 +807,7 @@ std::pair PerfParser::GetOrCreateProcessMapper( if (parent_mapper != process_mappers_.end()) { mapper.reset(new AddressMapper(*parent_mapper->second)); } else { - mapper.reset(new AddressMapper()); - mapper->set_page_alignment(kMmapPageAlignment); + mapper.reset(new AddressMapper(kMmapPageAlignment)); } const auto inserted =