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 
13 #include "TraceCursorIntelPT.h"
14 
15 #include <memory>
16 
17 using namespace lldb;
18 using namespace lldb_private;
19 using namespace lldb_private::trace_intel_pt;
20 using namespace llvm;
21 
22 bool lldb_private::trace_intel_pt::IsLibiptError(int libipt_status) {
23   return libipt_status < 0;
24 }
25 
26 bool lldb_private::trace_intel_pt::IsEndOfStream(int libipt_status) {
27   return libipt_status == -pte_eos;
28 }
29 
30 bool lldb_private::trace_intel_pt::IsTscUnavailable(int libipt_status) {
31   return libipt_status == -pte_no_time;
32 }
33 
34 char IntelPTError::ID;
35 
36 IntelPTError::IntelPTError(int libipt_error_code, lldb::addr_t address)
37     : m_libipt_error_code(libipt_error_code), m_address(address) {
38   assert(libipt_error_code < 0);
39 }
40 
41 void IntelPTError::log(llvm::raw_ostream &OS) const {
42   OS << pt_errstr(pt_errcode(m_libipt_error_code));
43   if (m_address != LLDB_INVALID_ADDRESS && m_address > 0)
44     OS << formatv(": {0:x+16}", m_address);
45 }
46 
47 int64_t DecodedThread::GetItemsCount() const {
48   return static_cast<int64_t>(m_item_kinds.size());
49 }
50 
51 lldb::addr_t DecodedThread::GetInstructionLoadAddress(size_t item_index) const {
52   return m_item_data[item_index].load_address;
53 }
54 
55 ThreadSP DecodedThread::GetThread() { return m_thread_sp; }
56 
57 DecodedThread::TraceItemStorage &
58 DecodedThread::CreateNewTraceItem(lldb::TraceItemKind kind) {
59   m_item_kinds.push_back(kind);
60   m_item_data.emplace_back();
61   return m_item_data.back();
62 }
63 
64 void DecodedThread::NotifyTsc(uint64_t tsc) {
65   if (!m_last_tsc || *m_last_tsc != tsc) {
66     m_instruction_timestamps.emplace(m_item_kinds.size(), tsc);
67     m_last_tsc = tsc;
68   }
69 }
70 
71 void DecodedThread::AppendEvent(lldb::TraceEvent event) {
72   CreateNewTraceItem(lldb::eTraceItemKindEvent).event = event;
73   m_events_stats.RecordEvent(event);
74 }
75 
76 void DecodedThread::AppendInstruction(const pt_insn &insn) {
77   CreateNewTraceItem(lldb::eTraceItemKindInstruction).load_address = insn.ip;
78 }
79 
80 void DecodedThread::AppendError(const IntelPTError &error) {
81   // End of stream shouldn't be a public error
82   if (IsEndOfStream(error.GetLibiptErrorCode()))
83     return;
84   CreateNewTraceItem(lldb::eTraceItemKindError).error =
85       ConstString(error.message()).AsCString();
86 }
87 
88 void DecodedThread::AppendCustomError(StringRef err) {
89   CreateNewTraceItem(lldb::eTraceItemKindError).error =
90       ConstString(err).AsCString();
91 }
92 
93 lldb::TraceEvent DecodedThread::GetEventByIndex(int item_index) const {
94   return m_item_data[item_index].event;
95 }
96 
97 void DecodedThread::LibiptErrorsStats::RecordError(int libipt_error_code) {
98   libipt_errors_counts[pt_errstr(pt_errcode(libipt_error_code))]++;
99   total_count++;
100 }
101 
102 void DecodedThread::RecordTscError(int libipt_error_code) {
103   m_tsc_errors_stats.RecordError(libipt_error_code);
104 }
105 
106 const DecodedThread::LibiptErrorsStats &
107 DecodedThread::GetTscErrorsStats() const {
108   return m_tsc_errors_stats;
109 }
110 
111 const DecodedThread::EventsStats &DecodedThread::GetEventsStats() const {
112   return m_events_stats;
113 }
114 
115 void DecodedThread::EventsStats::RecordEvent(lldb::TraceEvent event) {
116   events_counts[event]++;
117   total_count++;
118 }
119 
120 Optional<DecodedThread::TscRange> DecodedThread::CalculateTscRange(
121     size_t insn_index,
122     const Optional<DecodedThread::TscRange> &hint_range) const {
123   // We first try to check the given hint range in case we are traversing the
124   // trace in short jumps. If that fails, then we do the more expensive
125   // arbitrary lookup.
126   if (hint_range) {
127     Optional<TscRange> candidate_range;
128     if (insn_index < hint_range->GetStartInstructionIndex())
129       candidate_range = hint_range->Prev();
130     else if (insn_index > hint_range->GetEndInstructionIndex())
131       candidate_range = hint_range->Next();
132     else
133       candidate_range = hint_range;
134 
135     if (candidate_range && candidate_range->InRange(insn_index))
136       return candidate_range;
137   }
138   // Now we do a more expensive lookup
139   auto it = m_instruction_timestamps.upper_bound(insn_index);
140   if (it == m_instruction_timestamps.begin())
141     return None;
142 
143   return TscRange(--it, *this);
144 }
145 
146 lldb::TraceItemKind DecodedThread::GetItemKindByIndex(size_t item_index) const {
147   return static_cast<lldb::TraceItemKind>(m_item_kinds[item_index]);
148 }
149 
150 const char *DecodedThread::GetErrorByIndex(size_t item_index) const {
151   return m_item_data[item_index].error;
152 }
153 
154 DecodedThread::DecodedThread(ThreadSP thread_sp) : m_thread_sp(thread_sp) {}
155 
156 lldb::TraceCursorUP DecodedThread::CreateNewCursor() {
157   return std::make_unique<TraceCursorIntelPT>(m_thread_sp, shared_from_this());
158 }
159 
160 size_t DecodedThread::CalculateApproximateMemoryUsage() const {
161   return sizeof(TraceItemStorage) * m_item_data.size() +
162          sizeof(uint8_t) * m_item_kinds.size() +
163          (sizeof(size_t) + sizeof(uint64_t)) * m_instruction_timestamps.size();
164 }
165 
166 DecodedThread::TscRange::TscRange(std::map<size_t, uint64_t>::const_iterator it,
167                                   const DecodedThread &decoded_thread)
168     : m_it(it), m_decoded_thread(&decoded_thread) {
169   auto next_it = m_it;
170   ++next_it;
171   m_end_index = (next_it == m_decoded_thread->m_instruction_timestamps.end())
172                     ? std::numeric_limits<uint64_t>::max()
173                     : next_it->first - 1;
174 }
175 
176 size_t DecodedThread::TscRange::GetTsc() const { return m_it->second; }
177 
178 size_t DecodedThread::TscRange::GetStartInstructionIndex() const {
179   return m_it->first;
180 }
181 
182 size_t DecodedThread::TscRange::GetEndInstructionIndex() const {
183   return m_end_index;
184 }
185 
186 bool DecodedThread::TscRange::InRange(size_t insn_index) const {
187   return GetStartInstructionIndex() <= insn_index &&
188          insn_index <= GetEndInstructionIndex();
189 }
190 
191 Optional<DecodedThread::TscRange> DecodedThread::TscRange::Next() const {
192   auto next_it = m_it;
193   ++next_it;
194   if (next_it == m_decoded_thread->m_instruction_timestamps.end())
195     return None;
196   return TscRange(next_it, *m_decoded_thread);
197 }
198 
199 Optional<DecodedThread::TscRange> DecodedThread::TscRange::Prev() const {
200   if (m_it == m_decoded_thread->m_instruction_timestamps.begin())
201     return None;
202   auto prev_it = m_it;
203   --prev_it;
204   return TscRange(prev_it, *m_decoded_thread);
205 }
206