LLDB mainline
TraceIntelPTMultiCpuDecoder.cpp
Go to the documentation of this file.
1//===-- TraceIntelPTMultiCpuDecoder.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
10#include "TraceIntelPT.h"
11#include "llvm/Support/Error.h"
12#include <optional>
13
14using namespace lldb;
15using namespace lldb_private;
16using namespace lldb_private::trace_intel_pt;
17using namespace llvm;
18
20 TraceIntelPTSP trace_sp)
21 : m_trace_wp(trace_sp) {
22 for (Process *proc : trace_sp->GetAllProcesses()) {
23 for (ThreadSP thread_sp : proc->GetThreadList().Threads()) {
24 m_tids.insert(thread_sp->GetID());
25 }
26 }
27}
28
30 return m_trace_wp.lock();
31}
32
34 return m_tids.count(tid);
35}
36
37Expected<std::optional<uint64_t>> TraceIntelPTMultiCpuDecoder::FindLowestTSC() {
38 std::optional<uint64_t> lowest_tsc;
39 TraceIntelPTSP trace_sp = GetTrace();
40
41 Error err = GetTrace()->OnAllCpusBinaryDataRead(
43 [&](const DenseMap<cpu_id_t, ArrayRef<uint8_t>> &buffers) -> Error {
44 for (auto &cpu_id_to_buffer : buffers) {
45 Expected<std::optional<uint64_t>> tsc =
46 FindLowestTSCInTrace(*trace_sp, cpu_id_to_buffer.second);
47 if (!tsc)
48 return tsc.takeError();
49 if (*tsc && (!lowest_tsc || *lowest_tsc > **tsc))
50 lowest_tsc = **tsc;
51 }
52 return Error::success();
53 });
54 if (err)
55 return std::move(err);
56 return lowest_tsc;
57}
58
59Expected<DecodedThreadSP> TraceIntelPTMultiCpuDecoder::Decode(Thread &thread) {
61 return std::move(err);
62
63 TraceIntelPTSP trace_sp = GetTrace();
64
65 return trace_sp->GetThreadTimer(thread.GetID())
66 .TimeTask("Decoding instructions", [&]() -> Expected<DecodedThreadSP> {
67 auto it = m_decoded_threads.find(thread.GetID());
68 if (it != m_decoded_threads.end())
69 return it->second;
70
71 DecodedThreadSP decoded_thread_sp = std::make_shared<DecodedThread>(
72 thread.shared_from_this(), trace_sp->GetPerfZeroTscConversion());
73
74 Error err = trace_sp->OnAllCpusBinaryDataRead(
76 [&](const DenseMap<cpu_id_t, ArrayRef<uint8_t>> &buffers) -> Error {
77 auto it =
78 m_continuous_executions_per_thread->find(thread.GetID());
79 if (it != m_continuous_executions_per_thread->end())
80 return DecodeSystemWideTraceForThread(
81 *decoded_thread_sp, *trace_sp, buffers, it->second);
82
83 return Error::success();
84 });
85 if (err)
86 return std::move(err);
87
88 m_decoded_threads.try_emplace(thread.GetID(), decoded_thread_sp);
89 return decoded_thread_sp;
90 });
91}
92
93static Expected<std::vector<PSBBlock>> GetPSBBlocksForCPU(TraceIntelPT &trace,
94 cpu_id_t cpu_id) {
95 std::vector<PSBBlock> psb_blocks;
96 Error err = trace.OnCpuBinaryDataRead(
98 [&](ArrayRef<uint8_t> data) -> Error {
99 Expected<std::vector<PSBBlock>> split_trace =
100 SplitTraceIntoPSBBlock(trace, data, /*expect_tscs=*/true);
101 if (!split_trace)
102 return split_trace.takeError();
103
104 psb_blocks = std::move(*split_trace);
105 return Error::success();
106 });
107 if (err)
108 return std::move(err);
109 return psb_blocks;
110}
111
112Expected<DenseMap<lldb::tid_t, std::vector<IntelPTThreadContinousExecution>>>
114 DenseMap<lldb::tid_t, std::vector<IntelPTThreadContinousExecution>>
115 continuous_executions_per_thread;
116 TraceIntelPTSP trace_sp = GetTrace();
117
118 std::optional<LinuxPerfZeroTscConversion> conv_opt =
119 trace_sp->GetPerfZeroTscConversion();
120 if (!conv_opt)
121 return createStringError(
122 inconvertibleErrorCode(),
123 "TSC to nanoseconds conversion values were not found");
124
125 LinuxPerfZeroTscConversion tsc_conversion = *conv_opt;
126
127 for (cpu_id_t cpu_id : trace_sp->GetTracedCpus()) {
128 Expected<std::vector<PSBBlock>> psb_blocks =
129 GetPSBBlocksForCPU(*trace_sp, cpu_id);
130 if (!psb_blocks)
131 return psb_blocks.takeError();
132
133 m_total_psb_blocks += psb_blocks->size();
134 // We'll be iterating through the thread continuous executions and the intel
135 // pt subtraces sorted by time.
136 auto it = psb_blocks->begin();
137 auto on_new_thread_execution =
138 [&](const ThreadContinuousExecution &thread_execution) {
139 IntelPTThreadContinousExecution execution(thread_execution);
140
141 for (; it != psb_blocks->end() &&
142 *it->tsc < thread_execution.GetEndTSC();
143 it++) {
144 if (*it->tsc > thread_execution.GetStartTSC()) {
145 execution.psb_blocks.push_back(*it);
146 } else {
148 }
149 }
150 continuous_executions_per_thread[thread_execution.tid].push_back(
151 execution);
152 };
153 Error err = trace_sp->OnCpuBinaryDataRead(
155 [&](ArrayRef<uint8_t> data) -> Error {
156 Expected<std::vector<ThreadContinuousExecution>> executions =
157 DecodePerfContextSwitchTrace(data, cpu_id, tsc_conversion);
158 if (!executions)
159 return executions.takeError();
160 for (const ThreadContinuousExecution &exec : *executions)
161 on_new_thread_execution(exec);
162 return Error::success();
163 });
164 if (err)
165 return std::move(err);
166
167 m_unattributed_psb_blocks += psb_blocks->end() - it;
168 }
169 // We now sort the executions of each thread to have them ready for
170 // instruction decoding
171 for (auto &tid_executions : continuous_executions_per_thread)
172 std::sort(tid_executions.second.begin(), tid_executions.second.end());
173
174 return continuous_executions_per_thread;
175}
176
178 if (m_setup_error)
179 return createStringError(inconvertibleErrorCode(), m_setup_error->c_str());
180
182 return Error::success();
183
184 Error err = GetTrace()->GetGlobalTimer().TimeTask(
185 "Context switch and Intel PT traces correlation", [&]() -> Error {
186 if (auto correlation = DoCorrelateContextSwitchesAndIntelPtTraces()) {
187 m_continuous_executions_per_thread.emplace(std::move(*correlation));
188 return Error::success();
189 } else {
190 return correlation.takeError();
191 }
192 });
193 if (err) {
194 m_setup_error = toString(std::move(err));
195 return createStringError(inconvertibleErrorCode(), m_setup_error->c_str());
196 }
197 return Error::success();
198}
199
201 lldb::tid_t tid) const {
203 return 0;
204 auto it = m_continuous_executions_per_thread->find(tid);
205 if (it == m_continuous_executions_per_thread->end())
206 return 0;
207 return it->second.size();
208}
209
212 return 0;
213 size_t count = 0;
214 for (const auto &kv : *m_continuous_executions_per_thread)
215 count += kv.second.size();
216 return count;
217}
218
219size_t
222 return 0;
223 size_t count = 0;
224 auto it = m_continuous_executions_per_thread->find(tid);
225 if (it == m_continuous_executions_per_thread->end())
226 return 0;
227 for (const IntelPTThreadContinousExecution &execution : it->second)
228 count += execution.psb_blocks.size();
229 return count;
230}
231
234}
235
237 return m_total_psb_blocks;
238}
static Expected< std::vector< PSBBlock > > GetPSBBlocksForCPU(TraceIntelPT &trace, cpu_id_t cpu_id)
llvm::Error Error
A plug-in interface definition class for debugging a process.
Definition: Process.h:341
llvm::Error OnCpuBinaryDataRead(lldb::cpu_id_t cpu_id, llvm::StringRef kind, OnBinaryDataReadCallback callback)
Fetch binary data associated with a cpu, either live or postmortem, and pass it to the given callback...
Definition: Trace.cpp:504
llvm::Expected< llvm::DenseMap< lldb::tid_t, std::vector< IntelPTThreadContinousExecution > > > DoCorrelateContextSwitchesAndIntelPtTraces()
Produce a mapping from thread ids to the list of continuos executions with their associated intel pt ...
llvm::Expected< DecodedThreadSP > Decode(Thread &thread)
llvm::DenseMap< lldb::tid_t, DecodedThreadSP > m_decoded_threads
llvm::Expected< std::optional< uint64_t > > FindLowestTSC()
std::optional< std::string > m_setup_error
This variable will not be std::nullopt if a severe error happened during the setup of the decoder and...
llvm::Error CorrelateContextSwitchesAndIntelPtTraces()
Traverse the context switch traces and the basic intel pt continuous subtraces and produce a list of ...
std::optional< llvm::DenseMap< lldb::tid_t, std::vector< IntelPTThreadContinousExecution > > > m_continuous_executions_per_thread
llvm::Expected< std::vector< PSBBlock > > SplitTraceIntoPSBBlock(TraceIntelPT &trace_intel_pt, llvm::ArrayRef< uint8_t > buffer, bool expect_tscs)
Given an intel pt trace, split it in chunks delimited by PSB packets.
std::shared_ptr< DecodedThread > DecodedThreadSP
llvm::Expected< std::vector< ThreadContinuousExecution > > DecodePerfContextSwitchTrace(llvm::ArrayRef< uint8_t > data, lldb::cpu_id_t cpu_id, const LinuxPerfZeroTscConversion &tsc_conversion)
Decodes a context switch trace collected with perf_event_open.
std::shared_ptr< TraceIntelPT > TraceIntelPTSP
llvm::Expected< std::optional< uint64_t > > FindLowestTSCInTrace(TraceIntelPT &trace_intel_pt, llvm::ArrayRef< uint8_t > buffer)
Find the lowest TSC in the given trace.
A class that represents a running process on the host machine.
Definition: SBAttachInfo.h:14
const char * toString(AppleArm64ExceptionClass EC)
Definition: SBAddress.h:15
std::shared_ptr< lldb_private::Thread > ThreadSP
Definition: lldb-forward.h:438
uint32_t cpu_id_t
Definition: lldb-types.h:89
uint64_t tid_t
Definition: lldb-types.h:82
Definition: Debugger.h:53
lldb::user_id_t GetID() const
Get accessor for the user ID.
Definition: UserID.h:47
This struct represents a continuous execution of a thread in a cpu, delimited by a context switch in ...
Definition: LibiptDecoder.h:42
This class indicates the time interval in which a thread was running continuously on a cpu core.