xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp (revision fcaf7f8644a9988098ac6be2165bce3ea4786e91)
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_timestamps.emplace(m_item_kinds.size(), tsc);
67     m_last_tsc = tsc;
68   }
69 }
70 
71 void DecodedThread::NotifyCPU(lldb::cpu_id_t cpu_id) {
72   if (!m_last_cpu || *m_last_cpu != cpu_id) {
73     m_cpus.emplace(m_item_kinds.size(), cpu_id);
74     m_last_cpu = cpu_id;
75     AppendEvent(lldb::eTraceEventCPUChanged);
76   }
77 }
78 
79 Optional<lldb::cpu_id_t>
80 DecodedThread::GetCPUByIndex(uint64_t insn_index) const {
81   // Could possibly optimize the search
82   auto it = m_cpus.upper_bound(insn_index);
83   if (it == m_cpus.begin())
84     return None;
85   return prev(it)->second;
86 }
87 
88 void DecodedThread::AppendEvent(lldb::TraceEvent event) {
89   CreateNewTraceItem(lldb::eTraceItemKindEvent).event = event;
90   m_events_stats.RecordEvent(event);
91 }
92 
93 void DecodedThread::AppendInstruction(const pt_insn &insn) {
94   CreateNewTraceItem(lldb::eTraceItemKindInstruction).load_address = insn.ip;
95 }
96 
97 void DecodedThread::AppendError(const IntelPTError &error) {
98   // End of stream shouldn't be a public error
99   if (IsEndOfStream(error.GetLibiptErrorCode()))
100     return;
101   CreateNewTraceItem(lldb::eTraceItemKindError).error =
102       ConstString(error.message()).AsCString();
103 }
104 
105 void DecodedThread::AppendCustomError(StringRef err) {
106   CreateNewTraceItem(lldb::eTraceItemKindError).error =
107       ConstString(err).AsCString();
108 }
109 
110 lldb::TraceEvent DecodedThread::GetEventByIndex(int item_index) const {
111   return m_item_data[item_index].event;
112 }
113 
114 void DecodedThread::LibiptErrorsStats::RecordError(int libipt_error_code) {
115   libipt_errors_counts[pt_errstr(pt_errcode(libipt_error_code))]++;
116   total_count++;
117 }
118 
119 void DecodedThread::RecordTscError(int libipt_error_code) {
120   m_tsc_errors_stats.RecordError(libipt_error_code);
121 }
122 
123 const DecodedThread::LibiptErrorsStats &
124 DecodedThread::GetTscErrorsStats() const {
125   return m_tsc_errors_stats;
126 }
127 
128 const DecodedThread::EventsStats &DecodedThread::GetEventsStats() const {
129   return m_events_stats;
130 }
131 
132 void DecodedThread::EventsStats::RecordEvent(lldb::TraceEvent event) {
133   events_counts[event]++;
134   total_count++;
135 }
136 
137 Optional<DecodedThread::TscRange> DecodedThread::CalculateTscRange(
138     size_t insn_index,
139     const Optional<DecodedThread::TscRange> &hint_range) const {
140   // We first try to check the given hint range in case we are traversing the
141   // trace in short jumps. If that fails, then we do the more expensive
142   // arbitrary lookup.
143   if (hint_range) {
144     Optional<TscRange> candidate_range;
145     if (insn_index < hint_range->GetStartInstructionIndex())
146       candidate_range = hint_range->Prev();
147     else if (insn_index > hint_range->GetEndInstructionIndex())
148       candidate_range = hint_range->Next();
149     else
150       candidate_range = hint_range;
151 
152     if (candidate_range && candidate_range->InRange(insn_index))
153       return candidate_range;
154   }
155   // Now we do a more expensive lookup
156   auto it = m_timestamps.upper_bound(insn_index);
157   if (it == m_timestamps.begin())
158     return None;
159 
160   return TscRange(--it, *this);
161 }
162 
163 lldb::TraceItemKind DecodedThread::GetItemKindByIndex(size_t item_index) const {
164   return static_cast<lldb::TraceItemKind>(m_item_kinds[item_index]);
165 }
166 
167 const char *DecodedThread::GetErrorByIndex(size_t item_index) const {
168   return m_item_data[item_index].error;
169 }
170 
171 DecodedThread::DecodedThread(ThreadSP thread_sp) : m_thread_sp(thread_sp) {}
172 
173 lldb::TraceCursorUP DecodedThread::CreateNewCursor() {
174   return std::make_unique<TraceCursorIntelPT>(m_thread_sp, shared_from_this());
175 }
176 
177 size_t DecodedThread::CalculateApproximateMemoryUsage() const {
178   return sizeof(TraceItemStorage) * m_item_data.size() +
179          sizeof(uint8_t) * m_item_kinds.size() +
180          (sizeof(size_t) + sizeof(uint64_t)) * m_timestamps.size() +
181          (sizeof(size_t) + sizeof(lldb::cpu_id_t)) * m_cpus.size();
182 }
183 
184 DecodedThread::TscRange::TscRange(std::map<size_t, uint64_t>::const_iterator it,
185                                   const DecodedThread &decoded_thread)
186     : m_it(it), m_decoded_thread(&decoded_thread) {
187   auto next_it = m_it;
188   ++next_it;
189   m_end_index = (next_it == m_decoded_thread->m_timestamps.end())
190                     ? std::numeric_limits<uint64_t>::max()
191                     : next_it->first - 1;
192 }
193 
194 size_t DecodedThread::TscRange::GetTsc() const { return m_it->second; }
195 
196 size_t DecodedThread::TscRange::GetStartInstructionIndex() const {
197   return m_it->first;
198 }
199 
200 size_t DecodedThread::TscRange::GetEndInstructionIndex() const {
201   return m_end_index;
202 }
203 
204 bool DecodedThread::TscRange::InRange(size_t insn_index) const {
205   return GetStartInstructionIndex() <= insn_index &&
206          insn_index <= GetEndInstructionIndex();
207 }
208 
209 Optional<DecodedThread::TscRange> DecodedThread::TscRange::Next() const {
210   auto next_it = m_it;
211   ++next_it;
212   if (next_it == m_decoded_thread->m_timestamps.end())
213     return None;
214   return TscRange(next_it, *m_decoded_thread);
215 }
216 
217 Optional<DecodedThread::TscRange> DecodedThread::TscRange::Prev() const {
218   if (m_it == m_decoded_thread->m_timestamps.begin())
219     return None;
220   auto prev_it = m_it;
221   --prev_it;
222   return TscRange(prev_it, *m_decoded_thread);
223 }
224