|
| 1 | +/* |
| 2 | + * Copyright (C) 2026 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/proto/protovm_module.h" |
| 18 | + |
| 19 | +#include <memory> |
| 20 | +#include <string> |
| 21 | + |
| 22 | +#include "perfetto/protozero/field.h" |
| 23 | +#include "perfetto/protozero/scattered_heap_buffer.h" |
| 24 | +#include "perfetto/trace_processor/trace_blob_view.h" |
| 25 | +#include "protos/perfetto/trace/perfetto/trace_provenance.pbzero.h" |
| 26 | +#include "protos/perfetto/trace/trace_packet.pbzero.h" |
| 27 | +#include "src/trace_processor/importers/common/import_logs_tracker.h" |
| 28 | +#include "src/trace_processor/storage/stats.h" |
| 29 | +#include "src/trace_processor/types/trace_processor_context.h" |
| 30 | + |
| 31 | +namespace perfetto { |
| 32 | +namespace trace_processor { |
| 33 | + |
| 34 | +ProtoVmModule::ProtoVmModule(ProtoImporterModuleContext* context, |
| 35 | + TraceProcessorContext* trace_context) |
| 36 | + : ProtoImporterModule(context), trace_context_(trace_context) { |
| 37 | + RegisterForField(protos::pbzero::TracePacket::kTraceProvenanceFieldNumber); |
| 38 | + RegisterForField(protos::pbzero::TracePacket::kProtovmsFieldNumber); |
| 39 | + RegisterForField( |
| 40 | + protos::pbzero::TracePacket::kSurfaceflingerTransactionsFieldNumber); |
| 41 | +} |
| 42 | + |
| 43 | +ProtoVmModule::~ProtoVmModule() = default; |
| 44 | + |
| 45 | +ModuleResult ProtoVmModule::TokenizePacket( |
| 46 | + const protos::pbzero::TracePacket::Decoder& decoder, |
| 47 | + TraceBlobView* packet, |
| 48 | + int64_t packet_timestamp, |
| 49 | + RefPtr<PacketSequenceStateGeneration> state, |
| 50 | + uint32_t field_id) { |
| 51 | + if (field_id == protos::pbzero::TracePacket::kTraceProvenanceFieldNumber) { |
| 52 | + ProcessTraceProvenancePacket(decoder.trace_provenance()); |
| 53 | + return ModuleResult::Ignored(); |
| 54 | + } |
| 55 | + if (field_id == protos::pbzero::TracePacket::kProtovmsFieldNumber) { |
| 56 | + ProcessProtoVmsPacket(decoder.protovms()); |
| 57 | + return ModuleResult::Ignored(); |
| 58 | + } |
| 59 | + return TryProcessPatch(decoder, packet, packet_timestamp, state); |
| 60 | +} |
| 61 | + |
| 62 | +void ProtoVmModule::ProcessTraceProvenancePacket(protozero::ConstBytes blob) { |
| 63 | + protos::pbzero::TraceProvenance::Decoder trace_provenance(blob); |
| 64 | + for (auto it_buf = trace_provenance.buffers(); it_buf; ++it_buf) { |
| 65 | + protos::pbzero::TraceProvenance::Buffer::Decoder buffer(*it_buf); |
| 66 | + for (auto it_seq = buffer.sequences(); it_seq; ++it_seq) { |
| 67 | + protos::pbzero::TraceProvenance::Sequence::Decoder sequence(*it_seq); |
| 68 | + producer_id_to_sequence_ids_[sequence.producer_id()].push_back( |
| 69 | + sequence.id()); |
| 70 | + } |
| 71 | + } |
| 72 | +} |
| 73 | + |
| 74 | +void ProtoVmModule::ProcessProtoVmsPacket(protozero::ConstBytes blob) { |
| 75 | + protos::pbzero::TracePacket::ProtoVms::Decoder decoder(blob); |
| 76 | + for (auto it = decoder.instance(); it; ++it) { |
| 77 | + protos::pbzero::TracePacket::ProtoVms::Instance::Decoder instance(*it); |
| 78 | + protozero::ConstBytes state = instance.has_state() |
| 79 | + ? instance.state() |
| 80 | + : protozero::ConstBytes{nullptr, 0}; |
| 81 | + vms_.push_back(std::make_unique<protovm::Vm>( |
| 82 | + instance.program(), 1024 * instance.memory_limit_kb(), state)); |
| 83 | + protovm::Vm* vm = vms_.back().get(); |
| 84 | + for (auto producer_id = instance.producer_id(); producer_id; |
| 85 | + ++producer_id) { |
| 86 | + auto* sequence_ids = producer_id_to_sequence_ids_.Find(*producer_id); |
| 87 | + PERFETTO_CHECK(sequence_ids); // TODO: increment stats |
| 88 | + for (auto sequence_id : *sequence_ids) { |
| 89 | + sequence_id_to_vms_[sequence_id].push_back(vm); |
| 90 | + } |
| 91 | + } |
| 92 | + } |
| 93 | +} |
| 94 | + |
| 95 | +ModuleResult ProtoVmModule::TryProcessPatch( |
| 96 | + const protos::pbzero::TracePacket::Decoder& decoder, |
| 97 | + TraceBlobView* packet, |
| 98 | + int64_t, |
| 99 | + RefPtr<PacketSequenceStateGeneration> state) { |
| 100 | + std::vector<protovm::Vm*>* vms = |
| 101 | + sequence_id_to_vms_.Find(decoder.trusted_packet_sequence_id()); |
| 102 | + if (!vms) { |
| 103 | + return ModuleResult::Ignored(); |
| 104 | + } |
| 105 | + for (auto* vm : *vms) { |
| 106 | + auto status = vm->ApplyPatch({packet->data(), packet->size()}); |
| 107 | + if (status.IsOk()) { |
| 108 | + protozero::HeapBuffered<protozero::Message> incremental_state; |
| 109 | + TraceBlob serialized = SerializeIncrementalState(*vm, decoder); |
| 110 | + // TODO(lalitm): I suspect there will be discussions about this. FYI here |
| 111 | + // we read the timestamp from the serialized incremental state, so that a |
| 112 | + // ProtoVM's program also has the power of setting timestamps. Primiano |
| 113 | + // wanted this feature so you might want to chat directly with him. We can |
| 114 | + // always make the implementation below more efficient avoiding to |
| 115 | + // construct a full TracePacket::Decoder. |
| 116 | + protos::pbzero::TracePacket::Decoder serialized_decoder( |
| 117 | + protozero::ConstBytes{serialized.data(), serialized.size()}); |
| 118 | + auto serialized_timestamp = serialized_decoder.timestamp(); |
| 119 | + module_context_->trace_packet_stream->Push( |
| 120 | + static_cast<int64_t>(serialized_timestamp), |
| 121 | + TracePacketData{TraceBlobView{std::move(serialized)}, |
| 122 | + std::move(state)}); |
| 123 | + return ModuleResult::Handled(); |
| 124 | + } |
| 125 | + if (status.IsAbort()) { |
| 126 | + trace_context_->import_logs_tracker->RecordTokenizationError( |
| 127 | + stats::protovm_abort, packet->offset()); |
| 128 | + return ModuleResult::Handled(); |
| 129 | + } |
| 130 | + } |
| 131 | + return ModuleResult::Ignored(); |
| 132 | +} |
| 133 | + |
| 134 | +TraceBlob ProtoVmModule::SerializeIncrementalState( |
| 135 | + const protovm::Vm& vm, |
| 136 | + const protos::pbzero::TracePacket::Decoder& patch) const { |
| 137 | + protozero::HeapBuffered<protos::pbzero::TracePacket> proto; |
| 138 | + vm.SerializeIncrementalState(proto.get()); |
| 139 | + proto->set_trusted_uid(patch.trusted_uid()); |
| 140 | + proto->set_trusted_pid(patch.trusted_pid()); |
| 141 | + proto->set_trusted_packet_sequence_id(patch.trusted_packet_sequence_id()); |
| 142 | + auto [data, size] = proto.SerializeAsUniquePtr(); |
| 143 | + return TraceBlob::TakeOwnership(std::move(data), size); |
| 144 | +} |
| 145 | + |
| 146 | +} // namespace trace_processor |
| 147 | +} // namespace perfetto |
0 commit comments