| /* |
| * Copyright (C) 2023 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "src/trace_processor/importers/ftrace/pkvm_hyp_cpu_tracker.h" |
| |
| #include "perfetto/base/logging.h" |
| #include "perfetto/ext/base/string_utils.h" |
| #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h" |
| #include "protos/perfetto/trace/ftrace/hyp.pbzero.h" |
| #include "src/trace_processor/importers/common/event_tracker.h" |
| #include "src/trace_processor/importers/common/slice_tracker.h" |
| #include "src/trace_processor/importers/common/track_tracker.h" |
| |
| namespace perfetto { |
| namespace trace_processor { |
| |
| PkvmHypervisorCpuTracker::PkvmHypervisorCpuTracker( |
| TraceProcessorContext* context) |
| : context_(context), |
| category_(context->storage->InternString("pkvm_hyp")), |
| slice_name_(context->storage->InternString("hyp")), |
| hyp_enter_reason_(context->storage->InternString("hyp_enter_reason")) {} |
| |
| // static |
| bool PkvmHypervisorCpuTracker::IsPkvmHypervisorEvent(uint16_t event_id) { |
| using protos::pbzero::FtraceEvent; |
| switch (event_id) { |
| case FtraceEvent::kHypEnterFieldNumber: |
| case FtraceEvent::kHypExitFieldNumber: |
| case FtraceEvent::kHostHcallFieldNumber: |
| case FtraceEvent::kHostMemAbortFieldNumber: |
| case FtraceEvent::kHostSmcFieldNumber: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| void PkvmHypervisorCpuTracker::ParseHypEvent(uint32_t cpu, |
| int64_t timestamp, |
| uint16_t event_id, |
| protozero::ConstBytes blob) { |
| using protos::pbzero::FtraceEvent; |
| switch (event_id) { |
| case FtraceEvent::kHypEnterFieldNumber: |
| ParseHypEnter(cpu, timestamp); |
| break; |
| case FtraceEvent::kHypExitFieldNumber: |
| ParseHypExit(cpu, timestamp); |
| break; |
| case FtraceEvent::kHostHcallFieldNumber: |
| ParseHostHcall(cpu, blob); |
| break; |
| case FtraceEvent::kHostMemAbortFieldNumber: |
| ParseHostMemAbort(cpu, blob); |
| break; |
| case FtraceEvent::kHostSmcFieldNumber: |
| ParseHostSmc(cpu, blob); |
| break; |
| // TODO(b/249050813): add remaining hypervisor events |
| default: |
| PERFETTO_FATAL("Not a hypervisor event %d", event_id); |
| } |
| } |
| |
| void PkvmHypervisorCpuTracker::ParseHypEnter(uint32_t cpu, int64_t timestamp) { |
| // TODO(b/249050813): handle bad events (e.g. 2 hyp_enter in a row) |
| |
| TrackId track_id = GetHypCpuTrackId(cpu); |
| context_->slice_tracker->Begin(timestamp, track_id, category_, slice_name_); |
| } |
| |
| void PkvmHypervisorCpuTracker::ParseHypExit(uint32_t cpu, int64_t timestamp) { |
| // TODO(b/249050813): handle bad events (e.g. 2 hyp_exit in a row) |
| TrackId track_id = GetHypCpuTrackId(cpu); |
| context_->slice_tracker->End(timestamp, track_id); |
| } |
| |
| void PkvmHypervisorCpuTracker::ParseHostHcall(uint32_t cpu, |
| protozero::ConstBytes blob) { |
| protos::pbzero::HostHcallFtraceEvent::Decoder evt(blob.data, blob.size); |
| TrackId track_id = GetHypCpuTrackId(cpu); |
| |
| auto args_inserter = [this, &evt](ArgsTracker::BoundInserter* inserter) { |
| StringId host_hcall = context_->storage->InternString("host_hcall"); |
| StringId id = context_->storage->InternString("id"); |
| StringId invalid = context_->storage->InternString("invalid"); |
| inserter->AddArg(hyp_enter_reason_, Variadic::String(host_hcall)); |
| inserter->AddArg(id, Variadic::UnsignedInteger(evt.id())); |
| inserter->AddArg(invalid, Variadic::UnsignedInteger(evt.invalid())); |
| }; |
| context_->slice_tracker->AddArgs(track_id, category_, slice_name_, |
| args_inserter); |
| } |
| |
| void PkvmHypervisorCpuTracker::ParseHostSmc(uint32_t cpu, |
| protozero::ConstBytes blob) { |
| protos::pbzero::HostSmcFtraceEvent::Decoder evt(blob.data, blob.size); |
| TrackId track_id = GetHypCpuTrackId(cpu); |
| |
| auto args_inserter = [this, &evt](ArgsTracker::BoundInserter* inserter) { |
| StringId host_smc = context_->storage->InternString("host_smc"); |
| StringId id = context_->storage->InternString("id"); |
| StringId forwarded = context_->storage->InternString("forwarded"); |
| inserter->AddArg(hyp_enter_reason_, Variadic::String(host_smc)); |
| inserter->AddArg(id, Variadic::UnsignedInteger(evt.id())); |
| inserter->AddArg(forwarded, Variadic::UnsignedInteger(evt.forwarded())); |
| }; |
| context_->slice_tracker->AddArgs(track_id, category_, slice_name_, |
| args_inserter); |
| } |
| |
| void PkvmHypervisorCpuTracker::ParseHostMemAbort(uint32_t cpu, |
| protozero::ConstBytes blob) { |
| protos::pbzero::HostMemAbortFtraceEvent::Decoder evt(blob.data, blob.size); |
| TrackId track_id = GetHypCpuTrackId(cpu); |
| |
| auto args_inserter = [this, &evt](ArgsTracker::BoundInserter* inserter) { |
| StringId host_mem_abort = context_->storage->InternString("host_mem_abort"); |
| StringId esr = context_->storage->InternString("esr"); |
| StringId addr = context_->storage->InternString("addr"); |
| inserter->AddArg(hyp_enter_reason_, Variadic::String(host_mem_abort)); |
| inserter->AddArg(esr, Variadic::UnsignedInteger(evt.esr())); |
| inserter->AddArg(addr, Variadic::UnsignedInteger(evt.addr())); |
| }; |
| context_->slice_tracker->AddArgs(track_id, category_, slice_name_, |
| args_inserter); |
| } |
| |
| TrackId PkvmHypervisorCpuTracker::GetHypCpuTrackId(uint32_t cpu) { |
| base::StackString<255> track_name("pkVM Hypervisor CPU %d", cpu); |
| StringId track = context_->storage->InternString(track_name.string_view()); |
| return context_->track_tracker->InternCpuTrack(track, cpu); |
| } |
| |
| } // namespace trace_processor |
| } // namespace perfetto |