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