• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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