1 //===-- TraceIntelPTMultiCpuDecoder.cpp -----------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "TraceIntelPTMultiCpuDecoder.h"
10 #include "TraceIntelPT.h"
11 #include "llvm/Support/Error.h"
15 using namespace lldb_private
;
16 using namespace lldb_private::trace_intel_pt
;
19 TraceIntelPTMultiCpuDecoder::TraceIntelPTMultiCpuDecoder(
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());
29 TraceIntelPTSP
TraceIntelPTMultiCpuDecoder::GetTrace() {
30 return m_trace_wp
.lock();
33 bool TraceIntelPTMultiCpuDecoder::TracesThread(lldb::tid_t tid
) const {
34 return m_tids
.count(tid
);
37 Expected
<std::optional
<uint64_t>> TraceIntelPTMultiCpuDecoder::FindLowestTSC() {
38 std::optional
<uint64_t> lowest_tsc
;
39 TraceIntelPTSP trace_sp
= GetTrace();
41 Error err
= GetTrace()->OnAllCpusBinaryDataRead(
42 IntelPTDataKinds::kIptTrace
,
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
);
48 return tsc
.takeError();
49 if (*tsc
&& (!lowest_tsc
|| *lowest_tsc
> **tsc
))
52 return Error::success();
55 return std::move(err
);
59 Expected
<DecodedThreadSP
> TraceIntelPTMultiCpuDecoder::Decode(Thread
&thread
) {
60 if (Error err
= CorrelateContextSwitchesAndIntelPtTraces())
61 return std::move(err
);
63 TraceIntelPTSP trace_sp
= GetTrace();
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())
71 DecodedThreadSP decoded_thread_sp
= std::make_shared
<DecodedThread
>(
72 thread
.shared_from_this(), trace_sp
->GetPerfZeroTscConversion());
74 Error err
= trace_sp
->OnAllCpusBinaryDataRead(
75 IntelPTDataKinds::kIptTrace
,
76 [&](const DenseMap
<cpu_id_t
, ArrayRef
<uint8_t>> &buffers
) -> Error
{
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
);
83 return Error::success();
86 return std::move(err
);
88 m_decoded_threads
.try_emplace(thread
.GetID(), decoded_thread_sp
);
89 return decoded_thread_sp
;
93 static Expected
<std::vector
<PSBBlock
>> GetPSBBlocksForCPU(TraceIntelPT
&trace
,
95 std::vector
<PSBBlock
> psb_blocks
;
96 Error err
= trace
.OnCpuBinaryDataRead(
97 cpu_id
, IntelPTDataKinds::kIptTrace
,
98 [&](ArrayRef
<uint8_t> data
) -> Error
{
99 Expected
<std::vector
<PSBBlock
>> split_trace
=
100 SplitTraceIntoPSBBlock(trace
, data
, /*expect_tscs=*/true);
102 return split_trace
.takeError();
104 psb_blocks
= std::move(*split_trace
);
105 return Error::success();
108 return std::move(err
);
112 Expected
<DenseMap
<lldb::tid_t
, std::vector
<IntelPTThreadContinousExecution
>>>
113 TraceIntelPTMultiCpuDecoder::DoCorrelateContextSwitchesAndIntelPtTraces() {
114 DenseMap
<lldb::tid_t
, std::vector
<IntelPTThreadContinousExecution
>>
115 continuous_executions_per_thread
;
116 TraceIntelPTSP trace_sp
= GetTrace();
118 std::optional
<LinuxPerfZeroTscConversion
> conv_opt
=
119 trace_sp
->GetPerfZeroTscConversion();
121 return createStringError(
122 inconvertibleErrorCode(),
123 "TSC to nanoseconds conversion values were not found");
125 LinuxPerfZeroTscConversion tsc_conversion
= *conv_opt
;
127 for (cpu_id_t cpu_id
: trace_sp
->GetTracedCpus()) {
128 Expected
<std::vector
<PSBBlock
>> psb_blocks
=
129 GetPSBBlocksForCPU(*trace_sp
, cpu_id
);
131 return psb_blocks
.takeError();
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
);
141 for (; it
!= psb_blocks
->end() &&
142 *it
->tsc
< thread_execution
.GetEndTSC();
144 if (*it
->tsc
> thread_execution
.GetStartTSC()) {
145 execution
.psb_blocks
.push_back(*it
);
147 m_unattributed_psb_blocks
++;
150 continuous_executions_per_thread
[thread_execution
.tid
].push_back(
153 Error err
= trace_sp
->OnCpuBinaryDataRead(
154 cpu_id
, IntelPTDataKinds::kPerfContextSwitchTrace
,
155 [&](ArrayRef
<uint8_t> data
) -> Error
{
156 Expected
<std::vector
<ThreadContinuousExecution
>> executions
=
157 DecodePerfContextSwitchTrace(data
, cpu_id
, tsc_conversion
);
159 return executions
.takeError();
160 for (const ThreadContinuousExecution
&exec
: *executions
)
161 on_new_thread_execution(exec
);
162 return Error::success();
165 return std::move(err
);
167 m_unattributed_psb_blocks
+= psb_blocks
->end() - it
;
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());
174 return continuous_executions_per_thread
;
177 Error
TraceIntelPTMultiCpuDecoder::CorrelateContextSwitchesAndIntelPtTraces() {
179 return createStringError(inconvertibleErrorCode(), m_setup_error
->c_str());
181 if (m_continuous_executions_per_thread
)
182 return Error::success();
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();
190 return correlation
.takeError();
194 m_setup_error
= toString(std::move(err
));
195 return createStringError(inconvertibleErrorCode(), m_setup_error
->c_str());
197 return Error::success();
200 size_t TraceIntelPTMultiCpuDecoder::GetNumContinuousExecutionsForThread(
201 lldb::tid_t tid
) const {
202 if (!m_continuous_executions_per_thread
)
204 auto it
= m_continuous_executions_per_thread
->find(tid
);
205 if (it
== m_continuous_executions_per_thread
->end())
207 return it
->second
.size();
210 size_t TraceIntelPTMultiCpuDecoder::GetTotalContinuousExecutionsCount() const {
211 if (!m_continuous_executions_per_thread
)
214 for (const auto &kv
: *m_continuous_executions_per_thread
)
215 count
+= kv
.second
.size();
220 TraceIntelPTMultiCpuDecoder::GePSBBlocksCountForThread(lldb::tid_t tid
) const {
221 if (!m_continuous_executions_per_thread
)
224 auto it
= m_continuous_executions_per_thread
->find(tid
);
225 if (it
== m_continuous_executions_per_thread
->end())
227 for (const IntelPTThreadContinousExecution
&execution
: it
->second
)
228 count
+= execution
.psb_blocks
.size();
232 size_t TraceIntelPTMultiCpuDecoder::GetUnattributedPSBBlocksCount() const {
233 return m_unattributed_psb_blocks
;
236 size_t TraceIntelPTMultiCpuDecoder::GetTotalPSBBlocksCount() const {
237 return m_total_psb_blocks
;