1 //===-- DecodedThread.cpp -------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "DecodedThread.h"
10 
11 #include <intel-pt.h>
12 #include <memory>
13 
14 #include "TraceCursorIntelPT.h"
15 #include "lldb/Utility/StreamString.h"
16 
17 using namespace lldb;
18 using namespace lldb_private;
19 using namespace lldb_private::trace_intel_pt;
20 using namespace llvm;
21 
22 char IntelPTError::ID;
23 
24 IntelPTError::IntelPTError(int libipt_error_code, lldb::addr_t address)
25     : m_libipt_error_code(libipt_error_code), m_address(address) {
26   assert(libipt_error_code < 0);
27 }
28 
29 void IntelPTError::log(llvm::raw_ostream &OS) const {
30   const char *libipt_error_message = pt_errstr(pt_errcode(m_libipt_error_code));
31   if (m_address != LLDB_INVALID_ADDRESS && m_address > 0) {
32     write_hex(OS, m_address, HexPrintStyle::PrefixLower, 18);
33     OS << "    ";
34   }
35   OS << "error: " << libipt_error_message;
36 }
37 
38 IntelPTInstruction::IntelPTInstruction() {
39   m_pt_insn.ip = LLDB_INVALID_ADDRESS;
40   m_pt_insn.iclass = ptic_error;
41   m_is_error = true;
42 }
43 
44 bool IntelPTInstruction::IsError() const { return m_is_error; }
45 
46 lldb::addr_t IntelPTInstruction::GetLoadAddress() const { return m_pt_insn.ip; }
47 
48 size_t IntelPTInstruction::GetMemoryUsage() {
49   return sizeof(IntelPTInstruction);
50 }
51 
52 Optional<uint64_t> IntelPTInstruction::GetTimestampCounter() const {
53   return m_timestamp;
54 }
55 
56 Optional<size_t> DecodedThread::GetRawTraceSize() const {
57   return m_raw_trace_size;
58 }
59 
60 TraceInstructionControlFlowType
61 IntelPTInstruction::GetControlFlowType(lldb::addr_t next_load_address) const {
62   if (IsError())
63     return (TraceInstructionControlFlowType)0;
64 
65   TraceInstructionControlFlowType mask =
66       eTraceInstructionControlFlowTypeInstruction;
67 
68   switch (m_pt_insn.iclass) {
69   case ptic_cond_jump:
70   case ptic_jump:
71   case ptic_far_jump:
72     mask |= eTraceInstructionControlFlowTypeBranch;
73     if (m_pt_insn.ip + m_pt_insn.size != next_load_address)
74       mask |= eTraceInstructionControlFlowTypeTakenBranch;
75     break;
76   case ptic_return:
77   case ptic_far_return:
78     mask |= eTraceInstructionControlFlowTypeReturn;
79     break;
80   case ptic_call:
81   case ptic_far_call:
82     mask |= eTraceInstructionControlFlowTypeCall;
83     break;
84   default:
85     break;
86   }
87 
88   return mask;
89 }
90 
91 ThreadSP DecodedThread::GetThread() { return m_thread_sp; }
92 
93 void DecodedThread::AppendError(llvm::Error &&error) {
94   m_errors.try_emplace(m_instructions.size(), toString(std::move(error)));
95   m_instructions.emplace_back();
96 }
97 
98 ArrayRef<IntelPTInstruction> DecodedThread::GetInstructions() const {
99   return makeArrayRef(m_instructions);
100 }
101 
102 const char *DecodedThread::GetErrorByInstructionIndex(uint64_t idx) {
103   auto it = m_errors.find(idx);
104   if (it == m_errors.end())
105     return nullptr;
106 
107   return it->second.c_str();
108 }
109 
110 DecodedThread::DecodedThread(ThreadSP thread_sp) : m_thread_sp(thread_sp) {}
111 
112 DecodedThread::DecodedThread(ThreadSP thread_sp, Error &&error)
113     : m_thread_sp(thread_sp) {
114   AppendError(std::move(error));
115 }
116 
117 void DecodedThread::SetRawTraceSize(size_t size) { m_raw_trace_size = size; }
118 
119 lldb::TraceCursorUP DecodedThread::GetCursor() {
120   // We insert a fake error signaling an empty trace if needed becasue the
121   // TraceCursor requires non-empty traces.
122   if (m_instructions.empty())
123     AppendError(createStringError(inconvertibleErrorCode(), "empty trace"));
124   return std::make_unique<TraceCursorIntelPT>(m_thread_sp, shared_from_this());
125 }
126 
127 size_t DecodedThread::CalculateApproximateMemoryUsage() const {
128   return m_raw_trace_size.getValueOr(0) +
129          IntelPTInstruction::GetMemoryUsage() * m_instructions.size() +
130          m_errors.getMemorySize();
131 }
132