• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <stdio.h>
19 
20 #include <cinttypes>
21 
22 #include "perfetto/ext/base/string_utils.h"
23 #include "src/trace_processor/importers/common/track_tracker.h"
24 #include "src/trace_processor/storage/trace_storage.h"
25 #include "src/trace_processor/types/trace_processor_context.h"
26 
27 #include "protos/perfetto/common/perf_events.pbzero.h"
28 #include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
29 #include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
30 
31 namespace perfetto {
32 namespace trace_processor {
33 
34 namespace {
35 // Follow perf tool naming convention.
StringifyCounter(int32_t counter)36 const char* StringifyCounter(int32_t counter) {
37   using protos::pbzero::PerfEvents;
38   switch (counter) {
39     // software:
40     case PerfEvents::SW_CPU_CLOCK:
41       return "cpu-clock";
42     case PerfEvents::SW_PAGE_FAULTS:
43       return "page-faults";
44     case PerfEvents::SW_TASK_CLOCK:
45       return "task-clock";
46     case PerfEvents::SW_CONTEXT_SWITCHES:
47       return "context-switches";
48     case PerfEvents::SW_CPU_MIGRATIONS:
49       return "cpu-migrations";
50     case PerfEvents::SW_PAGE_FAULTS_MIN:
51       return "minor-faults";
52     case PerfEvents::SW_PAGE_FAULTS_MAJ:
53       return "major-faults";
54     case PerfEvents::SW_ALIGNMENT_FAULTS:
55       return "alignment-faults";
56     case PerfEvents::SW_EMULATION_FAULTS:
57       return "emulation-faults";
58     case PerfEvents::SW_DUMMY:
59       return "dummy";
60     // hardware:
61     case PerfEvents::HW_CPU_CYCLES:
62       return "cpu-cycles";
63     case PerfEvents::HW_INSTRUCTIONS:
64       return "instructions";
65     case PerfEvents::HW_CACHE_REFERENCES:
66       return "cache-references";
67     case PerfEvents::HW_CACHE_MISSES:
68       return "cache-misses";
69     case PerfEvents::HW_BRANCH_INSTRUCTIONS:
70       return "branch-instructions";
71     case PerfEvents::HW_BRANCH_MISSES:
72       return "branch-misses";
73     case PerfEvents::HW_BUS_CYCLES:
74       return "bus-cycles";
75     case PerfEvents::HW_STALLED_CYCLES_FRONTEND:
76       return "stalled-cycles-frontend";
77     case PerfEvents::HW_STALLED_CYCLES_BACKEND:
78       return "stalled-cycles-backend";
79     case PerfEvents::HW_REF_CPU_CYCLES:
80       return "ref-cycles";
81 
82     default:
83       break;
84   }
85   PERFETTO_DLOG("Unknown PerfEvents::Counter enum value");
86   return "unknown";
87 }
88 
InternTimebaseCounterName(const protos::pbzero::PerfSampleDefaults::Decoder & perf_defaults,TraceProcessorContext * context)89 StringId InternTimebaseCounterName(
90     const protos::pbzero::PerfSampleDefaults::Decoder& perf_defaults,
91     TraceProcessorContext* context) {
92   using namespace protos::pbzero;
93   PerfEvents::Timebase::Decoder timebase(perf_defaults.timebase());
94 
95   auto config_given_name = timebase.name();
96   if (config_given_name.size > 0) {
97     return context->storage->InternString(config_given_name);
98   }
99   if (timebase.has_counter()) {
100     return context->storage->InternString(StringifyCounter(timebase.counter()));
101   }
102   if (timebase.has_tracepoint()) {
103     PerfEvents::Tracepoint::Decoder tracepoint(timebase.tracepoint());
104     return context->storage->InternString(tracepoint.name());
105   }
106   if (timebase.has_raw_event()) {
107     PerfEvents::RawEvent::Decoder raw(timebase.raw_event());
108     // This doesn't follow any pre-existing naming scheme, but aims to be a
109     // short-enough default that is distinguishable.
110     base::StackString<128> name(
111         "raw.0x%" PRIx32 ".0x%" PRIx64 ".0x%" PRIx64 ".0x%" PRIx64, raw.type(),
112         raw.config(), raw.config1(), raw.config2());
113     return context->storage->InternString(name.string_view());
114   }
115 
116   PERFETTO_DLOG("Could not name the perf timebase counter");
117   return context->storage->InternString("unknown");
118 }
119 }  // namespace
120 
GetSamplingStreamInfo(uint32_t seq_id,uint32_t cpu,protos::pbzero::TracePacketDefaults::Decoder * nullable_defaults)121 PerfSampleTracker::SamplingStreamInfo PerfSampleTracker::GetSamplingStreamInfo(
122     uint32_t seq_id,
123     uint32_t cpu,
124     protos::pbzero::TracePacketDefaults::Decoder* nullable_defaults) {
125   using protos::pbzero::PerfSampleDefaults;
126 
127   auto seq_it = seq_state_.find(seq_id);
128   if (seq_it == seq_state_.end()) {
129     seq_it = seq_state_.emplace(seq_id, next_perf_session_id_++).first;
130   }
131   SequenceState* seq_state = &seq_it->second;
132   uint32_t session_id = seq_state->perf_session_id;
133 
134   auto cpu_it = seq_state->per_cpu.find(cpu);
135   if (cpu_it != seq_state->per_cpu.end())
136     return {seq_state->perf_session_id, cpu_it->second.timebase_track_id};
137 
138   std::optional<PerfSampleDefaults::Decoder> perf_defaults;
139   if (nullable_defaults && nullable_defaults->has_perf_sample_defaults()) {
140     perf_defaults.emplace(nullable_defaults->perf_sample_defaults());
141   }
142 
143   StringId name_id = kNullStringId;
144   if (perf_defaults.has_value()) {
145     name_id = InternTimebaseCounterName(perf_defaults.value(), context_);
146   } else {
147     // No defaults means legacy producer implementation, assume default timebase
148     // of per-cpu timer. This means either an Android R or early S build.
149     name_id = context_->storage->InternString(
150         StringifyCounter(protos::pbzero::PerfEvents::SW_CPU_CLOCK));
151   }
152 
153   TrackId timebase_track_id = context_->track_tracker->CreatePerfCounterTrack(
154       name_id, session_id, cpu, /*is_timebase=*/true);
155 
156   seq_state->per_cpu.emplace(cpu, timebase_track_id);
157 
158   // If the config requested process sharding, record in the stats table which
159   // shard was chosen for the trace. It should be the same choice for all data
160   // sources within one trace, but for consistency with other stats we put an
161   // entry per data source (i.e. |perf_session_id|, not to be confused with the
162   // tracing session).
163   if (perf_defaults.has_value() && perf_defaults->process_shard_count() > 0) {
164     context_->storage->SetIndexedStats(
165         stats::perf_process_shard_count, static_cast<int>(session_id),
166         static_cast<int64_t>(perf_defaults->process_shard_count()));
167     context_->storage->SetIndexedStats(
168         stats::perf_chosen_process_shard, static_cast<int>(session_id),
169         static_cast<int64_t>(perf_defaults->chosen_process_shard()));
170   }
171 
172   return {session_id, timebase_track_id};
173 }
174 
175 }  // namespace trace_processor
176 }  // namespace perfetto
177