1 /*
2 * Copyright (C) 2024 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/etm/etm_v4_stream.h"
18
19 #include <cstdint>
20 #include <memory>
21 #include <optional>
22 #include <utility>
23
24 #include "perfetto/base/logging.h"
25 #include "perfetto/base/status.h"
26 #include "perfetto/trace_processor/trace_blob.h"
27 #include "perfetto/trace_processor/trace_blob_view.h"
28 #include "src/trace_processor/importers/etm/etm_v4_stream_demultiplexer.h"
29 #include "src/trace_processor/importers/etm/frame_decoder.h"
30 #include "src/trace_processor/importers/etm/opencsd.h"
31 #include "src/trace_processor/importers/etm/storage_handle.h"
32 #include "src/trace_processor/importers/perf/util.h"
33 #include "src/trace_processor/util/status_macros.h"
34
35 namespace perfetto::trace_processor::etm {
36 namespace {
is_raw_format(const perf_importer::AuxRecord & aux)37 bool is_raw_format(const perf_importer::AuxRecord& aux) {
38 return (aux.flags & PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW);
39 }
40 } // namespace
41
EtmV4Stream(TraceProcessorContext * context,FrameDecoder * frame_decoder,tables::EtmV4ConfigurationTable::Id config_id)42 EtmV4Stream::EtmV4Stream(TraceProcessorContext* context,
43 FrameDecoder* frame_decoder,
44 tables::EtmV4ConfigurationTable::Id config_id)
45 : context_(context), frame_decoder_(frame_decoder), config_id_(config_id) {}
46
47 EtmV4Stream::~EtmV4Stream() = default;
48
Parse(perf_importer::AuxRecord aux,TraceBlobView data)49 base::Status EtmV4Stream::Parse(perf_importer::AuxRecord aux,
50 TraceBlobView data) {
51 if (!is_raw_format(aux)) {
52 return ParseFramedData(aux.offset, std::move(data));
53 }
54 AddTrace(std::move(data));
55 return base::OkStatus();
56 }
57
ParseFramedData(uint64_t offset,TraceBlobView data)58 base::Status EtmV4Stream::ParseFramedData(uint64_t offset, TraceBlobView data) {
59 PERFETTO_CHECK(offset == index_);
60 uint32_t data_block_size;
61 PERFETTO_CHECK(perf_importer::SafeCast(data.size(), &data_block_size));
62
63 ASSIGN_OR_RETURN(
64 bool keep_going,
65 frame_decoder_->TraceDataIn(OCSD_OP_RESET, index_, 0, nullptr, nullptr));
66 PERFETTO_CHECK(keep_going);
67
68 uint32_t num_bytes_processed;
69 ASSIGN_OR_RETURN(keep_going, frame_decoder_->TraceDataIn(
70 OCSD_OP_DATA, index_, data_block_size,
71 data.data(), &num_bytes_processed));
72 PERFETTO_CHECK(keep_going);
73 PERFETTO_CHECK(num_bytes_processed == data_block_size);
74 PERFETTO_CHECK(perf_importer::SafeAdd(index_, data_block_size, &index_));
75
76 ASSIGN_OR_RETURN(keep_going, frame_decoder_->TraceDataIn(
77 OCSD_OP_EOT, index_, 0, nullptr, nullptr));
78 PERFETTO_CHECK(keep_going);
79 return base::OkStatus();
80 }
81
TraceDataIn(const ocsd_datapath_op_t op,const ocsd_trc_index_t,const uint32_t size,const uint8_t * data,uint32_t * num_bytes_processed)82 ocsd_datapath_resp_t EtmV4Stream::TraceDataIn(const ocsd_datapath_op_t op,
83 const ocsd_trc_index_t,
84 const uint32_t size,
85 const uint8_t* data,
86 uint32_t* num_bytes_processed) {
87 switch (op) {
88 case OCSD_OP_RESET:
89 StartChunkedTrace();
90 break;
91
92 case OCSD_OP_DATA:
93 WriteChunkedTrace(data, size);
94 *num_bytes_processed = size;
95 break;
96
97 case OCSD_OP_FLUSH:
98 PERFETTO_FATAL("Unreachable");
99 break;
100
101 case OCSD_OP_EOT:
102 EndChunkedTrace();
103 }
104 return OCSD_RESP_CONT;
105 }
106
OnDataLoss(uint64_t num_bytes)107 void EtmV4Stream::OnDataLoss(uint64_t num_bytes) {
108 index_ += num_bytes;
109 // No need to do anything else as we treat every AuxData as a new trace, or
110 // in the case of non raw data, the decoder is reset for each AuxData
111 }
112
NotifyEndOfStream()113 base::Status EtmV4Stream::NotifyEndOfStream() {
114 PERFETTO_CHECK(stream_active_);
115 if (session_.has_value()) {
116 EndSession();
117 }
118 stream_active_ = false;
119 return base::OkStatus();
120 }
121
OnItraceStartRecord(perf_importer::ItraceStartRecord start)122 base::Status EtmV4Stream::OnItraceStartRecord(
123 perf_importer::ItraceStartRecord start) {
124 std::optional<int64_t> start_ts;
125 if (start.time().has_value()) {
126 ASSIGN_OR_RETURN(start_ts, context_->clock_tracker->ToTraceTime(
127 start.attr->clock_id(),
128 static_cast<int64_t>(*start.time())));
129 }
130 if (session_.has_value()) {
131 EndSession();
132 }
133 StartSession(start_ts);
134 return base::OkStatus();
135 }
136
StartSession(std::optional<int64_t> start_ts)137 void EtmV4Stream::StartSession(std::optional<int64_t> start_ts) {
138 PERFETTO_CHECK(stream_active_);
139 PERFETTO_CHECK(!session_.has_value());
140 session_.emplace(context_->storage->mutable_etm_v4_session_table()
141 ->Insert({config_id_, start_ts})
142 .id);
143 }
144
AddTrace(TraceBlobView trace)145 void EtmV4Stream::AddTrace(TraceBlobView trace) {
146 PERFETTO_CHECK(session_.has_value());
147 session_->traces_.push_back(std::move(trace));
148 }
149
EndSession()150 void EtmV4Stream::EndSession() {
151 PERFETTO_CHECK(session_.has_value());
152 // There should be no inflight framed data.
153 PERFETTO_CHECK(buffer_.empty());
154 uint32_t trace_set_id = context_->storage->etm_v4_trace_table().row_count();
155 for (auto& trace : session_->traces_) {
156 if (trace.size() == 0) {
157 continue;
158 }
159 auto id = context_->storage->mutable_etm_v4_trace_table()
160 ->Insert({session_->session_id, trace_set_id,
161 static_cast<int64_t>(trace.size())})
162 .id;
163 StorageHandle(context_).StoreTrace(id, std::move(trace));
164 }
165 session_.reset();
166 }
167
StartChunkedTrace()168 void EtmV4Stream::StartChunkedTrace() {
169 PERFETTO_CHECK(buffer_.empty());
170 }
171
WriteChunkedTrace(const uint8_t * src,uint32_t size)172 void EtmV4Stream::WriteChunkedTrace(const uint8_t* src, uint32_t size) {
173 buffer_.insert(buffer_.end(), src, src + size);
174 }
175
EndChunkedTrace()176 void EtmV4Stream::EndChunkedTrace() {
177 if (buffer_.empty()) {
178 return;
179 }
180 AddTrace(TraceBlobView(TraceBlob::CopyFrom(buffer_.data(), buffer_.size())));
181 buffer_.clear();
182 }
183
184 } // namespace perfetto::trace_processor::etm
185