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 <optional>
24 #include <string>
25 #include <utility>
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 "perfetto/trace_processor/trace_blob_view.h"
36 #include "protos/perfetto/common/builtin_clock.pbzero.h"
37 #include "src/trace_processor/importers/perf/perf_event.h"
38 #include "src/trace_processor/importers/perf/perf_event_attr.h"
39 #include "src/trace_processor/importers/perf/reader.h"
40 #include "src/trace_processor/storage/trace_storage.h" // IWYU pragma: keep
41 #include "src/trace_processor/types/trace_processor_context.h"
42 #include "src/trace_processor/util/build_id.h"
43
44 namespace perfetto::trace_processor::perf_importer {
45 namespace {
OffsetsMatch(const PerfEventAttr & attr,const PerfEventAttr & other)46 bool OffsetsMatch(const PerfEventAttr& attr, const PerfEventAttr& other) {
47 return attr.id_offset_from_start() == other.id_offset_from_start() &&
48 (!attr.sample_id_all() ||
49 attr.id_offset_from_end() == other.id_offset_from_end());
50 }
51 } // namespace
52
Build()53 base::StatusOr<RefPtr<PerfSession>> PerfSession::Builder::Build() {
54 if (attr_with_ids_.empty()) {
55 return base::ErrStatus("No perf_event_attr");
56 }
57
58 auto perf_session_id =
59 context_->storage->mutable_perf_session_table()->Insert({}).id;
60
61 RefPtr<PerfEventAttr> first_attr;
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 (!first_attr) {
67 first_attr = attr;
68 }
69 if (first_attr->sample_id_all() != attr->sample_id_all()) {
70 return base::ErrStatus(
71 "perf_event_attr with different sample_id_all values");
72 }
73
74 if (!OffsetsMatch(*first_attr, *attr)) {
75 return base::ErrStatus("perf_event_attr with different id offsets");
76 }
77
78 for (uint64_t id : entry.ids) {
79 if (!attrs_by_id.Insert(id, attr).second) {
80 return base::ErrStatus(
81 "Same id maps to multiple perf_event_attr: %" PRIu64, id);
82 }
83 }
84 }
85 if (attr_with_ids_.size() > 1 &&
86 (!first_attr->id_offset_from_start().has_value() ||
87 (first_attr->sample_id_all() &&
88 !first_attr->id_offset_from_end().has_value()))) {
89 return base::ErrStatus("No id offsets for multiple perf_event_attr");
90 }
91 return RefPtr<PerfSession>(
92 new PerfSession(context_, perf_session_id, std::move(first_attr),
93 std::move(attrs_by_id), attr_with_ids_.size() == 1));
94 }
95
FindAttrForRecord(const perf_event_header & header,const TraceBlobView & payload) const96 base::StatusOr<RefPtr<PerfEventAttr>> PerfSession::FindAttrForRecord(
97 const perf_event_header& header,
98 const TraceBlobView& payload) const {
99 if (header.type >= PERF_RECORD_USER_TYPE_START) {
100 return RefPtr<PerfEventAttr>();
101 }
102
103 if (has_single_perf_event_attr_) {
104 return first_attr_;
105 }
106
107 if (header.type != PERF_RECORD_SAMPLE && !first_attr_->sample_id_all()) {
108 return first_attr_;
109 }
110
111 uint64_t id;
112 if (!ReadEventId(header, payload, id)) {
113 return base::ErrStatus("Failed to read record id");
114 }
115
116 if (id == 0) {
117 return first_attr_;
118 }
119
120 auto it = FindAttrForEventId(id);
121 if (!it) {
122 return base::ErrStatus("No perf_event_attr for id %" PRIu64, id);
123 }
124 return it;
125 }
126
ReadEventId(const perf_event_header & header,const TraceBlobView & payload,uint64_t & id) const127 bool PerfSession::ReadEventId(const perf_event_header& header,
128 const TraceBlobView& payload,
129 uint64_t& id) const {
130 const PerfEventAttr& first = *attrs_by_id_.GetIterator().value();
131 Reader reader(payload.copy());
132
133 if (header.type != PERF_RECORD_SAMPLE) {
134 PERFETTO_CHECK(first.id_offset_from_end().has_value());
135 if (reader.size_left() < *first.id_offset_from_end()) {
136 return false;
137 }
138 const size_t off = reader.size_left() - *first.id_offset_from_end();
139 return reader.Skip(off) && reader.Read(id);
140 }
141 PERFETTO_CHECK(first.id_offset_from_start().has_value());
142 return reader.Skip(*first.id_offset_from_start()) && reader.Read(id);
143 }
144
FindAttrForEventId(uint64_t id) const145 RefPtr<PerfEventAttr> PerfSession::FindAttrForEventId(uint64_t id) const {
146 auto* it = attrs_by_id_.Find(id);
147 if (!it) {
148 return {};
149 }
150 return RefPtr<PerfEventAttr>(it->get());
151 }
152
SetEventName(uint64_t event_id,std::string name)153 void PerfSession::SetEventName(uint64_t event_id, std::string name) {
154 auto* it = attrs_by_id_.Find(event_id);
155 if (!it) {
156 return;
157 }
158 (*it)->set_event_name(std::move(name));
159 }
160
SetEventName(uint32_t type,uint64_t config,const std::string & name)161 void PerfSession::SetEventName(uint32_t type,
162 uint64_t config,
163 const std::string& name) {
164 for (auto it = attrs_by_id_.GetIterator(); it; ++it) {
165 if (it.value()->type() == type && it.value()->config() == config) {
166 it.value()->set_event_name(name);
167 }
168 }
169 }
170
AddBuildId(int32_t pid,std::string filename,BuildId build_id)171 void PerfSession::AddBuildId(int32_t pid,
172 std::string filename,
173 BuildId build_id) {
174 build_ids_.Insert({pid, std::move(filename)}, std::move(build_id));
175 }
176
LookupBuildId(uint32_t pid,const std::string & filename) const177 std::optional<BuildId> PerfSession::LookupBuildId(
178 uint32_t pid,
179 const std::string& filename) const {
180 // -1 is used in BUILD_ID feature to match any pid.
181 static constexpr int32_t kAnyPid = -1;
182 auto* it = build_ids_.Find({static_cast<int32_t>(pid), filename});
183 if (!it) {
184 it = build_ids_.Find({kAnyPid, filename});
185 }
186 return it ? std::make_optional(*it) : std::nullopt;
187 }
188
SetCmdline(const std::vector<std::string> & args)189 void PerfSession::SetCmdline(const std::vector<std::string>& args) {
190 context_->storage->mutable_perf_session_table()
191 ->FindById(perf_session_id_)
192 ->set_cmdline(context_->storage->InternString(
193 base::StringView(base::Join(args, " "))));
194 }
195
HasPerfClock() const196 bool PerfSession::HasPerfClock() const {
197 for (auto it = attrs_by_id_.GetIterator(); it; ++it) {
198 if (it.value()->clock_id() == protos::pbzero::BUILTIN_CLOCK_PERF) {
199 return true;
200 }
201 }
202 return false;
203 }
204
205 } // namespace perfetto::trace_processor::perf_importer
206