1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "src/trace_processor/importers/perf/perf_session.h"
18
19 #include <cinttypes>
20 #include <cstddef>
21 #include <cstdint>
22 #include <cstring>
23 #include <limits>
24 #include <optional>
25 #include <string>
26 #include <vector>
27
28 #include "perfetto/base/logging.h"
29 #include "perfetto/base/status.h"
30 #include "perfetto/ext/base/flat_hash_map.h"
31 #include "perfetto/ext/base/status_or.h"
32 #include "perfetto/ext/base/string_utils.h"
33 #include "perfetto/ext/base/string_view.h"
34 #include "perfetto/trace_processor/ref_counted.h"
35 #include "src/trace_processor/importers/perf/perf_event.h"
36 #include "src/trace_processor/importers/perf/perf_event_attr.h"
37 #include "src/trace_processor/importers/perf/reader.h"
38 #include "src/trace_processor/storage/trace_storage.h"
39 #include "src/trace_processor/types/trace_processor_context.h"
40 #include "src/trace_processor/util/build_id.h"
41
42 namespace perfetto::trace_processor::perf_importer {
43 namespace {
OffsetsMatch(const PerfEventAttr & attr,const PerfEventAttr & other)44 bool OffsetsMatch(const PerfEventAttr& attr, const PerfEventAttr& other) {
45 return attr.id_offset_from_start() == other.id_offset_from_start() &&
46 (!attr.sample_id_all() ||
47 attr.id_offset_from_end() == other.id_offset_from_end());
48 }
49 } // namespace
50
Build()51 base::StatusOr<RefPtr<PerfSession>> PerfSession::Builder::Build() {
52 if (attr_with_ids_.empty()) {
53 return base::ErrStatus("No perf_event_attr");
54 }
55
56 auto perf_session_id =
57 context_->storage->mutable_perf_session_table()->Insert({}).id;
58
59 const PerfEventAttr base_attr(context_, perf_session_id,
60 attr_with_ids_[0].attr);
61
62 base::FlatHashMap<uint64_t, RefPtr<PerfEventAttr>> attrs_by_id;
63 for (const auto& entry : attr_with_ids_) {
64 RefPtr<PerfEventAttr> attr(
65 new PerfEventAttr(context_, perf_session_id, entry.attr));
66 if (base_attr.sample_id_all() != attr->sample_id_all()) {
67 return base::ErrStatus(
68 "perf_event_attr with different sample_id_all values");
69 }
70
71 if (!OffsetsMatch(base_attr, *attr)) {
72 return base::ErrStatus("perf_event_attr with different id offsets");
73 }
74
75 for (uint64_t id : entry.ids) {
76 if (!attrs_by_id.Insert(id, attr).second) {
77 return base::ErrStatus(
78 "Same id maps to multiple perf_event_attr: %" PRIu64, id);
79 }
80 }
81 }
82
83 if (attr_with_ids_.size() > 1 &&
84 (!base_attr.id_offset_from_start().has_value() ||
85 (base_attr.sample_id_all() &&
86 !base_attr.id_offset_from_end().has_value()))) {
87 return base::ErrStatus("No id offsets for multiple perf_event_attr");
88 }
89
90 return RefPtr<PerfSession>(new PerfSession(context_, perf_session_id,
91 std::move(attrs_by_id),
92 attr_with_ids_.size() == 1));
93 }
94
FindAttrForRecord(const perf_event_header & header,const TraceBlobView & payload) const95 base::StatusOr<RefPtr<const PerfEventAttr>> PerfSession::FindAttrForRecord(
96 const perf_event_header& header,
97 const TraceBlobView& payload) const {
98 RefPtr<const PerfEventAttr> first(attrs_by_id_.GetIterator().value().get());
99 if (has_single_perf_event_attr_) {
100 return first;
101 }
102
103 if (header.type >= PERF_RECORD_USER_TYPE_START ||
104 (header.type != PERF_RECORD_SAMPLE && !first->sample_id_all())) {
105 return RefPtr<const PerfEventAttr>();
106 }
107
108 uint64_t id;
109 if (!ReadEventId(header, payload, id)) {
110 return base::ErrStatus("Failed to read record id");
111 }
112
113 auto it = FindAttrForEventId(id);
114 if (!it) {
115 return base::ErrStatus("No perf_event_attr for id %" PRIu64, id);
116 }
117
118 return it;
119 }
120
ReadEventId(const perf_event_header & header,const TraceBlobView & payload,uint64_t & id) const121 bool PerfSession::ReadEventId(const perf_event_header& header,
122 const TraceBlobView& payload,
123 uint64_t& id) const {
124 const PerfEventAttr& first = *attrs_by_id_.GetIterator().value();
125 Reader reader(payload.copy());
126
127 if (header.type != PERF_RECORD_SAMPLE) {
128 PERFETTO_CHECK(first.id_offset_from_end().has_value());
129 if (reader.size_left() < *first.id_offset_from_end()) {
130 return false;
131 }
132 const size_t off = reader.size_left() - *first.id_offset_from_end();
133 return reader.Skip(off) && reader.Read(id);
134 }
135
136 PERFETTO_CHECK(first.id_offset_from_start().has_value());
137
138 return reader.Skip(*first.id_offset_from_start()) && reader.Read(id);
139 }
140
FindAttrForEventId(uint64_t id) const141 RefPtr<const PerfEventAttr> PerfSession::FindAttrForEventId(uint64_t id) const {
142 auto it = attrs_by_id_.Find(id);
143 if (!it) {
144 return RefPtr<const PerfEventAttr>();
145 }
146 return RefPtr<const PerfEventAttr>(it->get());
147 }
148
SetEventName(uint64_t event_id,std::string name)149 void PerfSession::SetEventName(uint64_t event_id, std::string name) {
150 auto it = attrs_by_id_.Find(event_id);
151 if (!it) {
152 return;
153 }
154 (*it)->set_event_name(std::move(name));
155 }
156
SetEventName(uint32_t type,uint64_t config,const std::string & name)157 void PerfSession::SetEventName(uint32_t type,
158 uint64_t config,
159 const std::string& name) {
160 for (auto it = attrs_by_id_.GetIterator(); it; ++it) {
161 if (it.value()->type() == type && it.value()->config() == config) {
162 it.value()->set_event_name(name);
163 }
164 }
165 }
166
AddBuildId(int32_t pid,std::string filename,BuildId build_id)167 void PerfSession::AddBuildId(int32_t pid,
168 std::string filename,
169 BuildId build_id) {
170 build_ids_.Insert({pid, std::move(filename)}, std::move(build_id));
171 }
172
LookupBuildId(uint32_t pid,const std::string & filename) const173 std::optional<BuildId> PerfSession::LookupBuildId(
174 uint32_t pid,
175 const std::string& filename) const {
176 // -1 is used in BUILD_ID feature to match any pid.
177 static constexpr int32_t kAnyPid = -1;
178 auto it = build_ids_.Find({static_cast<int32_t>(pid), filename});
179 if (!it) {
180 it = build_ids_.Find({kAnyPid, filename});
181 }
182 return it ? std::make_optional(*it) : std::nullopt;
183 }
184
SetCmdline(const std::vector<std::string> & args)185 void PerfSession::SetCmdline(const std::vector<std::string>& args) {
186 context_->storage->mutable_perf_session_table()
187 ->FindById(perf_session_id_)
188 ->set_cmdline(context_->storage->InternString(
189 base::StringView(base::Join(args, " "))));
190 }
191
192 } // namespace perfetto::trace_processor::perf_importer
193