xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/proto/perf_sample_tracker.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2021 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 #include "src/trace_processor/importers/proto/perf_sample_tracker.h"
17 
18 #include <cinttypes>
19 #include <cstdint>
20 #include <optional>
21 #include <utility>
22 #include <vector>
23 
24 #include "perfetto/base/logging.h"
25 #include "perfetto/ext/base/string_utils.h"
26 #include "perfetto/ext/base/string_view.h"
27 #include "src/trace_processor/importers/common/track_tracker.h"
28 #include "src/trace_processor/importers/common/tracks.h"
29 #include "src/trace_processor/importers/common/tracks_common.h"
30 #include "src/trace_processor/storage/stats.h"
31 #include "src/trace_processor/storage/trace_storage.h"
32 #include "src/trace_processor/tables/profiler_tables_py.h"
33 #include "src/trace_processor/types/trace_processor_context.h"
34 
35 #include "protos/perfetto/common/perf_events.pbzero.h"
36 #include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
37 #include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
38 #include "src/trace_processor/types/variadic.h"
39 
40 namespace perfetto::trace_processor {
41 
42 namespace {
43 // Follow perf tool naming convention.
StringifyCounter(int32_t counter)44 const char* StringifyCounter(int32_t counter) {
45   using protos::pbzero::PerfEvents;
46   switch (counter) {
47     // software:
48     case PerfEvents::SW_CPU_CLOCK:
49       return "cpu-clock";
50     case PerfEvents::SW_PAGE_FAULTS:
51       return "page-faults";
52     case PerfEvents::SW_TASK_CLOCK:
53       return "task-clock";
54     case PerfEvents::SW_CONTEXT_SWITCHES:
55       return "context-switches";
56     case PerfEvents::SW_CPU_MIGRATIONS:
57       return "cpu-migrations";
58     case PerfEvents::SW_PAGE_FAULTS_MIN:
59       return "minor-faults";
60     case PerfEvents::SW_PAGE_FAULTS_MAJ:
61       return "major-faults";
62     case PerfEvents::SW_ALIGNMENT_FAULTS:
63       return "alignment-faults";
64     case PerfEvents::SW_EMULATION_FAULTS:
65       return "emulation-faults";
66     case PerfEvents::SW_DUMMY:
67       return "dummy";
68     // hardware:
69     case PerfEvents::HW_CPU_CYCLES:
70       return "cpu-cycles";
71     case PerfEvents::HW_INSTRUCTIONS:
72       return "instructions";
73     case PerfEvents::HW_CACHE_REFERENCES:
74       return "cache-references";
75     case PerfEvents::HW_CACHE_MISSES:
76       return "cache-misses";
77     case PerfEvents::HW_BRANCH_INSTRUCTIONS:
78       return "branch-instructions";
79     case PerfEvents::HW_BRANCH_MISSES:
80       return "branch-misses";
81     case PerfEvents::HW_BUS_CYCLES:
82       return "bus-cycles";
83     case PerfEvents::HW_STALLED_CYCLES_FRONTEND:
84       return "stalled-cycles-frontend";
85     case PerfEvents::HW_STALLED_CYCLES_BACKEND:
86       return "stalled-cycles-backend";
87     case PerfEvents::HW_REF_CPU_CYCLES:
88       return "ref-cycles";
89 
90     default:
91       break;
92   }
93   PERFETTO_DLOG("Unknown PerfEvents::Counter enum value");
94   return "unknown";
95 }
96 
97 template <typename T>
InternCounterName(const protos::pbzero::PerfSampleDefaults::Decoder & perf_defaults,TraceProcessorContext * context,const T & desc_decoder)98 StringId InternCounterName(
99     const protos::pbzero::PerfSampleDefaults::Decoder& perf_defaults,
100     TraceProcessorContext* context,
101     const T& desc_decoder) {
102   using namespace protos::pbzero;
103   PerfEvents::Timebase::Decoder timebase(perf_defaults.timebase());
104 
105   auto config_given_name = desc_decoder.name();
106   if (config_given_name.size > 0) {
107     return context->storage->InternString(config_given_name);
108   }
109   if (desc_decoder.has_counter()) {
110     return context->storage->InternString(
111         StringifyCounter(desc_decoder.counter()));
112   }
113   if (desc_decoder.has_tracepoint()) {
114     PerfEvents::Tracepoint::Decoder tracepoint(desc_decoder.tracepoint());
115     return context->storage->InternString(tracepoint.name());
116   }
117   if (desc_decoder.has_raw_event()) {
118     PerfEvents::RawEvent::Decoder raw(desc_decoder.raw_event());
119     // This doesn't follow any pre-existing naming scheme, but aims to be a
120     // short-enough default that is distinguishable.
121     base::StackString<128> name(
122         "raw.0x%" PRIx32 ".0x%" PRIx64 ".0x%" PRIx64 ".0x%" PRIx64, raw.type(),
123         raw.config(), raw.config1(), raw.config2());
124     return context->storage->InternString(name.string_view());
125   }
126 
127   PERFETTO_DLOG("Could not name the perf counter");
128   return context->storage->InternString("unknown");
129 }
130 
InternTimebaseCounterName(const protos::pbzero::PerfSampleDefaults::Decoder & perf_defaults,TraceProcessorContext * context)131 StringId InternTimebaseCounterName(
132     const protos::pbzero::PerfSampleDefaults::Decoder& perf_defaults,
133     TraceProcessorContext* context) {
134   using namespace protos::pbzero;
135   PerfEvents::Timebase::Decoder timebase(perf_defaults.timebase());
136   return InternCounterName(perf_defaults, context, timebase);
137 }
138 
InternFollowersCounterName(const protos::pbzero::PerfSampleDefaults::Decoder & perf_defaults,TraceProcessorContext * context)139 std::vector<StringId> InternFollowersCounterName(
140     const protos::pbzero::PerfSampleDefaults::Decoder& perf_defaults,
141     TraceProcessorContext* context) {
142   using namespace protos::pbzero;
143 
144   std::vector<StringId> string_ids;
145 
146   for (auto it = perf_defaults.followers(); it; ++it) {
147     FollowerEvent::Decoder followers(*it);
148     string_ids.push_back(InternCounterName(perf_defaults, context, followers));
149   }
150 
151   return string_ids;
152 }
153 }  // namespace
154 
PerfSampleTracker(TraceProcessorContext * context)155 PerfSampleTracker::PerfSampleTracker(TraceProcessorContext* context)
156     : is_timebase_id_(context->storage->InternString("is_timebase")),
157       context_(context) {}
158 
GetSamplingStreamInfo(uint32_t seq_id,uint32_t cpu,protos::pbzero::TracePacketDefaults::Decoder * nullable_defaults)159 PerfSampleTracker::SamplingStreamInfo PerfSampleTracker::GetSamplingStreamInfo(
160     uint32_t seq_id,
161     uint32_t cpu,
162     protos::pbzero::TracePacketDefaults::Decoder* nullable_defaults) {
163   using protos::pbzero::PerfSampleDefaults;
164 
165   auto seq_it = seq_state_.find(seq_id);
166   if (seq_it == seq_state_.end()) {
167     seq_it = seq_state_.emplace(seq_id, CreatePerfSession()).first;
168   }
169   SequenceState* seq_state = &seq_it->second;
170   tables::PerfSessionTable::Id session_id = seq_state->perf_session_id;
171 
172   auto cpu_it = seq_state->per_cpu.find(cpu);
173   if (cpu_it != seq_state->per_cpu.end())
174     return {seq_state->perf_session_id, cpu_it->second.timebase_track_id,
175             cpu_it->second.follower_track_ids};
176 
177   std::optional<PerfSampleDefaults::Decoder> perf_defaults;
178   if (nullable_defaults && nullable_defaults->has_perf_sample_defaults()) {
179     perf_defaults.emplace(nullable_defaults->perf_sample_defaults());
180   }
181 
182   StringId name_id = kNullStringId;
183   if (perf_defaults.has_value()) {
184     name_id = InternTimebaseCounterName(perf_defaults.value(), context_);
185   } else {
186     // No defaults means legacy producer implementation, assume default timebase
187     // of per-cpu timer. This means either an Android R or early S build.
188     name_id = context_->storage->InternString(
189         StringifyCounter(protos::pbzero::PerfEvents::SW_CPU_CLOCK));
190   }
191 
192   base::StringView name = context_->storage->GetString(name_id);
193   TrackId timebase_track_id = context_->track_tracker->InternTrack(
194       tracks::kPerfCounterBlueprint,
195       tracks::Dimensions(cpu, session_id.value, name),
196       tracks::DynamicName(name_id),
197       [this](ArgsTracker::BoundInserter& inserter) {
198         inserter.AddArg(is_timebase_id_, Variadic::Boolean(true));
199       });
200 
201   std::vector<TrackId> follower_track_ids;
202   if (perf_defaults.has_value()) {
203     auto name_ids = InternFollowersCounterName(perf_defaults.value(), context_);
204     follower_track_ids.reserve(name_ids.size());
205     for (const auto& follower_name_id : name_ids) {
206       base::StringView follower_name =
207           context_->storage->GetString(follower_name_id);
208       follower_track_ids.push_back(context_->track_tracker->InternTrack(
209           tracks::kPerfCounterBlueprint,
210           tracks::Dimensions(cpu, session_id.value, follower_name),
211           tracks::DynamicName(follower_name_id),
212           [this](ArgsTracker::BoundInserter& inserter) {
213             inserter.AddArg(is_timebase_id_, Variadic::Boolean(true));
214           }));
215     }
216   }
217 
218   seq_state->per_cpu.emplace(
219       cpu, CpuSequenceState{timebase_track_id, follower_track_ids});
220 
221   // If the config requested process sharding, record in the stats table which
222   // shard was chosen for the trace. It should be the same choice for all data
223   // sources within one trace, but for consistency with other stats we put an
224   // entry per data source (i.e. |perf_session_id|, not to be confused with the
225   // tracing session).
226   if (perf_defaults.has_value() && perf_defaults->process_shard_count() > 0) {
227     context_->storage->SetIndexedStats(
228         stats::perf_process_shard_count, static_cast<int>(session_id.value),
229         static_cast<int64_t>(perf_defaults->process_shard_count()));
230     context_->storage->SetIndexedStats(
231         stats::perf_chosen_process_shard, static_cast<int>(session_id.value),
232         static_cast<int64_t>(perf_defaults->chosen_process_shard()));
233   }
234 
235   return {session_id, timebase_track_id, std::move(follower_track_ids)};
236 }
237 
CreatePerfSession()238 tables::PerfSessionTable::Id PerfSampleTracker::CreatePerfSession() {
239   return context_->storage->mutable_perf_session_table()->Insert({}).id;
240 }
241 
242 }  // namespace perfetto::trace_processor
243