xref: /openbsd-src/gnu/llvm/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1be691f3bSpatrick //===-- DecodedThread.cpp -------------------------------------------------===//
2be691f3bSpatrick //
3be691f3bSpatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4be691f3bSpatrick // See https://llvm.org/LICENSE.txt for license information.
5be691f3bSpatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6be691f3bSpatrick //
7be691f3bSpatrick //===----------------------------------------------------------------------===//
8be691f3bSpatrick 
9be691f3bSpatrick #include "DecodedThread.h"
10*f6aab3d8Srobert #include "TraceCursorIntelPT.h"
11be691f3bSpatrick #include <intel-pt.h>
12be691f3bSpatrick #include <memory>
13*f6aab3d8Srobert #include <optional>
14be691f3bSpatrick 
15be691f3bSpatrick using namespace lldb;
16be691f3bSpatrick using namespace lldb_private;
17be691f3bSpatrick using namespace lldb_private::trace_intel_pt;
18be691f3bSpatrick using namespace llvm;
19be691f3bSpatrick 
20be691f3bSpatrick char IntelPTError::ID;
21be691f3bSpatrick 
IntelPTError(int libipt_error_code,lldb::addr_t address)22be691f3bSpatrick IntelPTError::IntelPTError(int libipt_error_code, lldb::addr_t address)
23be691f3bSpatrick     : m_libipt_error_code(libipt_error_code), m_address(address) {
24be691f3bSpatrick   assert(libipt_error_code < 0);
25be691f3bSpatrick }
26be691f3bSpatrick 
log(llvm::raw_ostream & OS) const27be691f3bSpatrick void IntelPTError::log(llvm::raw_ostream &OS) const {
28*f6aab3d8Srobert   OS << pt_errstr(pt_errcode(m_libipt_error_code));
29*f6aab3d8Srobert   if (m_address != LLDB_INVALID_ADDRESS && m_address > 0)
30*f6aab3d8Srobert     OS << formatv(": {0:x+16}", m_address);
31be691f3bSpatrick }
32be691f3bSpatrick 
InRange(uint64_t item_index) const33*f6aab3d8Srobert bool DecodedThread::TSCRange::InRange(uint64_t item_index) const {
34*f6aab3d8Srobert   return item_index >= first_item_index &&
35*f6aab3d8Srobert          item_index < first_item_index + items_count;
36be691f3bSpatrick }
37be691f3bSpatrick 
InRange(uint64_t item_index) const38*f6aab3d8Srobert bool DecodedThread::NanosecondsRange::InRange(uint64_t item_index) const {
39*f6aab3d8Srobert   return item_index >= first_item_index &&
40*f6aab3d8Srobert          item_index < first_item_index + items_count;
41be691f3bSpatrick }
42be691f3bSpatrick 
GetInterpolatedTime(uint64_t item_index,uint64_t begin_of_time_nanos,const LinuxPerfZeroTscConversion & tsc_conversion) const43*f6aab3d8Srobert double DecodedThread::NanosecondsRange::GetInterpolatedTime(
44*f6aab3d8Srobert     uint64_t item_index, uint64_t begin_of_time_nanos,
45*f6aab3d8Srobert     const LinuxPerfZeroTscConversion &tsc_conversion) const {
46*f6aab3d8Srobert   uint64_t items_since_last_tsc = item_index - first_item_index;
47be691f3bSpatrick 
48*f6aab3d8Srobert   auto interpolate = [&](uint64_t next_range_start_ns) {
49*f6aab3d8Srobert     if (next_range_start_ns == nanos) {
50*f6aab3d8Srobert       // If the resolution of the conversion formula is bad enough to consider
51*f6aab3d8Srobert       // these two timestamps as equal, then we just increase the next one by 1
52*f6aab3d8Srobert       // for correction
53*f6aab3d8Srobert       next_range_start_ns++;
54be691f3bSpatrick     }
55*f6aab3d8Srobert     long double item_duration =
56*f6aab3d8Srobert         static_cast<long double>(items_count) / (next_range_start_ns - nanos);
57*f6aab3d8Srobert     return (nanos - begin_of_time_nanos) + items_since_last_tsc * item_duration;
58*f6aab3d8Srobert   };
59be691f3bSpatrick 
60*f6aab3d8Srobert   if (!next_range) {
61*f6aab3d8Srobert     // If this is the last TSC range, so we have to extrapolate. In this case,
62*f6aab3d8Srobert     // we assume that each instruction took one TSC, which is what an
63*f6aab3d8Srobert     // instruction would take if no parallelism is achieved and the frequency
64*f6aab3d8Srobert     // multiplier is 1.
65*f6aab3d8Srobert     return interpolate(tsc_conversion.ToNanos(tsc + items_count));
66*f6aab3d8Srobert   }
67*f6aab3d8Srobert   if (items_count < (next_range->tsc - tsc)) {
68*f6aab3d8Srobert     // If the numbers of items in this range is less than the total TSC duration
69*f6aab3d8Srobert     // of this range, i.e. each instruction taking longer than 1 TSC, then we
70*f6aab3d8Srobert     // can assume that something else happened between these TSCs (e.g. a
71*f6aab3d8Srobert     // context switch, change to kernel, decoding errors, etc). In this case, we
72*f6aab3d8Srobert     // also assume that each instruction took 1 TSC. A proper way to improve
73*f6aab3d8Srobert     // this would be to analize the next events in the trace looking for context
74*f6aab3d8Srobert     // switches or trace disablement events, but for now, as we only want an
75*f6aab3d8Srobert     // approximation, we keep it simple. We are also guaranteed that the time in
76*f6aab3d8Srobert     // nanos of the next range is different to the current one, just because of
77*f6aab3d8Srobert     // the definition of a NanosecondsRange.
78*f6aab3d8Srobert     return interpolate(
79*f6aab3d8Srobert         std::min(tsc_conversion.ToNanos(tsc + items_count), next_range->nanos));
80be691f3bSpatrick   }
81be691f3bSpatrick 
82*f6aab3d8Srobert   // In this case, each item took less than 1 TSC, so some parallelism was
83*f6aab3d8Srobert   // achieved, which is an indication that we didn't suffered of any kind of
84*f6aab3d8Srobert   // interruption.
85*f6aab3d8Srobert   return interpolate(next_range->nanos);
86be691f3bSpatrick }
87be691f3bSpatrick 
GetItemsCount() const88*f6aab3d8Srobert uint64_t DecodedThread::GetItemsCount() const { return m_item_kinds.size(); }
89*f6aab3d8Srobert 
90*f6aab3d8Srobert lldb::addr_t
GetInstructionLoadAddress(uint64_t item_index) const91*f6aab3d8Srobert DecodedThread::GetInstructionLoadAddress(uint64_t item_index) const {
92*f6aab3d8Srobert   return m_item_data[item_index].load_address;
93be691f3bSpatrick }
94be691f3bSpatrick 
95*f6aab3d8Srobert lldb::addr_t
GetSyncPointOffsetByIndex(uint64_t item_index) const96*f6aab3d8Srobert DecodedThread::GetSyncPointOffsetByIndex(uint64_t item_index) const {
97*f6aab3d8Srobert   return m_psb_offsets.find(item_index)->second;
98be691f3bSpatrick }
99be691f3bSpatrick 
GetThread()100*f6aab3d8Srobert ThreadSP DecodedThread::GetThread() { return m_thread_sp; }
101*f6aab3d8Srobert 
102*f6aab3d8Srobert DecodedThread::TraceItemStorage &
CreateNewTraceItem(lldb::TraceItemKind kind)103*f6aab3d8Srobert DecodedThread::CreateNewTraceItem(lldb::TraceItemKind kind) {
104*f6aab3d8Srobert   m_item_kinds.push_back(kind);
105*f6aab3d8Srobert   m_item_data.emplace_back();
106*f6aab3d8Srobert   if (m_last_tsc)
107*f6aab3d8Srobert     (*m_last_tsc)->second.items_count++;
108*f6aab3d8Srobert   if (m_last_nanoseconds)
109*f6aab3d8Srobert     (*m_last_nanoseconds)->second.items_count++;
110*f6aab3d8Srobert   return m_item_data.back();
111be691f3bSpatrick }
112be691f3bSpatrick 
NotifySyncPoint(lldb::addr_t psb_offset)113*f6aab3d8Srobert void DecodedThread::NotifySyncPoint(lldb::addr_t psb_offset) {
114*f6aab3d8Srobert   m_psb_offsets.try_emplace(GetItemsCount(), psb_offset);
115*f6aab3d8Srobert   AppendEvent(lldb::eTraceEventSyncPoint);
116*f6aab3d8Srobert }
117*f6aab3d8Srobert 
NotifyTsc(TSC tsc)118*f6aab3d8Srobert void DecodedThread::NotifyTsc(TSC tsc) {
119*f6aab3d8Srobert   if (m_last_tsc && (*m_last_tsc)->second.tsc == tsc)
120*f6aab3d8Srobert     return;
121*f6aab3d8Srobert   if (m_last_tsc)
122*f6aab3d8Srobert     assert(tsc >= (*m_last_tsc)->second.tsc &&
123*f6aab3d8Srobert            "We can't have decreasing times");
124*f6aab3d8Srobert 
125*f6aab3d8Srobert   m_last_tsc =
126*f6aab3d8Srobert       m_tscs.emplace(GetItemsCount(), TSCRange{tsc, 0, GetItemsCount()}).first;
127*f6aab3d8Srobert 
128*f6aab3d8Srobert   if (m_tsc_conversion) {
129*f6aab3d8Srobert     uint64_t nanos = m_tsc_conversion->ToNanos(tsc);
130*f6aab3d8Srobert     if (!m_last_nanoseconds || (*m_last_nanoseconds)->second.nanos != nanos) {
131*f6aab3d8Srobert       m_last_nanoseconds =
132*f6aab3d8Srobert           m_nanoseconds
133*f6aab3d8Srobert               .emplace(GetItemsCount(), NanosecondsRange{nanos, tsc, nullptr, 0,
134*f6aab3d8Srobert                                                          GetItemsCount()})
135*f6aab3d8Srobert               .first;
136*f6aab3d8Srobert       if (*m_last_nanoseconds != m_nanoseconds.begin()) {
137*f6aab3d8Srobert         auto prev_range = prev(*m_last_nanoseconds);
138*f6aab3d8Srobert         prev_range->second.next_range = &(*m_last_nanoseconds)->second;
139*f6aab3d8Srobert       }
140*f6aab3d8Srobert     }
141*f6aab3d8Srobert   }
142*f6aab3d8Srobert   AppendEvent(lldb::eTraceEventHWClockTick);
143*f6aab3d8Srobert }
144*f6aab3d8Srobert 
NotifyCPU(lldb::cpu_id_t cpu_id)145*f6aab3d8Srobert void DecodedThread::NotifyCPU(lldb::cpu_id_t cpu_id) {
146*f6aab3d8Srobert   if (!m_last_cpu || *m_last_cpu != cpu_id) {
147*f6aab3d8Srobert     m_cpus.emplace(GetItemsCount(), cpu_id);
148*f6aab3d8Srobert     m_last_cpu = cpu_id;
149*f6aab3d8Srobert     AppendEvent(lldb::eTraceEventCPUChanged);
150*f6aab3d8Srobert   }
151*f6aab3d8Srobert }
152*f6aab3d8Srobert 
GetCPUByIndex(uint64_t item_index) const153*f6aab3d8Srobert lldb::cpu_id_t DecodedThread::GetCPUByIndex(uint64_t item_index) const {
154*f6aab3d8Srobert   auto it = m_cpus.upper_bound(item_index);
155*f6aab3d8Srobert   return it == m_cpus.begin() ? LLDB_INVALID_CPU_ID : prev(it)->second;
156*f6aab3d8Srobert }
157*f6aab3d8Srobert 
158*f6aab3d8Srobert std::optional<DecodedThread::TSCRange>
GetTSCRangeByIndex(uint64_t item_index) const159*f6aab3d8Srobert DecodedThread::GetTSCRangeByIndex(uint64_t item_index) const {
160*f6aab3d8Srobert   auto next_it = m_tscs.upper_bound(item_index);
161*f6aab3d8Srobert   if (next_it == m_tscs.begin())
162*f6aab3d8Srobert     return std::nullopt;
163*f6aab3d8Srobert   return prev(next_it)->second;
164*f6aab3d8Srobert }
165*f6aab3d8Srobert 
166*f6aab3d8Srobert std::optional<DecodedThread::NanosecondsRange>
GetNanosecondsRangeByIndex(uint64_t item_index)167*f6aab3d8Srobert DecodedThread::GetNanosecondsRangeByIndex(uint64_t item_index) {
168*f6aab3d8Srobert   auto next_it = m_nanoseconds.upper_bound(item_index);
169*f6aab3d8Srobert   if (next_it == m_nanoseconds.begin())
170*f6aab3d8Srobert     return std::nullopt;
171*f6aab3d8Srobert   return prev(next_it)->second;
172*f6aab3d8Srobert }
173*f6aab3d8Srobert 
GetTotalInstructionCount() const174*f6aab3d8Srobert uint64_t DecodedThread::GetTotalInstructionCount() const {
175*f6aab3d8Srobert   return m_insn_count;
176*f6aab3d8Srobert }
177*f6aab3d8Srobert 
AppendEvent(lldb::TraceEvent event)178*f6aab3d8Srobert void DecodedThread::AppendEvent(lldb::TraceEvent event) {
179*f6aab3d8Srobert   CreateNewTraceItem(lldb::eTraceItemKindEvent).event = event;
180*f6aab3d8Srobert   m_events_stats.RecordEvent(event);
181*f6aab3d8Srobert }
182*f6aab3d8Srobert 
AppendInstruction(const pt_insn & insn)183*f6aab3d8Srobert void DecodedThread::AppendInstruction(const pt_insn &insn) {
184*f6aab3d8Srobert   CreateNewTraceItem(lldb::eTraceItemKindInstruction).load_address = insn.ip;
185*f6aab3d8Srobert   m_insn_count++;
186*f6aab3d8Srobert }
187*f6aab3d8Srobert 
AppendError(const IntelPTError & error)188*f6aab3d8Srobert void DecodedThread::AppendError(const IntelPTError &error) {
189*f6aab3d8Srobert   CreateNewTraceItem(lldb::eTraceItemKindError).error =
190*f6aab3d8Srobert       ConstString(error.message()).AsCString();
191*f6aab3d8Srobert   m_error_stats.RecordError(/*fatal=*/false);
192*f6aab3d8Srobert }
193*f6aab3d8Srobert 
AppendCustomError(StringRef err,bool fatal)194*f6aab3d8Srobert void DecodedThread::AppendCustomError(StringRef err, bool fatal) {
195*f6aab3d8Srobert   CreateNewTraceItem(lldb::eTraceItemKindError).error =
196*f6aab3d8Srobert       ConstString(err).AsCString();
197*f6aab3d8Srobert   m_error_stats.RecordError(fatal);
198*f6aab3d8Srobert }
199*f6aab3d8Srobert 
GetEventByIndex(int item_index) const200*f6aab3d8Srobert lldb::TraceEvent DecodedThread::GetEventByIndex(int item_index) const {
201*f6aab3d8Srobert   return m_item_data[item_index].event;
202*f6aab3d8Srobert }
203*f6aab3d8Srobert 
GetEventsStats() const204*f6aab3d8Srobert const DecodedThread::EventsStats &DecodedThread::GetEventsStats() const {
205*f6aab3d8Srobert   return m_events_stats;
206*f6aab3d8Srobert }
207*f6aab3d8Srobert 
RecordEvent(lldb::TraceEvent event)208*f6aab3d8Srobert void DecodedThread::EventsStats::RecordEvent(lldb::TraceEvent event) {
209*f6aab3d8Srobert   events_counts[event]++;
210*f6aab3d8Srobert   total_count++;
211*f6aab3d8Srobert }
212*f6aab3d8Srobert 
GetTotalCount() const213*f6aab3d8Srobert uint64_t DecodedThread::ErrorStats::GetTotalCount() const {
214*f6aab3d8Srobert   uint64_t total = 0;
215*f6aab3d8Srobert   for (const auto &[kind, count] : libipt_errors)
216*f6aab3d8Srobert     total += count;
217*f6aab3d8Srobert 
218*f6aab3d8Srobert   return total + other_errors + fatal_errors;
219*f6aab3d8Srobert }
220*f6aab3d8Srobert 
RecordError(bool fatal)221*f6aab3d8Srobert void DecodedThread::ErrorStats::RecordError(bool fatal) {
222*f6aab3d8Srobert   if (fatal)
223*f6aab3d8Srobert     fatal_errors++;
224*f6aab3d8Srobert   else
225*f6aab3d8Srobert     other_errors++;
226*f6aab3d8Srobert }
227*f6aab3d8Srobert 
RecordError(int libipt_error_code)228*f6aab3d8Srobert void DecodedThread::ErrorStats::RecordError(int libipt_error_code) {
229*f6aab3d8Srobert   libipt_errors[pt_errstr(pt_errcode(libipt_error_code))]++;
230*f6aab3d8Srobert }
231*f6aab3d8Srobert 
GetErrorStats() const232*f6aab3d8Srobert const DecodedThread::ErrorStats &DecodedThread::GetErrorStats() const {
233*f6aab3d8Srobert   return m_error_stats;
234*f6aab3d8Srobert }
235*f6aab3d8Srobert 
236*f6aab3d8Srobert lldb::TraceItemKind
GetItemKindByIndex(uint64_t item_index) const237*f6aab3d8Srobert DecodedThread::GetItemKindByIndex(uint64_t item_index) const {
238*f6aab3d8Srobert   return static_cast<lldb::TraceItemKind>(m_item_kinds[item_index]);
239*f6aab3d8Srobert }
240*f6aab3d8Srobert 
GetErrorByIndex(uint64_t item_index) const241*f6aab3d8Srobert const char *DecodedThread::GetErrorByIndex(uint64_t item_index) const {
242*f6aab3d8Srobert   return m_item_data[item_index].error;
243*f6aab3d8Srobert }
244*f6aab3d8Srobert 
DecodedThread(ThreadSP thread_sp,const std::optional<LinuxPerfZeroTscConversion> & tsc_conversion)245*f6aab3d8Srobert DecodedThread::DecodedThread(
246*f6aab3d8Srobert     ThreadSP thread_sp,
247*f6aab3d8Srobert     const std::optional<LinuxPerfZeroTscConversion> &tsc_conversion)
248*f6aab3d8Srobert     : m_thread_sp(thread_sp), m_tsc_conversion(tsc_conversion) {}
249*f6aab3d8Srobert 
CalculateApproximateMemoryUsage() const250*f6aab3d8Srobert size_t DecodedThread::CalculateApproximateMemoryUsage() const {
251*f6aab3d8Srobert   return sizeof(TraceItemStorage) * m_item_data.size() +
252*f6aab3d8Srobert          sizeof(uint8_t) * m_item_kinds.size() +
253*f6aab3d8Srobert          (sizeof(uint64_t) + sizeof(TSC)) * m_tscs.size() +
254*f6aab3d8Srobert          (sizeof(uint64_t) + sizeof(uint64_t)) * m_nanoseconds.size() +
255*f6aab3d8Srobert          (sizeof(uint64_t) + sizeof(lldb::cpu_id_t)) * m_cpus.size();
256be691f3bSpatrick }
257