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(index_ <= std::numeric_limits<decltype(index_)>::max() -
75 data_block_size);
76 index_ += data_block_size;
77
78 ASSIGN_OR_RETURN(keep_going, frame_decoder_->TraceDataIn(
79 OCSD_OP_EOT, index_, 0, nullptr, nullptr));
80 PERFETTO_CHECK(keep_going);
81 return base::OkStatus();
82 }
83
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)84 ocsd_datapath_resp_t EtmV4Stream::TraceDataIn(const ocsd_datapath_op_t op,
85 const ocsd_trc_index_t,
86 const uint32_t size,
87 const uint8_t* data,
88 uint32_t* num_bytes_processed) {
89 switch (op) {
90 case OCSD_OP_RESET:
91 StartChunkedTrace();
92 break;
93
94 case OCSD_OP_DATA:
95 WriteChunkedTrace(data, size);
96 *num_bytes_processed = size;
97 break;
98
99 case OCSD_OP_FLUSH:
100 PERFETTO_FATAL("Unreachable");
101 break;
102
103 case OCSD_OP_EOT:
104 EndChunkedTrace();
105 }
106 return OCSD_RESP_CONT;
107 }
108
OnDataLoss(uint64_t num_bytes)109 void EtmV4Stream::OnDataLoss(uint64_t num_bytes) {
110 index_ += num_bytes;
111 // No need to do anything else as we treat every AuxData as a new trace, or
112 // in the case of non raw data, the decoder is reset for each AuxData
113 }
114
NotifyEndOfStream()115 base::Status EtmV4Stream::NotifyEndOfStream() {
116 PERFETTO_CHECK(stream_active_);
117 if (session_.has_value()) {
118 EndSession();
119 }
120 stream_active_ = false;
121 return base::OkStatus();
122 }
123
OnItraceStartRecord(perf_importer::ItraceStartRecord start)124 base::Status EtmV4Stream::OnItraceStartRecord(
125 perf_importer::ItraceStartRecord start) {
126 std::optional<int64_t> start_ts;
127 if (start.time().has_value()) {
128 ASSIGN_OR_RETURN(start_ts, context_->clock_tracker->ToTraceTime(
129 start.attr->clock_id(),
130 static_cast<int64_t>(*start.time())));
131 }
132 if (session_.has_value()) {
133 EndSession();
134 }
135 StartSession(start_ts);
136 return base::OkStatus();
137 }
138
StartSession(std::optional<int64_t> start_ts)139 void EtmV4Stream::StartSession(std::optional<int64_t> start_ts) {
140 PERFETTO_CHECK(stream_active_);
141 PERFETTO_CHECK(!session_.has_value());
142 session_.emplace(context_->storage->mutable_etm_v4_session_table()
143 ->Insert({config_id_, start_ts})
144 .id);
145 }
146
AddTrace(TraceBlobView trace)147 void EtmV4Stream::AddTrace(TraceBlobView trace) {
148 PERFETTO_CHECK(session_.has_value());
149 session_->traces_.push_back(std::move(trace));
150 }
151
EndSession()152 void EtmV4Stream::EndSession() {
153 PERFETTO_CHECK(session_.has_value());
154 // There should be no inflight framed data.
155 PERFETTO_CHECK(buffer_.empty());
156 uint32_t trace_set_id = context_->storage->etm_v4_trace_table().row_count();
157 for (auto& trace : session_->traces_) {
158 if (trace.size() == 0) {
159 continue;
160 }
161 auto id = context_->storage->mutable_etm_v4_trace_table()
162 ->Insert({session_->session_id, trace_set_id,
163 static_cast<int64_t>(trace.size())})
164 .id;
165 StorageHandle(context_).StoreTrace(id, std::move(trace));
166 }
167 session_.reset();
168 }
169
StartChunkedTrace()170 void EtmV4Stream::StartChunkedTrace() {
171 PERFETTO_CHECK(buffer_.empty());
172 }
173
WriteChunkedTrace(const uint8_t * src,uint32_t size)174 void EtmV4Stream::WriteChunkedTrace(const uint8_t* src, uint32_t size) {
175 buffer_.insert(buffer_.end(), src, src + size);
176 }
177
EndChunkedTrace()178 void EtmV4Stream::EndChunkedTrace() {
179 if (buffer_.empty()) {
180 return;
181 }
182 AddTrace(TraceBlobView(TraceBlob::CopyFrom(buffer_.data(), buffer_.size())));
183 buffer_.clear();
184 }
185
186 } // namespace perfetto::trace_processor::etm
187