LLDB mainline
DecodedThread.cpp
Go to the documentation of this file.
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#include "TraceCursorIntelPT.h"
11#include <intel-pt.h>
12#include <memory>
13#include <optional>
14
15using namespace lldb;
16using namespace lldb_private;
17using namespace lldb_private::trace_intel_pt;
18using namespace llvm;
19
21
22IntelPTError::IntelPTError(int libipt_error_code, lldb::addr_t address)
23 : m_libipt_error_code(libipt_error_code), m_address(address) {
24 assert(libipt_error_code < 0);
25}
26
27void IntelPTError::log(llvm::raw_ostream &OS) const {
28 OS << pt_errstr(pt_errcode(m_libipt_error_code));
30 OS << formatv(": {0:x+16}", m_address);
31}
32
33bool DecodedThread::TSCRange::InRange(uint64_t item_index) const {
34 return item_index >= first_item_index &&
35 item_index < first_item_index + items_count;
36}
37
38bool DecodedThread::NanosecondsRange::InRange(uint64_t item_index) const {
39 return item_index >= first_item_index &&
40 item_index < first_item_index + items_count;
41}
42
44 uint64_t item_index, uint64_t begin_of_time_nanos,
45 const LinuxPerfZeroTscConversion &tsc_conversion) const {
46 uint64_t items_since_last_tsc = item_index - first_item_index;
47
48 auto interpolate = [&](uint64_t next_range_start_ns) {
49 if (next_range_start_ns == nanos) {
50 // If the resolution of the conversion formula is bad enough to consider
51 // these two timestamps as equal, then we just increase the next one by 1
52 // for correction
53 next_range_start_ns++;
54 }
55 long double item_duration =
56 static_cast<long double>(items_count) / (next_range_start_ns - nanos);
57 return (nanos - begin_of_time_nanos) + items_since_last_tsc * item_duration;
58 };
59
60 if (!next_range) {
61 // If this is the last TSC range, so we have to extrapolate. In this case,
62 // we assume that each instruction took one TSC, which is what an
63 // instruction would take if no parallelism is achieved and the frequency
64 // multiplier is 1.
65 return interpolate(tsc_conversion.ToNanos(tsc + items_count));
66 }
67 if (items_count < (next_range->tsc - tsc)) {
68 // If the numbers of items in this range is less than the total TSC duration
69 // of this range, i.e. each instruction taking longer than 1 TSC, then we
70 // can assume that something else happened between these TSCs (e.g. a
71 // context switch, change to kernel, decoding errors, etc). In this case, we
72 // also assume that each instruction took 1 TSC. A proper way to improve
73 // this would be to analize the next events in the trace looking for context
74 // switches or trace disablement events, but for now, as we only want an
75 // approximation, we keep it simple. We are also guaranteed that the time in
76 // nanos of the next range is different to the current one, just because of
77 // the definition of a NanosecondsRange.
78 return interpolate(
79 std::min(tsc_conversion.ToNanos(tsc + items_count), next_range->nanos));
80 }
81
82 // In this case, each item took less than 1 TSC, so some parallelism was
83 // achieved, which is an indication that we didn't suffered of any kind of
84 // interruption.
85 return interpolate(next_range->nanos);
86}
87
88uint64_t DecodedThread::GetItemsCount() const { return m_item_kinds.size(); }
89
91DecodedThread::GetInstructionLoadAddress(uint64_t item_index) const {
92 return m_item_data[item_index].load_address;
93}
94
96DecodedThread::GetSyncPointOffsetByIndex(uint64_t item_index) const {
97 return m_psb_offsets.find(item_index)->second;
98}
99
101
104 m_item_kinds.push_back(kind);
105 m_item_data.emplace_back();
106 if (m_last_tsc)
107 (*m_last_tsc)->second.items_count++;
109 (*m_last_nanoseconds)->second.items_count++;
110 return m_item_data.back();
111}
112
114 m_psb_offsets.try_emplace(GetItemsCount(), psb_offset);
116}
117
119 if (m_last_tsc && (*m_last_tsc)->second.tsc == tsc)
120 return;
121 if (m_last_tsc)
122 assert(tsc >= (*m_last_tsc)->second.tsc &&
123 "We can't have decreasing times");
124
125 m_last_tsc =
126 m_tscs.emplace(GetItemsCount(), TSCRange{tsc, 0, GetItemsCount()}).first;
127
128 if (m_tsc_conversion) {
129 uint64_t nanos = m_tsc_conversion->ToNanos(tsc);
130 if (!m_last_nanoseconds || (*m_last_nanoseconds)->second.nanos != nanos) {
133 .emplace(GetItemsCount(), NanosecondsRange{nanos, tsc, nullptr, 0,
134 GetItemsCount()})
135 .first;
136 if (*m_last_nanoseconds != m_nanoseconds.begin()) {
137 auto prev_range = prev(*m_last_nanoseconds);
138 prev_range->second.next_range = &(*m_last_nanoseconds)->second;
139 }
140 }
141 }
143}
144
146 if (!m_last_cpu || *m_last_cpu != cpu_id) {
147 m_cpus.emplace(GetItemsCount(), cpu_id);
148 m_last_cpu = cpu_id;
150 }
151}
152
154 auto it = m_cpus.upper_bound(item_index);
155 return it == m_cpus.begin() ? LLDB_INVALID_CPU_ID : prev(it)->second;
156}
157
158std::optional<DecodedThread::TSCRange>
159DecodedThread::GetTSCRangeByIndex(uint64_t item_index) const {
160 auto next_it = m_tscs.upper_bound(item_index);
161 if (next_it == m_tscs.begin())
162 return std::nullopt;
163 return prev(next_it)->second;
164}
165
166std::optional<DecodedThread::NanosecondsRange>
168 auto next_it = m_nanoseconds.upper_bound(item_index);
169 if (next_it == m_nanoseconds.begin())
170 return std::nullopt;
171 return prev(next_it)->second;
172}
173
175 return m_insn_count;
176}
177
181}
182
183void DecodedThread::AppendInstruction(const pt_insn &insn) {
185 m_insn_count++;
186}
187
190 m_error_stats.RecordError(/*fatal=*/false);
191}
192
193void DecodedThread::AppendCustomError(StringRef err, bool fatal) {
196}
197
199 return m_item_data[item_index].event;
200}
201
203 return m_events_stats;
204}
205
207 events_counts[event]++;
208 total_count++;
209}
210
212 uint64_t total = 0;
213 for (const auto &[kind, count] : libipt_errors)
214 total += count;
215
216 return total + other_errors + fatal_errors;
217}
218
220 if (fatal)
221 fatal_errors++;
222 else
223 other_errors++;
224}
225
226void DecodedThread::ErrorStats::RecordError(int libipt_error_code) {
227 libipt_errors[pt_errstr(pt_errcode(libipt_error_code))]++;
228}
229
231 return m_error_stats;
232}
233
235DecodedThread::GetItemKindByIndex(uint64_t item_index) const {
236 return static_cast<lldb::TraceItemKind>(m_item_kinds[item_index]);
237}
238
239llvm::StringRef DecodedThread::GetErrorByIndex(uint64_t item_index) const {
240 if (item_index >= m_item_data.size())
241 return llvm::StringRef();
242 return m_item_data[item_index].error;
243}
244
246 ThreadSP thread_sp,
247 const std::optional<LinuxPerfZeroTscConversion> &tsc_conversion)
248 : m_thread_sp(thread_sp), m_tsc_conversion(tsc_conversion) {}
249
251 return sizeof(TraceItemStorage) * m_item_data.size() +
252 sizeof(uint8_t) * m_item_kinds.size() +
253 (sizeof(uint64_t) + sizeof(TSC)) * m_tscs.size() +
254 (sizeof(uint64_t) + sizeof(uint64_t)) * m_nanoseconds.size() +
255 (sizeof(uint64_t) + sizeof(lldb::cpu_id_t)) * m_cpus.size();
256}
static llvm::raw_ostream & error(Stream &strm)
uint64_t m_insn_count
Total number of instructions in the trace.
const EventsStats & GetEventsStats() const
Return an object with statistics of the trace events that happened.
const ErrorStats & GetErrorStats() const
Return an object with statistics of the trace errors that happened.
std::optional< uint64_t > m_last_cpu
This is the chronologically last CPU ID.
std::optional< std::map< uint64_t, TSCRange >::iterator > m_last_tsc
This is the chronologically last TSC that has been added.
lldb::TraceItemKind GetItemKindByIndex(uint64_t item_index) const
void AppendEvent(lldb::TraceEvent)
Append an event.
std::vector< TraceItemStorage > m_item_data
Most of the trace data is stored here.
std::map< uint64_t, NanosecondsRange > m_nanoseconds
This map contains the non-interpolated nanoseconds timestamps of the decoded trace items.
llvm::StringRef GetErrorByIndex(uint64_t item_index) const
EventsStats m_events_stats
Statistics of all tracing events.
lldb::addr_t GetInstructionLoadAddress(uint64_t item_index) const
llvm::DenseMap< uint64_t, lldb::addr_t > m_psb_offsets
std::optional< DecodedThread::NanosecondsRange > GetNanosecondsRangeByIndex(uint64_t item_index)
Get a maximal range of trace items that include the given item_index that have the same nanoseconds t...
DecodedThread::TraceItemStorage & CreateNewTraceItem(lldb::TraceItemKind kind)
Create a new trace item.
void AppendError(const IntelPTError &error)
Append a decoding error.
lldb::TraceEvent GetEventByIndex(int item_index) const
void NotifyCPU(lldb::cpu_id_t cpu_id)
Notify this object that a CPU has been seen.
size_t CalculateApproximateMemoryUsage() const
The approximate size in bytes used by this instance, including all the already decoded instructions.
std::optional< LinuxPerfZeroTscConversion > m_tsc_conversion
TSC -> nanos conversion utility.
ErrorStats m_error_stats
Statistics of all tracing errors.
std::map< uint64_t, lldb::cpu_id_t > m_cpus
std::vector< uint8_t > m_item_kinds
The TraceItemKind for each trace item encoded as uint8_t.
void NotifySyncPoint(lldb::addr_t psb_offset)
Notify this object that a new PSB has been seen.
std::optional< DecodedThread::TSCRange > GetTSCRangeByIndex(uint64_t item_index) const
Get a maximal range of trace items that include the given item_index that have the same TSC value.
void AppendCustomError(llvm::StringRef error, bool fatal=false)
Append a custom decoding.
lldb::cpu_id_t GetCPUByIndex(uint64_t item_index) const
Get the most recent CPU id before or at the given trace item index.
void NotifyTsc(TSC tsc)
Notify this object that a new tsc has been seen.
lldb::addr_t GetSyncPointOffsetByIndex(uint64_t item_index) const
std::map< uint64_t, TSCRange > m_tscs
This map contains the TSCs of the decoded trace items.
uint64_t GetItemsCount() const
Get the total number of instruction, errors and events from the decoded trace.
lldb::ThreadSP m_thread_sp
When adding new members to this class, make sure to update CalculateApproximateMemoryUsage() accordin...
DecodedThread(lldb::ThreadSP thread_sp, const std::optional< LinuxPerfZeroTscConversion > &tsc_conversion)
std::optional< std::map< uint64_t, NanosecondsRange >::iterator > m_last_nanoseconds
void AppendInstruction(const pt_insn &insn)
Append an instruction.
Class for representing a libipt decoding error.
Definition: DecodedThread.h:25
void log(llvm::raw_ostream &OS) const override
IntelPTError(int libipt_error_code, lldb::addr_t address=LLDB_INVALID_ADDRESS)
#define LLDB_INVALID_ADDRESS
Definition: lldb-defines.h:76
#define LLDB_INVALID_CPU_ID
Definition: lldb-defines.h:91
A class that represents a running process on the host machine.
Definition: SBAttachInfo.h:14
Definition: SBAddress.h:15
std::shared_ptr< lldb_private::Thread > ThreadSP
Definition: lldb-forward.h:425
TraceEvent
Events that might happen during a trace session.
@ eTraceEventSyncPoint
The underlying tracing technology emitted a synchronization event used by trace processors.
@ eTraceEventCPUChanged
Event due to CPU change for a thread.
@ eTraceEventHWClockTick
Event due to a CPU HW clock tick.
uint32_t cpu_id_t
Definition: lldb-types.h:88
@ eTraceItemKindInstruction
@ eTraceItemKindEvent
@ eTraceItemKindError
uint64_t addr_t
Definition: lldb-types.h:79
Definition: Debugger.h:53
uint64_t ToNanos(uint64_t tsc) const
Convert TSC value to nanosecond wall time.
A structure that represents a maximal range of trace items associated to the same non-interpolated ti...
Definition: DecodedThread.h:82
double GetInterpolatedTime(uint64_t item_index, uint64_t beginning_of_time_nanos, const LinuxPerfZeroTscConversion &tsc_conversion) const
Calculate an interpolated timestamp in nanoseconds for the given item index.
A structure that represents a maximal range of trace items associated to the same TSC value.
Definition: DecodedThread.h:67
uint64_t items_count
Number of trace items in this range.
Definition: DecodedThread.h:70
uint64_t first_item_index
Index of the first trace item in this range.
Definition: DecodedThread.h:72
We use a union to optimize the memory usage for the different kinds of trace items.
uint64_t load_address
The load addresses of this item if it's an instruction.
lldb::TraceEvent event
The event kind of this item if it's an event.
std::string error
The string message of this item if it's an error.