1 /*
2 * Copyright (C) 2019 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 "ETMDecoder.h"
18
19 #include <android-base/logging.h>
20 #include <android-base/strings.h>
21 #include <llvm/Support/MemoryBuffer.h>
22 #include <opencsd.h>
23
24 using namespace simpleperf;
25
26 namespace {
27
28 class DecoderLogStr : public ocsdMsgLogStrOutI {
29 public:
printOutStr(const std::string & out_str)30 void printOutStr(const std::string& out_str) override { LOG(INFO) << out_str; }
31 };
32
33 class DecodeErrorLogger : public ocsdDefaultErrorLogger {
34 public:
DecodeErrorLogger(const std::function<void (const ocsdError &)> & error_callback)35 DecodeErrorLogger(const std::function<void(const ocsdError&)>& error_callback)
36 : error_callback_(error_callback) {
37 initErrorLogger(OCSD_ERR_SEV_INFO, false);
38 msg_logger_.setLogOpts(ocsdMsgLogger::OUT_STR_CB);
39 msg_logger_.setStrOutFn(&log_str_);
40 setOutputLogger(&msg_logger_);
41 }
42
LogError(const ocsd_hndl_err_log_t handle,const ocsdError * error)43 void LogError(const ocsd_hndl_err_log_t handle, const ocsdError* error) override {
44 ocsdDefaultErrorLogger::LogError(handle, error);
45 if (error != nullptr) {
46 error_callback_(*error);
47 }
48 }
49
50 private:
51 std::function<void(const ocsdError&)> error_callback_;
52 DecoderLogStr log_str_;
53 ocsdMsgLogger msg_logger_;
54 };
55
IsRespError(ocsd_datapath_resp_t resp)56 static bool IsRespError(ocsd_datapath_resp_t resp) { return resp >= OCSD_RESP_ERR_CONT; }
57
58 // Used instead of DecodeTree in OpenCSD to avoid linking decoders not for ETMV4 instruction tracing
59 // in OpenCSD.
60 class ETMV4IDecodeTree {
61 public:
ETMV4IDecodeTree()62 ETMV4IDecodeTree()
63 : error_logger_(std::bind(&ETMV4IDecodeTree::ProcessError, this, std::placeholders::_1)) {
64 frame_decoder_.Configure(OCSD_DFRMTR_FRAME_MEM_ALIGN);
65 frame_decoder_.getErrLogAttachPt()->attach(&error_logger_);
66 }
67
CreateDecoder(const EtmV4Config & config)68 bool CreateDecoder(const EtmV4Config& config) {
69 uint8_t trace_id = config.getTraceID();
70 auto packet_decoder = std::make_unique<TrcPktProcEtmV4I>(trace_id);
71 packet_decoder->setProtocolConfig(&config);
72 packet_decoder->getErrorLogAttachPt()->replace_first(&error_logger_);
73 frame_decoder_.getIDStreamAttachPt(trace_id)->attach(packet_decoder.get());
74 auto result = packet_decoders_.emplace(trace_id, packet_decoder.release());
75 if (!result.second) {
76 LOG(ERROR) << "trace id " << trace_id << " has been used";
77 }
78 return result.second;
79 }
80
AttachPacketSink(uint8_t trace_id,IPktDataIn<EtmV4ITrcPacket> & packet_sink)81 void AttachPacketSink(uint8_t trace_id, IPktDataIn<EtmV4ITrcPacket>& packet_sink) {
82 auto& packet_decoder = packet_decoders_[trace_id];
83 CHECK(packet_decoder);
84 packet_decoder->getPacketOutAttachPt()->replace_first(&packet_sink);
85 }
86
AttachPacketMonitor(uint8_t trace_id,IPktRawDataMon<EtmV4ITrcPacket> & packet_monitor)87 void AttachPacketMonitor(uint8_t trace_id, IPktRawDataMon<EtmV4ITrcPacket>& packet_monitor) {
88 auto& packet_decoder = packet_decoders_[trace_id];
89 CHECK(packet_decoder);
90 packet_decoder->getRawPacketMonAttachPt()->replace_first(&packet_monitor);
91 }
92
AttachRawFramePrinter(RawFramePrinter & frame_printer)93 void AttachRawFramePrinter(RawFramePrinter& frame_printer) {
94 frame_decoder_.Configure(frame_decoder_.getConfigFlags() | OCSD_DFRMTR_PACKED_RAW_OUT);
95 frame_decoder_.getTrcRawFrameAttachPt()->replace_first(&frame_printer);
96 }
97
GetDataIn()98 ITrcDataIn& GetDataIn() { return frame_decoder_; }
99
ProcessError(const ocsdError & error)100 void ProcessError(const ocsdError& error) {
101 if (error.getErrorCode() == OCSD_ERR_INVALID_PCKT_HDR) {
102 // Found an invalid packet header, following packets for this trace id may also be invalid.
103 // So reset the decoder to find I_ASYNC packet in the data stream.
104 if (auto it = packet_decoders_.find(error.getErrorChanID()); it != packet_decoders_.end()) {
105 auto& packet_decoder = it->second;
106 CHECK(packet_decoder);
107 packet_decoder->TraceDataIn(OCSD_OP_RESET, error.getErrorIndex(), 0, nullptr, nullptr);
108 }
109 }
110 }
111
ErrorLogger()112 DecodeErrorLogger& ErrorLogger() { return error_logger_; }
113
114 private:
115 DecodeErrorLogger error_logger_;
116 TraceFormatterFrameDecoder frame_decoder_;
117 std::unordered_map<uint8_t, std::unique_ptr<TrcPktProcEtmV4I>> packet_decoders_;
118 };
119
120 // Similar to IPktDataIn<EtmV4ITrcPacket>, but add trace id.
121 struct PacketCallback {
122 // packet callbacks are called in priority order.
123 enum Priority {
124 MAP_LOCATOR,
125 PACKET_TO_ELEMENT,
126 };
127
PacketCallback__anon1bce05b70111::PacketCallback128 PacketCallback(Priority prio) : priority(prio) {}
~PacketCallback__anon1bce05b70111::PacketCallback129 virtual ~PacketCallback() {}
130 virtual ocsd_datapath_resp_t ProcessPacket(uint8_t trace_id, ocsd_datapath_op_t op,
131 ocsd_trc_index_t index_sop,
132 const EtmV4ITrcPacket* pkt) = 0;
133 const Priority priority;
134 };
135
136 // Receives packets from a packet decoder in OpenCSD library.
137 class PacketSink : public IPktDataIn<EtmV4ITrcPacket> {
138 public:
PacketSink(uint8_t trace_id)139 PacketSink(uint8_t trace_id) : trace_id_(trace_id) {}
140
AddCallback(PacketCallback * callback)141 void AddCallback(PacketCallback* callback) {
142 auto it = std::lower_bound(callbacks_.begin(), callbacks_.end(), callback,
143 [](const PacketCallback* c1, const PacketCallback* c2) {
144 return c1->priority < c2->priority;
145 });
146 callbacks_.insert(it, callback);
147 }
148
PacketDataIn(ocsd_datapath_op_t op,ocsd_trc_index_t index_sop,const EtmV4ITrcPacket * pkt)149 ocsd_datapath_resp_t PacketDataIn(ocsd_datapath_op_t op, ocsd_trc_index_t index_sop,
150 const EtmV4ITrcPacket* pkt) override {
151 for (auto& callback : callbacks_) {
152 auto resp = callback->ProcessPacket(trace_id_, op, index_sop, pkt);
153 if (IsRespError(resp)) {
154 return resp;
155 }
156 }
157 return OCSD_RESP_CONT;
158 }
159
160 private:
161 uint8_t trace_id_;
162 std::vector<PacketCallback*> callbacks_;
163 };
164
165 // For each trace_id, when given an addr, find the thread and map it belongs to.
166 class MapLocator : public PacketCallback {
167 public:
MapLocator(ThreadTree & thread_tree)168 MapLocator(ThreadTree& thread_tree)
169 : PacketCallback(PacketCallback::MAP_LOCATOR), thread_tree_(thread_tree) {}
170
GetThreadTree()171 ThreadTree& GetThreadTree() { return thread_tree_; }
172
ProcessPacket(uint8_t trace_id,ocsd_datapath_op_t op,ocsd_trc_index_t index_sop,const EtmV4ITrcPacket * pkt)173 ocsd_datapath_resp_t ProcessPacket(uint8_t trace_id, ocsd_datapath_op_t op,
174 ocsd_trc_index_t index_sop,
175 const EtmV4ITrcPacket* pkt) override {
176 TraceData& data = trace_data_[trace_id];
177 if (op == OCSD_OP_DATA) {
178 if (pkt != nullptr && pkt->getContext().updated_c) {
179 int32_t new_tid = static_cast<int32_t>(pkt->getContext().ctxtID);
180 if (data.tid != new_tid) {
181 data.tid = new_tid;
182 data.thread = nullptr;
183 data.userspace_map = nullptr;
184 }
185 }
186 } else if (op == OCSD_OP_RESET) {
187 data.tid = -1;
188 data.thread = nullptr;
189 data.userspace_map = nullptr;
190 }
191 return OCSD_RESP_CONT;
192 }
193
FindMap(uint8_t trace_id,uint64_t addr)194 const MapEntry* FindMap(uint8_t trace_id, uint64_t addr) {
195 TraceData& data = trace_data_[trace_id];
196 if (data.userspace_map != nullptr && data.userspace_map->Contains(addr)) {
197 return data.userspace_map;
198 }
199 if (data.tid == -1) {
200 return nullptr;
201 }
202 if (data.thread == nullptr) {
203 data.thread = thread_tree_.FindThread(data.tid);
204 if (data.thread == nullptr) {
205 return nullptr;
206 }
207 }
208 data.userspace_map = data.thread->maps->FindMapByAddr(addr);
209 if (data.userspace_map != nullptr) {
210 return data.userspace_map;
211 }
212 // We don't cache kernel map. Because kernel map can start from 0 and overlap all userspace
213 // maps.
214 return thread_tree_.GetKernelMaps().FindMapByAddr(addr);
215 }
216
217 private:
218 struct TraceData {
219 int32_t tid = -1; // thread id, -1 if invalid
220 const ThreadEntry* thread = nullptr;
221 const MapEntry* userspace_map = nullptr;
222 };
223
224 ThreadTree& thread_tree_;
225 TraceData trace_data_[256];
226 };
227
228 // Map (trace_id, ip address) to (binary_path, binary_offset), and read binary files.
229 class MemAccess : public ITargetMemAccess {
230 public:
MemAccess(MapLocator & map_locator)231 MemAccess(MapLocator& map_locator) : map_locator_(map_locator) {}
232
ReadTargetMemory(const ocsd_vaddr_t address,uint8_t trace_id,ocsd_mem_space_acc_t,uint32_t * num_bytes,uint8_t * p_buffer)233 ocsd_err_t ReadTargetMemory(const ocsd_vaddr_t address, uint8_t trace_id, ocsd_mem_space_acc_t,
234 uint32_t* num_bytes, uint8_t* p_buffer) override {
235 TraceData& data = trace_data_[trace_id];
236 const MapEntry* map = map_locator_.FindMap(trace_id, address);
237 // fast path
238 if (map != nullptr && map == data.buffer_map && address >= data.buffer_start &&
239 address + *num_bytes <= data.buffer_end) {
240 if (data.buffer == nullptr) {
241 *num_bytes = 0;
242 } else {
243 memcpy(p_buffer, data.buffer + (address - data.buffer_start), *num_bytes);
244 }
245 return OCSD_OK;
246 }
247
248 // slow path
249 size_t copy_size = 0;
250 if (map != nullptr) {
251 llvm::MemoryBuffer* memory = GetMemoryBuffer(map->dso);
252 if (memory != nullptr) {
253 uint64_t offset = address - map->start_addr + map->pgoff;
254 size_t file_size = memory->getBufferSize();
255 copy_size = file_size > offset ? std::min<size_t>(file_size - offset, *num_bytes) : 0;
256 if (copy_size > 0) {
257 memcpy(p_buffer, memory->getBufferStart() + offset, copy_size);
258 }
259 }
260 // Update the last buffer cache.
261 data.buffer_map = map;
262 data.buffer = memory == nullptr ? nullptr : (memory->getBufferStart() + map->pgoff);
263 data.buffer_start = map->start_addr;
264 data.buffer_end = map->get_end_addr();
265 }
266 *num_bytes = copy_size;
267 return OCSD_OK;
268 }
269
270 private:
GetMemoryBuffer(Dso * dso)271 llvm::MemoryBuffer* GetMemoryBuffer(Dso* dso) {
272 auto it = elf_map_.find(dso);
273 if (it == elf_map_.end()) {
274 ElfStatus status;
275 auto res = elf_map_.emplace(dso, ElfFile::Open(dso->GetDebugFilePath(), &status));
276 it = res.first;
277 }
278 return it->second ? it->second->GetMemoryBuffer() : nullptr;
279 }
280
281 struct TraceData {
282 const MapEntry* buffer_map = nullptr;
283 const char* buffer = nullptr;
284 uint64_t buffer_start = 0;
285 uint64_t buffer_end = 0;
286 };
287
288 MapLocator& map_locator_;
289 std::unordered_map<Dso*, std::unique_ptr<ElfFile>> elf_map_;
290 TraceData trace_data_[256];
291 };
292
293 class InstructionDecoder : public TrcIDecode {
294 public:
DecodeInstruction(ocsd_instr_info * instr_info)295 ocsd_err_t DecodeInstruction(ocsd_instr_info* instr_info) {
296 this->instr_info = instr_info;
297 return TrcIDecode::DecodeInstruction(instr_info);
298 }
299
300 ocsd_instr_info* instr_info;
301 };
302
303 // Similar to ITrcGenElemIn, but add next instruction info, which is needed to get branch to addr
304 // for an InstructionRange element.
305 struct ElementCallback {
306 public:
~ElementCallback__anon1bce05b70111::ElementCallback307 virtual ~ElementCallback(){};
308 virtual ocsd_datapath_resp_t ProcessElement(ocsd_trc_index_t index_sop, uint8_t trace_id,
309 const OcsdTraceElement& elem,
310 const ocsd_instr_info* next_instr) = 0;
311 };
312
313 // Decode packets into elements.
314 class PacketToElement : public PacketCallback, public ITrcGenElemIn {
315 public:
PacketToElement(MapLocator & map_locator,const std::unordered_map<uint8_t,EtmV4Config> & configs,DecodeErrorLogger & error_logger)316 PacketToElement(MapLocator& map_locator, const std::unordered_map<uint8_t, EtmV4Config>& configs,
317 DecodeErrorLogger& error_logger)
318 : PacketCallback(PacketCallback::PACKET_TO_ELEMENT), mem_access_(map_locator) {
319 for (auto& p : configs) {
320 uint8_t trace_id = p.first;
321 const EtmV4Config& config = p.second;
322 element_decoders_.emplace(trace_id, trace_id);
323 auto& decoder = element_decoders_[trace_id];
324 decoder.setProtocolConfig(&config);
325 decoder.getErrorLogAttachPt()->replace_first(&error_logger);
326 decoder.getInstrDecodeAttachPt()->replace_first(&instruction_decoder_);
327 decoder.getMemoryAccessAttachPt()->replace_first(&mem_access_);
328 decoder.getTraceElemOutAttachPt()->replace_first(this);
329 }
330 }
331
AddCallback(ElementCallback * callback)332 void AddCallback(ElementCallback* callback) { callbacks_.push_back(callback); }
333
ProcessPacket(uint8_t trace_id,ocsd_datapath_op_t op,ocsd_trc_index_t index_sop,const EtmV4ITrcPacket * pkt)334 ocsd_datapath_resp_t ProcessPacket(uint8_t trace_id, ocsd_datapath_op_t op,
335 ocsd_trc_index_t index_sop,
336 const EtmV4ITrcPacket* pkt) override {
337 return element_decoders_[trace_id].PacketDataIn(op, index_sop, pkt);
338 }
339
TraceElemIn(const ocsd_trc_index_t index_sop,uint8_t trc_chan_id,const OcsdTraceElement & elem)340 ocsd_datapath_resp_t TraceElemIn(const ocsd_trc_index_t index_sop, uint8_t trc_chan_id,
341 const OcsdTraceElement& elem) override {
342 for (auto& callback : callbacks_) {
343 auto resp =
344 callback->ProcessElement(index_sop, trc_chan_id, elem, instruction_decoder_.instr_info);
345 if (IsRespError(resp)) {
346 return resp;
347 }
348 }
349 return OCSD_RESP_CONT;
350 }
351
352 private:
353 // map from trace id of an etm device to its element decoder
354 std::unordered_map<uint8_t, TrcPktDecodeEtmV4I> element_decoders_;
355 MemAccess mem_access_;
356 InstructionDecoder instruction_decoder_;
357 std::vector<ElementCallback*> callbacks_;
358 };
359
360 // Dump etm data generated at different stages.
361 class DataDumper : public ElementCallback {
362 public:
DataDumper(ETMV4IDecodeTree & decode_tree)363 DataDumper(ETMV4IDecodeTree& decode_tree) : decode_tree_(decode_tree) {}
364
DumpRawData()365 void DumpRawData() {
366 decode_tree_.AttachRawFramePrinter(frame_printer_);
367 frame_printer_.setMessageLogger(&stdout_logger_);
368 }
369
DumpPackets(const std::unordered_map<uint8_t,EtmV4Config> & configs)370 void DumpPackets(const std::unordered_map<uint8_t, EtmV4Config>& configs) {
371 for (auto& p : configs) {
372 uint8_t trace_id = p.first;
373 auto result = packet_printers_.emplace(trace_id, trace_id);
374 CHECK(result.second);
375 auto& packet_printer = result.first->second;
376 decode_tree_.AttachPacketMonitor(trace_id, packet_printer);
377 packet_printer.setMessageLogger(&stdout_logger_);
378 }
379 }
380
DumpElements()381 void DumpElements() { element_printer_.setMessageLogger(&stdout_logger_); }
382
ProcessElement(ocsd_trc_index_t index_sop,uint8_t trc_chan_id,const OcsdTraceElement & elem,const ocsd_instr_info *)383 ocsd_datapath_resp_t ProcessElement(ocsd_trc_index_t index_sop, uint8_t trc_chan_id,
384 const OcsdTraceElement& elem, const ocsd_instr_info*) {
385 return element_printer_.TraceElemIn(index_sop, trc_chan_id, elem);
386 }
387
388 private:
389 ETMV4IDecodeTree& decode_tree_;
390 RawFramePrinter frame_printer_;
391 std::unordered_map<uint8_t, PacketPrinter<EtmV4ITrcPacket>> packet_printers_;
392 TrcGenericElementPrinter element_printer_;
393 ocsdMsgLogger stdout_logger_;
394 };
395
396 // It decodes each ETMV4IPacket into TraceElements, and generates ETMInstrRanges from TraceElements.
397 // Decoding each packet is slow, but ensures correctness.
398 class InstrRangeParser : public ElementCallback {
399 private:
400 struct TraceData {
401 ETMInstrRange instr_range;
402 bool wait_for_branch_to_addr_fix = false;
403 };
404
405 public:
InstrRangeParser(MapLocator & map_locator,const ETMDecoder::CallbackFn & callback)406 InstrRangeParser(MapLocator& map_locator, const ETMDecoder::CallbackFn& callback)
407 : map_locator_(map_locator), callback_(callback) {}
408
ProcessElement(const ocsd_trc_index_t,uint8_t trace_id,const OcsdTraceElement & elem,const ocsd_instr_info * next_instr)409 ocsd_datapath_resp_t ProcessElement(const ocsd_trc_index_t, uint8_t trace_id,
410 const OcsdTraceElement& elem,
411 const ocsd_instr_info* next_instr) override {
412 if (elem.getType() == OCSD_GEN_TRC_ELEM_INSTR_RANGE) {
413 TraceData& data = trace_data_[trace_id];
414 const MapEntry* map = map_locator_.FindMap(trace_id, elem.st_addr);
415 if (map == nullptr) {
416 FlushData(data);
417 return OCSD_RESP_CONT;
418 }
419 uint64_t start_addr = map->GetVaddrInFile(elem.st_addr);
420 auto& instr_range = data.instr_range;
421
422 if (data.wait_for_branch_to_addr_fix) {
423 // OpenCSD may cache a list of InstrRange elements, making it inaccurate to get branch to
424 // address from next_instr->branch_addr. So fix it by using the start address of the next
425 // InstrRange element.
426 instr_range.branch_to_addr = start_addr;
427 }
428 FlushData(data);
429 instr_range.dso = map->dso;
430 instr_range.start_addr = start_addr;
431 instr_range.end_addr = map->GetVaddrInFile(elem.en_addr - elem.last_instr_sz);
432 bool end_with_branch =
433 elem.last_i_type == OCSD_INSTR_BR || elem.last_i_type == OCSD_INSTR_BR_INDIRECT;
434 bool branch_taken = end_with_branch && elem.last_instr_exec;
435 if (elem.last_i_type == OCSD_INSTR_BR && branch_taken) {
436 // It is based on the assumption that we only do immediate branch inside a binary,
437 // which may not be true for all cases. TODO: http://b/151665001.
438 instr_range.branch_to_addr = map->GetVaddrInFile(next_instr->branch_addr);
439 data.wait_for_branch_to_addr_fix = true;
440 } else {
441 instr_range.branch_to_addr = 0;
442 }
443 instr_range.branch_taken_count = branch_taken ? 1 : 0;
444 instr_range.branch_not_taken_count = branch_taken ? 0 : 1;
445
446 } else if (elem.getType() == OCSD_GEN_TRC_ELEM_TRACE_ON) {
447 // According to the ETM Specification, the Trace On element indicates a discontinuity in the
448 // instruction trace stream. So it cuts the connection between instr ranges.
449 FlushData(trace_data_[trace_id]);
450 }
451 return OCSD_RESP_CONT;
452 }
453
FinishData()454 void FinishData() {
455 for (auto& pair : trace_data_) {
456 FlushData(pair.second);
457 }
458 }
459
460 private:
FlushData(TraceData & data)461 void FlushData(TraceData& data) {
462 if (data.instr_range.dso != nullptr) {
463 callback_(data.instr_range);
464 data.instr_range.dso = nullptr;
465 }
466 data.wait_for_branch_to_addr_fix = false;
467 }
468
469 MapLocator& map_locator_;
470 std::unordered_map<uint8_t, TraceData> trace_data_;
471 ETMDecoder::CallbackFn callback_;
472 };
473
474 // Etm data decoding in OpenCSD library has two steps:
475 // 1. From byte stream to etm packets. Each packet shows an event happened. For example,
476 // an Address packet shows the cpu is running the instruction at that address, an Atom
477 // packet shows whether the cpu decides to branch or not.
478 // 2. From etm packets to trace elements. To generates elements, the decoder needs both etm
479 // packets and executed binaries. For example, an InstructionRange element needs the decoder
480 // to find the next branch instruction starting from an address.
481 //
482 // ETMDecoderImpl uses OpenCSD library to decode etm data. It has the following properties:
483 // 1. Supports flexible decoding strategy. It allows installing packet callbacks and element
484 // callbacks, and decodes to either packets or elements based on requirements.
485 // 2. Supports dumping data at different stages.
486 class ETMDecoderImpl : public ETMDecoder {
487 public:
ETMDecoderImpl(ThreadTree & thread_tree)488 ETMDecoderImpl(ThreadTree& thread_tree) : thread_tree_(thread_tree) {}
489
CreateDecodeTree(const AuxTraceInfoRecord & auxtrace_info)490 void CreateDecodeTree(const AuxTraceInfoRecord& auxtrace_info) {
491 for (int i = 0; i < auxtrace_info.data->nr_cpu; i++) {
492 auto& etm4 = auxtrace_info.data->etm4_info[i];
493 ocsd_etmv4_cfg cfg;
494 memset(&cfg, 0, sizeof(cfg));
495 cfg.reg_idr0 = etm4.trcidr0;
496 cfg.reg_idr1 = etm4.trcidr1;
497 cfg.reg_idr2 = etm4.trcidr2;
498 cfg.reg_idr8 = etm4.trcidr8;
499 cfg.reg_configr = etm4.trcconfigr;
500 cfg.reg_traceidr = etm4.trctraceidr;
501 cfg.arch_ver = ARCH_V8;
502 cfg.core_prof = profile_CortexA;
503 uint8_t trace_id = cfg.reg_traceidr & 0x7f;
504 configs_.emplace(trace_id, &cfg);
505 decode_tree_.CreateDecoder(configs_[trace_id]);
506 auto result = packet_sinks_.emplace(trace_id, trace_id);
507 CHECK(result.second);
508 decode_tree_.AttachPacketSink(trace_id, result.first->second);
509 }
510 }
511
EnableDump(const ETMDumpOption & option)512 void EnableDump(const ETMDumpOption& option) override {
513 dumper_.reset(new DataDumper(decode_tree_));
514 if (option.dump_raw_data) {
515 dumper_->DumpRawData();
516 }
517 if (option.dump_packets) {
518 dumper_->DumpPackets(configs_);
519 }
520 if (option.dump_elements) {
521 dumper_->DumpElements();
522 InstallElementCallback(dumper_.get());
523 }
524 }
525
RegisterCallback(const CallbackFn & callback)526 void RegisterCallback(const CallbackFn& callback) {
527 InstallMapLocator();
528 instr_range_parser_.reset(new InstrRangeParser(*map_locator_, callback));
529 InstallElementCallback(instr_range_parser_.get());
530 }
531
ProcessData(const uint8_t * data,size_t size)532 bool ProcessData(const uint8_t* data, size_t size) override {
533 // Reset decoders before processing each data block. Because:
534 // 1. Data blocks are not continuous. So decoders shouldn't keep previous states when
535 // processing a new block.
536 // 2. The beginning part of a data block may be truncated if kernel buffer is temporarily full.
537 // So we may see garbage data, which can cause decoding errors if we don't reset decoders.
538 auto resp =
539 decode_tree_.GetDataIn().TraceDataIn(OCSD_OP_RESET, data_index_, 0, nullptr, nullptr);
540 if (IsRespError(resp)) {
541 LOG(ERROR) << "failed to reset decoder, resp " << resp;
542 return false;
543 }
544 size_t left_size = size;
545 while (left_size > 0) {
546 uint32_t processed;
547 auto resp = decode_tree_.GetDataIn().TraceDataIn(OCSD_OP_DATA, data_index_, left_size, data,
548 &processed);
549 if (IsRespError(resp)) {
550 // A decoding error shouldn't ruin all data. Reset decoders to recover from it.
551 LOG(INFO) << "reset etm decoders for seeing a decode failure, resp " << resp;
552 decode_tree_.GetDataIn().TraceDataIn(OCSD_OP_RESET, data_index_ + processed, 0, nullptr,
553 nullptr);
554 }
555 data += processed;
556 left_size -= processed;
557 data_index_ += processed;
558 }
559 return true;
560 }
561
FinishData()562 bool FinishData() override {
563 if (instr_range_parser_) {
564 instr_range_parser_->FinishData();
565 }
566 return true;
567 }
568
569 private:
InstallMapLocator()570 void InstallMapLocator() {
571 if (!map_locator_) {
572 map_locator_.reset(new MapLocator(thread_tree_));
573 InstallPacketCallback(map_locator_.get());
574 }
575 }
576
InstallPacketCallback(PacketCallback * callback)577 void InstallPacketCallback(PacketCallback* callback) {
578 for (auto& p : packet_sinks_) {
579 p.second.AddCallback(callback);
580 }
581 }
582
InstallElementCallback(ElementCallback * callback)583 void InstallElementCallback(ElementCallback* callback) {
584 if (!packet_to_element_) {
585 InstallMapLocator();
586 packet_to_element_.reset(
587 new PacketToElement(*map_locator_, configs_, decode_tree_.ErrorLogger()));
588 InstallPacketCallback(packet_to_element_.get());
589 }
590 packet_to_element_->AddCallback(callback);
591 }
592
593 // map ip address to binary path and binary offset
594 ThreadTree& thread_tree_;
595 // handle to build OpenCSD decoder
596 ETMV4IDecodeTree decode_tree_;
597 // map from the trace id of an etm device to its config
598 std::unordered_map<uint8_t, EtmV4Config> configs_;
599 // map from the trace id of an etm device to its PacketSink
600 std::unordered_map<uint8_t, PacketSink> packet_sinks_;
601 std::unique_ptr<PacketToElement> packet_to_element_;
602 std::unique_ptr<DataDumper> dumper_;
603 // an index keeping processed etm data size
604 size_t data_index_ = 0;
605 std::unique_ptr<InstrRangeParser> instr_range_parser_;
606 std::unique_ptr<MapLocator> map_locator_;
607 };
608
609 } // namespace
610
611 namespace simpleperf {
612
ParseEtmDumpOption(const std::string & s,ETMDumpOption * option)613 bool ParseEtmDumpOption(const std::string& s, ETMDumpOption* option) {
614 for (auto& value : android::base::Split(s, ",")) {
615 if (value == "raw") {
616 option->dump_raw_data = true;
617 } else if (value == "packet") {
618 option->dump_packets = true;
619 } else if (value == "element") {
620 option->dump_elements = true;
621 } else {
622 LOG(ERROR) << "unknown etm dump option: " << value;
623 return false;
624 }
625 }
626 return true;
627 }
628
Create(const AuxTraceInfoRecord & auxtrace_info,ThreadTree & thread_tree)629 std::unique_ptr<ETMDecoder> ETMDecoder::Create(const AuxTraceInfoRecord& auxtrace_info,
630 ThreadTree& thread_tree) {
631 auto decoder = std::make_unique<ETMDecoderImpl>(thread_tree);
632 decoder->CreateDecodeTree(auxtrace_info);
633 return std::unique_ptr<ETMDecoder>(decoder.release());
634 }
635
636 } // namespace simpleperf