1 /*
2 * Copyright (C) 2023 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/ftrace/pkvm_hyp_cpu_tracker.h"
18
19 #include <cstdint>
20
21 #include "perfetto/base/logging.h"
22 #include "perfetto/ext/base/string_utils.h"
23 #include "perfetto/protozero/field.h"
24 #include "src/trace_processor/importers/common/slice_tracker.h"
25 #include "src/trace_processor/importers/common/track_tracker.h"
26 #include "src/trace_processor/importers/common/tracks.h"
27 #include "src/trace_processor/importers/common/tracks_common.h"
28 #include "src/trace_processor/storage/trace_storage.h"
29
30 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
31 #include "protos/perfetto/trace/ftrace/hyp.pbzero.h"
32 #include "src/trace_processor/types/variadic.h"
33
34 namespace perfetto::trace_processor {
35 namespace {
36
37 constexpr auto kPkvmBlueprint = tracks::SliceBlueprint(
38 "pkvm_hypervisor",
39 tracks::DimensionBlueprints(tracks::kCpuDimensionBlueprint),
__anonbb614a1f0202(uint32_t cpu) 40 tracks::FnNameBlueprint([](uint32_t cpu) {
41 return base::StackString<255>("pkVM Hypervisor CPU %u", cpu);
42 }));
43
44 } // namespace
45
PkvmHypervisorCpuTracker(TraceProcessorContext * context)46 PkvmHypervisorCpuTracker::PkvmHypervisorCpuTracker(
47 TraceProcessorContext* context)
48 : context_(context),
49 category_(context->storage->InternString("pkvm_hyp")),
50 slice_name_(context->storage->InternString("hyp")),
51 hyp_enter_reason_(context->storage->InternString("hyp_enter_reason")) {}
52
53 // static
IsPkvmHypervisorEvent(uint32_t event_id)54 bool PkvmHypervisorCpuTracker::IsPkvmHypervisorEvent(uint32_t event_id) {
55 using protos::pbzero::FtraceEvent;
56 switch (event_id) {
57 case FtraceEvent::kHypEnterFieldNumber:
58 case FtraceEvent::kHypExitFieldNumber:
59 case FtraceEvent::kHostHcallFieldNumber:
60 case FtraceEvent::kHostMemAbortFieldNumber:
61 case FtraceEvent::kHostSmcFieldNumber:
62 return true;
63 default:
64 return false;
65 }
66 }
67
ParseHypEvent(uint32_t cpu,int64_t timestamp,uint32_t event_id,protozero::ConstBytes blob)68 void PkvmHypervisorCpuTracker::ParseHypEvent(uint32_t cpu,
69 int64_t timestamp,
70 uint32_t event_id,
71 protozero::ConstBytes blob) {
72 using protos::pbzero::FtraceEvent;
73 switch (event_id) {
74 case FtraceEvent::kHypEnterFieldNumber:
75 ParseHypEnter(cpu, timestamp);
76 break;
77 case FtraceEvent::kHypExitFieldNumber:
78 ParseHypExit(cpu, timestamp);
79 break;
80 case FtraceEvent::kHostHcallFieldNumber:
81 ParseHostHcall(cpu, blob);
82 break;
83 case FtraceEvent::kHostMemAbortFieldNumber:
84 ParseHostMemAbort(cpu, blob);
85 break;
86 case FtraceEvent::kHostSmcFieldNumber:
87 ParseHostSmc(cpu, blob);
88 break;
89 // TODO(b/249050813): add remaining hypervisor events
90 default:
91 PERFETTO_FATAL("Not a hypervisor event %u", event_id);
92 }
93 }
94
ParseHypEnter(uint32_t cpu,int64_t timestamp)95 void PkvmHypervisorCpuTracker::ParseHypEnter(uint32_t cpu, int64_t timestamp) {
96 // TODO(b/249050813): handle bad events (e.g. 2 hyp_enter in a row)
97 TrackId track_id = context_->track_tracker->InternTrack(
98 kPkvmBlueprint, tracks::Dimensions(cpu));
99 context_->slice_tracker->Begin(timestamp, track_id, category_, slice_name_);
100 }
101
ParseHypExit(uint32_t cpu,int64_t timestamp)102 void PkvmHypervisorCpuTracker::ParseHypExit(uint32_t cpu, int64_t timestamp) {
103 // TODO(b/249050813): handle bad events (e.g. 2 hyp_exit in a row)
104 TrackId track_id = context_->track_tracker->InternTrack(
105 kPkvmBlueprint, tracks::Dimensions(cpu));
106 context_->slice_tracker->End(timestamp, track_id);
107 }
108
ParseHostHcall(uint32_t cpu,protozero::ConstBytes blob)109 void PkvmHypervisorCpuTracker::ParseHostHcall(uint32_t cpu,
110 protozero::ConstBytes blob) {
111 protos::pbzero::HostHcallFtraceEvent::Decoder evt(blob);
112 TrackId track_id = context_->track_tracker->InternTrack(
113 kPkvmBlueprint, tracks::Dimensions(cpu));
114 context_->slice_tracker->AddArgs(
115 track_id, category_, slice_name_,
116 [&, this](ArgsTracker::BoundInserter* inserter) {
117 StringId host_hcall = context_->storage->InternString("host_hcall");
118 StringId id = context_->storage->InternString("id");
119 StringId invalid = context_->storage->InternString("invalid");
120 inserter->AddArg(hyp_enter_reason_, Variadic::String(host_hcall));
121 inserter->AddArg(id, Variadic::UnsignedInteger(evt.id()));
122 inserter->AddArg(invalid, Variadic::UnsignedInteger(evt.invalid()));
123 });
124 }
125
ParseHostSmc(uint32_t cpu,protozero::ConstBytes blob)126 void PkvmHypervisorCpuTracker::ParseHostSmc(uint32_t cpu,
127 protozero::ConstBytes blob) {
128 protos::pbzero::HostSmcFtraceEvent::Decoder evt(blob);
129 TrackId track_id = context_->track_tracker->InternTrack(
130 kPkvmBlueprint, tracks::Dimensions(cpu));
131 context_->slice_tracker->AddArgs(
132 track_id, category_, slice_name_,
133 [&, this](ArgsTracker::BoundInserter* inserter) {
134 StringId host_smc = context_->storage->InternString("host_smc");
135 StringId id = context_->storage->InternString("id");
136 StringId forwarded = context_->storage->InternString("forwarded");
137 inserter->AddArg(hyp_enter_reason_, Variadic::String(host_smc));
138 inserter->AddArg(id, Variadic::UnsignedInteger(evt.id()));
139 inserter->AddArg(forwarded, Variadic::UnsignedInteger(evt.forwarded()));
140 });
141 }
142
ParseHostMemAbort(uint32_t cpu,protozero::ConstBytes blob)143 void PkvmHypervisorCpuTracker::ParseHostMemAbort(uint32_t cpu,
144 protozero::ConstBytes blob) {
145 protos::pbzero::HostMemAbortFtraceEvent::Decoder evt(blob);
146 TrackId track_id = context_->track_tracker->InternTrack(
147 kPkvmBlueprint, tracks::Dimensions(cpu));
148 context_->slice_tracker->AddArgs(
149 track_id, category_, slice_name_,
150 [&, this](ArgsTracker::BoundInserter* inserter) {
151 StringId host_mem_abort =
152 context_->storage->InternString("host_mem_abort");
153 StringId esr = context_->storage->InternString("esr");
154 StringId addr = context_->storage->InternString("addr");
155 inserter->AddArg(hyp_enter_reason_, Variadic::String(host_mem_abort));
156 inserter->AddArg(esr, Variadic::UnsignedInteger(evt.esr()));
157 inserter->AddArg(addr, Variadic::UnsignedInteger(evt.addr()));
158 });
159 }
160
161 } // namespace perfetto::trace_processor
162