xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp (revision 297eecfb02bb25902531dbb5c3b9a88caf8adf29)
1e8d8bef9SDimitry Andric //===-- DecodedThread.cpp -------------------------------------------------===//
2e8d8bef9SDimitry Andric //
3e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e8d8bef9SDimitry Andric //
7e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===//
8e8d8bef9SDimitry Andric 
9e8d8bef9SDimitry Andric #include "DecodedThread.h"
10fe6060f1SDimitry Andric #include "TraceCursorIntelPT.h"
11bdd1243dSDimitry Andric #include <intel-pt.h>
1281ad6265SDimitry Andric #include <memory>
13bdd1243dSDimitry Andric #include <optional>
14e8d8bef9SDimitry Andric 
15fe6060f1SDimitry Andric using namespace lldb;
16e8d8bef9SDimitry Andric using namespace lldb_private;
17e8d8bef9SDimitry Andric using namespace lldb_private::trace_intel_pt;
18e8d8bef9SDimitry Andric using namespace llvm;
19e8d8bef9SDimitry Andric 
20e8d8bef9SDimitry Andric char IntelPTError::ID;
21e8d8bef9SDimitry Andric 
22e8d8bef9SDimitry Andric IntelPTError::IntelPTError(int libipt_error_code, lldb::addr_t address)
23e8d8bef9SDimitry Andric     : m_libipt_error_code(libipt_error_code), m_address(address) {
24e8d8bef9SDimitry Andric   assert(libipt_error_code < 0);
25e8d8bef9SDimitry Andric }
26e8d8bef9SDimitry Andric 
27e8d8bef9SDimitry Andric void IntelPTError::log(llvm::raw_ostream &OS) const {
2881ad6265SDimitry Andric   OS << pt_errstr(pt_errcode(m_libipt_error_code));
2981ad6265SDimitry Andric   if (m_address != LLDB_INVALID_ADDRESS && m_address > 0)
3081ad6265SDimitry Andric     OS << formatv(": {0:x+16}", m_address);
31e8d8bef9SDimitry Andric }
32e8d8bef9SDimitry Andric 
33972a253aSDimitry Andric bool DecodedThread::TSCRange::InRange(uint64_t item_index) const {
34972a253aSDimitry Andric   return item_index >= first_item_index &&
35972a253aSDimitry Andric          item_index < first_item_index + items_count;
36fe6060f1SDimitry Andric }
37fe6060f1SDimitry Andric 
38972a253aSDimitry Andric bool DecodedThread::NanosecondsRange::InRange(uint64_t item_index) const {
39972a253aSDimitry Andric   return item_index >= first_item_index &&
40972a253aSDimitry Andric          item_index < first_item_index + items_count;
41972a253aSDimitry Andric }
42972a253aSDimitry Andric 
43972a253aSDimitry Andric double DecodedThread::NanosecondsRange::GetInterpolatedTime(
44972a253aSDimitry Andric     uint64_t item_index, uint64_t begin_of_time_nanos,
45972a253aSDimitry Andric     const LinuxPerfZeroTscConversion &tsc_conversion) const {
46972a253aSDimitry Andric   uint64_t items_since_last_tsc = item_index - first_item_index;
47972a253aSDimitry Andric 
48972a253aSDimitry Andric   auto interpolate = [&](uint64_t next_range_start_ns) {
49972a253aSDimitry Andric     if (next_range_start_ns == nanos) {
50972a253aSDimitry Andric       // If the resolution of the conversion formula is bad enough to consider
51972a253aSDimitry Andric       // these two timestamps as equal, then we just increase the next one by 1
52972a253aSDimitry Andric       // for correction
53972a253aSDimitry Andric       next_range_start_ns++;
54972a253aSDimitry Andric     }
55972a253aSDimitry Andric     long double item_duration =
56972a253aSDimitry Andric         static_cast<long double>(items_count) / (next_range_start_ns - nanos);
57972a253aSDimitry Andric     return (nanos - begin_of_time_nanos) + items_since_last_tsc * item_duration;
58972a253aSDimitry Andric   };
59972a253aSDimitry Andric 
60972a253aSDimitry Andric   if (!next_range) {
61972a253aSDimitry Andric     // If this is the last TSC range, so we have to extrapolate. In this case,
62972a253aSDimitry Andric     // we assume that each instruction took one TSC, which is what an
63972a253aSDimitry Andric     // instruction would take if no parallelism is achieved and the frequency
64972a253aSDimitry Andric     // multiplier is 1.
65972a253aSDimitry Andric     return interpolate(tsc_conversion.ToNanos(tsc + items_count));
66972a253aSDimitry Andric   }
67972a253aSDimitry Andric   if (items_count < (next_range->tsc - tsc)) {
68972a253aSDimitry Andric     // If the numbers of items in this range is less than the total TSC duration
69972a253aSDimitry Andric     // of this range, i.e. each instruction taking longer than 1 TSC, then we
70972a253aSDimitry Andric     // can assume that something else happened between these TSCs (e.g. a
71972a253aSDimitry Andric     // context switch, change to kernel, decoding errors, etc). In this case, we
72972a253aSDimitry Andric     // also assume that each instruction took 1 TSC. A proper way to improve
73972a253aSDimitry Andric     // this would be to analize the next events in the trace looking for context
74972a253aSDimitry Andric     // switches or trace disablement events, but for now, as we only want an
75972a253aSDimitry Andric     // approximation, we keep it simple. We are also guaranteed that the time in
76972a253aSDimitry Andric     // nanos of the next range is different to the current one, just because of
77972a253aSDimitry Andric     // the definition of a NanosecondsRange.
78972a253aSDimitry Andric     return interpolate(
79972a253aSDimitry Andric         std::min(tsc_conversion.ToNanos(tsc + items_count), next_range->nanos));
80972a253aSDimitry Andric   }
81972a253aSDimitry Andric 
82972a253aSDimitry Andric   // In this case, each item took less than 1 TSC, so some parallelism was
83972a253aSDimitry Andric   // achieved, which is an indication that we didn't suffered of any kind of
84972a253aSDimitry Andric   // interruption.
85972a253aSDimitry Andric   return interpolate(next_range->nanos);
86972a253aSDimitry Andric }
87972a253aSDimitry Andric 
88*297eecfbSDimitry Andric uint64_t DecodedThread::GetItemsCount() const { return m_item_data.size(); }
89972a253aSDimitry Andric 
90972a253aSDimitry Andric lldb::addr_t
91972a253aSDimitry Andric DecodedThread::GetInstructionLoadAddress(uint64_t item_index) const {
92*297eecfbSDimitry Andric   return std::get<lldb::addr_t>(m_item_data[item_index]);
93e8d8bef9SDimitry Andric }
94e8d8bef9SDimitry Andric 
95bdd1243dSDimitry Andric lldb::addr_t
96bdd1243dSDimitry Andric DecodedThread::GetSyncPointOffsetByIndex(uint64_t item_index) const {
97bdd1243dSDimitry Andric   return m_psb_offsets.find(item_index)->second;
98bdd1243dSDimitry Andric }
99bdd1243dSDimitry Andric 
10081ad6265SDimitry Andric ThreadSP DecodedThread::GetThread() { return m_thread_sp; }
101e8d8bef9SDimitry Andric 
102*297eecfbSDimitry Andric template <typename Data>
10381ad6265SDimitry Andric DecodedThread::TraceItemStorage &
104*297eecfbSDimitry Andric DecodedThread::CreateNewTraceItem(lldb::TraceItemKind kind, Data &&data) {
105*297eecfbSDimitry Andric   m_item_data.emplace_back(data);
106*297eecfbSDimitry Andric 
107972a253aSDimitry Andric   if (m_last_tsc)
108972a253aSDimitry Andric     (*m_last_tsc)->second.items_count++;
109972a253aSDimitry Andric   if (m_last_nanoseconds)
110972a253aSDimitry Andric     (*m_last_nanoseconds)->second.items_count++;
111*297eecfbSDimitry Andric 
11281ad6265SDimitry Andric   return m_item_data.back();
113fe6060f1SDimitry Andric }
114fe6060f1SDimitry Andric 
115bdd1243dSDimitry Andric void DecodedThread::NotifySyncPoint(lldb::addr_t psb_offset) {
116bdd1243dSDimitry Andric   m_psb_offsets.try_emplace(GetItemsCount(), psb_offset);
117bdd1243dSDimitry Andric   AppendEvent(lldb::eTraceEventSyncPoint);
118bdd1243dSDimitry Andric }
119bdd1243dSDimitry Andric 
120972a253aSDimitry Andric void DecodedThread::NotifyTsc(TSC tsc) {
121972a253aSDimitry Andric   if (m_last_tsc && (*m_last_tsc)->second.tsc == tsc)
122972a253aSDimitry Andric     return;
123bdd1243dSDimitry Andric   if (m_last_tsc)
124bdd1243dSDimitry Andric     assert(tsc >= (*m_last_tsc)->second.tsc &&
125bdd1243dSDimitry Andric            "We can't have decreasing times");
126972a253aSDimitry Andric 
127972a253aSDimitry Andric   m_last_tsc =
128972a253aSDimitry Andric       m_tscs.emplace(GetItemsCount(), TSCRange{tsc, 0, GetItemsCount()}).first;
129972a253aSDimitry Andric 
130972a253aSDimitry Andric   if (m_tsc_conversion) {
131972a253aSDimitry Andric     uint64_t nanos = m_tsc_conversion->ToNanos(tsc);
132972a253aSDimitry Andric     if (!m_last_nanoseconds || (*m_last_nanoseconds)->second.nanos != nanos) {
133972a253aSDimitry Andric       m_last_nanoseconds =
134972a253aSDimitry Andric           m_nanoseconds
135972a253aSDimitry Andric               .emplace(GetItemsCount(), NanosecondsRange{nanos, tsc, nullptr, 0,
136972a253aSDimitry Andric                                                          GetItemsCount()})
137972a253aSDimitry Andric               .first;
138972a253aSDimitry Andric       if (*m_last_nanoseconds != m_nanoseconds.begin()) {
139972a253aSDimitry Andric         auto prev_range = prev(*m_last_nanoseconds);
140972a253aSDimitry Andric         prev_range->second.next_range = &(*m_last_nanoseconds)->second;
14181ad6265SDimitry Andric       }
142e8d8bef9SDimitry Andric     }
143972a253aSDimitry Andric   }
144972a253aSDimitry Andric   AppendEvent(lldb::eTraceEventHWClockTick);
145972a253aSDimitry Andric }
146e8d8bef9SDimitry Andric 
147753f127fSDimitry Andric void DecodedThread::NotifyCPU(lldb::cpu_id_t cpu_id) {
148753f127fSDimitry Andric   if (!m_last_cpu || *m_last_cpu != cpu_id) {
149972a253aSDimitry Andric     m_cpus.emplace(GetItemsCount(), cpu_id);
150753f127fSDimitry Andric     m_last_cpu = cpu_id;
151753f127fSDimitry Andric     AppendEvent(lldb::eTraceEventCPUChanged);
152753f127fSDimitry Andric   }
153753f127fSDimitry Andric }
154753f127fSDimitry Andric 
155bdd1243dSDimitry Andric lldb::cpu_id_t DecodedThread::GetCPUByIndex(uint64_t item_index) const {
156972a253aSDimitry Andric   auto it = m_cpus.upper_bound(item_index);
157bdd1243dSDimitry Andric   return it == m_cpus.begin() ? LLDB_INVALID_CPU_ID : prev(it)->second;
158753f127fSDimitry Andric }
159753f127fSDimitry Andric 
160bdd1243dSDimitry Andric std::optional<DecodedThread::TSCRange>
161972a253aSDimitry Andric DecodedThread::GetTSCRangeByIndex(uint64_t item_index) const {
162972a253aSDimitry Andric   auto next_it = m_tscs.upper_bound(item_index);
163972a253aSDimitry Andric   if (next_it == m_tscs.begin())
164bdd1243dSDimitry Andric     return std::nullopt;
165972a253aSDimitry Andric   return prev(next_it)->second;
166972a253aSDimitry Andric }
167972a253aSDimitry Andric 
168bdd1243dSDimitry Andric std::optional<DecodedThread::NanosecondsRange>
169972a253aSDimitry Andric DecodedThread::GetNanosecondsRangeByIndex(uint64_t item_index) {
170972a253aSDimitry Andric   auto next_it = m_nanoseconds.upper_bound(item_index);
171972a253aSDimitry Andric   if (next_it == m_nanoseconds.begin())
172bdd1243dSDimitry Andric     return std::nullopt;
173972a253aSDimitry Andric   return prev(next_it)->second;
174972a253aSDimitry Andric }
175972a253aSDimitry Andric 
176bdd1243dSDimitry Andric uint64_t DecodedThread::GetTotalInstructionCount() const {
177bdd1243dSDimitry Andric   return m_insn_count;
178bdd1243dSDimitry Andric }
179bdd1243dSDimitry Andric 
18081ad6265SDimitry Andric void DecodedThread::AppendEvent(lldb::TraceEvent event) {
181*297eecfbSDimitry Andric   CreateNewTraceItem(lldb::eTraceItemKindEvent, event);
18281ad6265SDimitry Andric   m_events_stats.RecordEvent(event);
183e8d8bef9SDimitry Andric }
184e8d8bef9SDimitry Andric 
18581ad6265SDimitry Andric void DecodedThread::AppendInstruction(const pt_insn &insn) {
186*297eecfbSDimitry Andric   CreateNewTraceItem(lldb::eTraceItemKindInstruction, insn.ip);
187bdd1243dSDimitry Andric   m_insn_count++;
188fe6060f1SDimitry Andric }
189e8d8bef9SDimitry Andric 
19081ad6265SDimitry Andric void DecodedThread::AppendError(const IntelPTError &error) {
191*297eecfbSDimitry Andric   CreateNewTraceItem(lldb::eTraceItemKindError, error.message());
192bdd1243dSDimitry Andric   m_error_stats.RecordError(/*fatal=*/false);
193fe6060f1SDimitry Andric }
194fe6060f1SDimitry Andric 
195bdd1243dSDimitry Andric void DecodedThread::AppendCustomError(StringRef err, bool fatal) {
196*297eecfbSDimitry Andric   CreateNewTraceItem(lldb::eTraceItemKindError, err.str());
197bdd1243dSDimitry Andric   m_error_stats.RecordError(fatal);
19881ad6265SDimitry Andric }
19981ad6265SDimitry Andric 
20081ad6265SDimitry Andric lldb::TraceEvent DecodedThread::GetEventByIndex(int item_index) const {
201*297eecfbSDimitry Andric   return std::get<lldb::TraceEvent>(m_item_data[item_index]);
20281ad6265SDimitry Andric }
20381ad6265SDimitry Andric 
20481ad6265SDimitry Andric const DecodedThread::EventsStats &DecodedThread::GetEventsStats() const {
20581ad6265SDimitry Andric   return m_events_stats;
20681ad6265SDimitry Andric }
20781ad6265SDimitry Andric 
20881ad6265SDimitry Andric void DecodedThread::EventsStats::RecordEvent(lldb::TraceEvent event) {
20981ad6265SDimitry Andric   events_counts[event]++;
21081ad6265SDimitry Andric   total_count++;
21181ad6265SDimitry Andric }
21281ad6265SDimitry Andric 
213bdd1243dSDimitry Andric uint64_t DecodedThread::ErrorStats::GetTotalCount() const {
214bdd1243dSDimitry Andric   uint64_t total = 0;
215bdd1243dSDimitry Andric   for (const auto &[kind, count] : libipt_errors)
216bdd1243dSDimitry Andric     total += count;
217bdd1243dSDimitry Andric 
218bdd1243dSDimitry Andric   return total + other_errors + fatal_errors;
219bdd1243dSDimitry Andric }
220bdd1243dSDimitry Andric 
221bdd1243dSDimitry Andric void DecodedThread::ErrorStats::RecordError(bool fatal) {
222bdd1243dSDimitry Andric   if (fatal)
223bdd1243dSDimitry Andric     fatal_errors++;
224bdd1243dSDimitry Andric   else
225bdd1243dSDimitry Andric     other_errors++;
226bdd1243dSDimitry Andric }
227bdd1243dSDimitry Andric 
228bdd1243dSDimitry Andric void DecodedThread::ErrorStats::RecordError(int libipt_error_code) {
229bdd1243dSDimitry Andric   libipt_errors[pt_errstr(pt_errcode(libipt_error_code))]++;
230bdd1243dSDimitry Andric }
231bdd1243dSDimitry Andric 
232bdd1243dSDimitry Andric const DecodedThread::ErrorStats &DecodedThread::GetErrorStats() const {
233bdd1243dSDimitry Andric   return m_error_stats;
234bdd1243dSDimitry Andric }
235bdd1243dSDimitry Andric 
236972a253aSDimitry Andric lldb::TraceItemKind
237972a253aSDimitry Andric DecodedThread::GetItemKindByIndex(uint64_t item_index) const {
238*297eecfbSDimitry Andric   return std::visit(
239*297eecfbSDimitry Andric       llvm::makeVisitor(
240*297eecfbSDimitry Andric           [](const std::string &) { return lldb::eTraceItemKindError; },
241*297eecfbSDimitry Andric           [](lldb::TraceEvent) { return lldb::eTraceItemKindEvent; },
242*297eecfbSDimitry Andric           [](lldb::addr_t) { return lldb::eTraceItemKindInstruction; }),
243*297eecfbSDimitry Andric       m_item_data[item_index]);
24481ad6265SDimitry Andric }
24581ad6265SDimitry Andric 
24606c3fb27SDimitry Andric llvm::StringRef DecodedThread::GetErrorByIndex(uint64_t item_index) const {
24706c3fb27SDimitry Andric   if (item_index >= m_item_data.size())
24806c3fb27SDimitry Andric     return llvm::StringRef();
249*297eecfbSDimitry Andric   return std::get<std::string>(m_item_data[item_index]);
25081ad6265SDimitry Andric }
25181ad6265SDimitry Andric 
252972a253aSDimitry Andric DecodedThread::DecodedThread(
253972a253aSDimitry Andric     ThreadSP thread_sp,
254bdd1243dSDimitry Andric     const std::optional<LinuxPerfZeroTscConversion> &tsc_conversion)
255972a253aSDimitry Andric     : m_thread_sp(thread_sp), m_tsc_conversion(tsc_conversion) {}
25681ad6265SDimitry Andric 
25781ad6265SDimitry Andric size_t DecodedThread::CalculateApproximateMemoryUsage() const {
25881ad6265SDimitry Andric   return sizeof(TraceItemStorage) * m_item_data.size() +
259972a253aSDimitry Andric          (sizeof(uint64_t) + sizeof(TSC)) * m_tscs.size() +
260972a253aSDimitry Andric          (sizeof(uint64_t) + sizeof(uint64_t)) * m_nanoseconds.size() +
261972a253aSDimitry Andric          (sizeof(uint64_t) + sizeof(lldb::cpu_id_t)) * m_cpus.size();
26281ad6265SDimitry Andric }
263