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
9 #include "TraceIntelPTMultiCpuDecoder.h"
10
11 #include "TraceIntelPT.h"
12
13 #include "llvm/Support/Error.h"
14
15 using namespace lldb;
16 using namespace lldb_private;
17 using namespace lldb_private::trace_intel_pt;
18 using namespace llvm;
19
TraceIntelPTMultiCpuDecoder(TraceIntelPTSP trace_sp)20 TraceIntelPTMultiCpuDecoder::TraceIntelPTMultiCpuDecoder(
21 TraceIntelPTSP trace_sp)
22 : m_trace_wp(trace_sp) {
23 for (Process *proc : trace_sp->GetAllProcesses()) {
24 for (ThreadSP thread_sp : proc->GetThreadList().Threads()) {
25 m_tids.insert(thread_sp->GetID());
26 }
27 }
28 }
29
GetTrace()30 TraceIntelPTSP TraceIntelPTMultiCpuDecoder::GetTrace() {
31 return m_trace_wp.lock();
32 }
33
TracesThread(lldb::tid_t tid) const34 bool TraceIntelPTMultiCpuDecoder::TracesThread(lldb::tid_t tid) const {
35 return m_tids.count(tid);
36 }
37
FindLowestTSC()38 Expected<Optional<uint64_t>> TraceIntelPTMultiCpuDecoder::FindLowestTSC() {
39 Optional<uint64_t> lowest_tsc;
40 TraceIntelPTSP trace_sp = GetTrace();
41
42 Error err = GetTrace()->OnAllCpusBinaryDataRead(
43 IntelPTDataKinds::kIptTrace,
44 [&](const DenseMap<cpu_id_t, ArrayRef<uint8_t>> &buffers) -> Error {
45 for (auto &cpu_id_to_buffer : buffers) {
46 Expected<Optional<uint64_t>> tsc =
47 FindLowestTSCInTrace(*trace_sp, cpu_id_to_buffer.second);
48 if (!tsc)
49 return tsc.takeError();
50 if (*tsc && (!lowest_tsc || *lowest_tsc > **tsc))
51 lowest_tsc = **tsc;
52 }
53 return Error::success();
54 });
55 if (err)
56 return std::move(err);
57 return lowest_tsc;
58 }
59
Decode(Thread & thread)60 Expected<DecodedThreadSP> TraceIntelPTMultiCpuDecoder::Decode(Thread &thread) {
61 if (Error err = CorrelateContextSwitchesAndIntelPtTraces())
62 return std::move(err);
63
64 TraceIntelPTSP trace_sp = GetTrace();
65
66 return trace_sp
67 ->GetThreadTimer(thread.GetID())
68 .TimeTask("Decoding instructions", [&]() -> Expected<DecodedThreadSP> {
69 auto it = m_decoded_threads.find(thread.GetID());
70 if (it != m_decoded_threads.end())
71 return it->second;
72
73 DecodedThreadSP decoded_thread_sp = std::make_shared<DecodedThread>(
74 thread.shared_from_this(), trace_sp->GetPerfZeroTscConversion());
75
76 Error err = trace_sp->OnAllCpusBinaryDataRead(
77 IntelPTDataKinds::kIptTrace,
78 [&](const DenseMap<cpu_id_t, ArrayRef<uint8_t>> &buffers) -> Error {
79 auto it =
80 m_continuous_executions_per_thread->find(thread.GetID());
81 if (it != m_continuous_executions_per_thread->end())
82 return DecodeSystemWideTraceForThread(
83 *decoded_thread_sp, *trace_sp, buffers, it->second);
84
85 return Error::success();
86 });
87 if (err)
88 return std::move(err);
89
90 m_decoded_threads.try_emplace(thread.GetID(), decoded_thread_sp);
91 return decoded_thread_sp;
92 });
93 }
94
95 static Expected<std::vector<IntelPTThreadSubtrace>>
GetIntelPTSubtracesForCpu(TraceIntelPT & trace,cpu_id_t cpu_id)96 GetIntelPTSubtracesForCpu(TraceIntelPT &trace, cpu_id_t cpu_id) {
97 std::vector<IntelPTThreadSubtrace> intel_pt_subtraces;
98 Error err = trace.OnCpuBinaryDataRead(
99 cpu_id, IntelPTDataKinds::kIptTrace,
100 [&](ArrayRef<uint8_t> data) -> Error {
101 Expected<std::vector<IntelPTThreadSubtrace>> split_trace =
102 SplitTraceInContinuousExecutions(trace, data);
103 if (!split_trace)
104 return split_trace.takeError();
105
106 intel_pt_subtraces = std::move(*split_trace);
107 return Error::success();
108 });
109 if (err)
110 return std::move(err);
111 return intel_pt_subtraces;
112 }
113
114 Expected<DenseMap<lldb::tid_t, std::vector<IntelPTThreadContinousExecution>>>
DoCorrelateContextSwitchesAndIntelPtTraces()115 TraceIntelPTMultiCpuDecoder::DoCorrelateContextSwitchesAndIntelPtTraces() {
116 DenseMap<lldb::tid_t, std::vector<IntelPTThreadContinousExecution>>
117 continuous_executions_per_thread;
118 TraceIntelPTSP trace_sp = GetTrace();
119
120 Optional<LinuxPerfZeroTscConversion> conv_opt =
121 trace_sp->GetPerfZeroTscConversion();
122 if (!conv_opt)
123 return createStringError(
124 inconvertibleErrorCode(),
125 "TSC to nanoseconds conversion values were not found");
126
127 LinuxPerfZeroTscConversion tsc_conversion = *conv_opt;
128
129 for (cpu_id_t cpu_id : trace_sp->GetTracedCpus()) {
130 Expected<std::vector<IntelPTThreadSubtrace>> intel_pt_subtraces =
131 GetIntelPTSubtracesForCpu(*trace_sp, cpu_id);
132 if (!intel_pt_subtraces)
133 return intel_pt_subtraces.takeError();
134
135 m_total_psb_blocks += intel_pt_subtraces->size();
136 // We'll be iterating through the thread continuous executions and the intel
137 // pt subtraces sorted by time.
138 auto it = intel_pt_subtraces->begin();
139 auto on_new_thread_execution =
140 [&](const ThreadContinuousExecution &thread_execution) {
141 IntelPTThreadContinousExecution execution(thread_execution);
142
143 for (; it != intel_pt_subtraces->end() &&
144 it->tsc < thread_execution.GetEndTSC();
145 it++) {
146 if (it->tsc > thread_execution.GetStartTSC()) {
147 execution.intelpt_subtraces.push_back(*it);
148 } else {
149 m_unattributed_psb_blocks++;
150 }
151 }
152 continuous_executions_per_thread[thread_execution.tid].push_back(
153 execution);
154 };
155 Error err = trace_sp->OnCpuBinaryDataRead(
156 cpu_id, IntelPTDataKinds::kPerfContextSwitchTrace,
157 [&](ArrayRef<uint8_t> data) -> Error {
158 Expected<std::vector<ThreadContinuousExecution>> executions =
159 DecodePerfContextSwitchTrace(data, cpu_id, tsc_conversion);
160 if (!executions)
161 return executions.takeError();
162 for (const ThreadContinuousExecution &exec : *executions)
163 on_new_thread_execution(exec);
164 return Error::success();
165 });
166 if (err)
167 return std::move(err);
168
169 m_unattributed_psb_blocks += intel_pt_subtraces->end() - it;
170 }
171 // We now sort the executions of each thread to have them ready for
172 // instruction decoding
173 for (auto &tid_executions : continuous_executions_per_thread)
174 std::sort(tid_executions.second.begin(), tid_executions.second.end());
175
176 return continuous_executions_per_thread;
177 }
178
CorrelateContextSwitchesAndIntelPtTraces()179 Error TraceIntelPTMultiCpuDecoder::CorrelateContextSwitchesAndIntelPtTraces() {
180 if (m_setup_error)
181 return createStringError(inconvertibleErrorCode(), m_setup_error->c_str());
182
183 if (m_continuous_executions_per_thread)
184 return Error::success();
185
186 Error err = GetTrace()->GetGlobalTimer().TimeTask(
187 "Context switch and Intel PT traces correlation", [&]() -> Error {
188 if (auto correlation = DoCorrelateContextSwitchesAndIntelPtTraces()) {
189 m_continuous_executions_per_thread.emplace(std::move(*correlation));
190 return Error::success();
191 } else {
192 return correlation.takeError();
193 }
194 });
195 if (err) {
196 m_setup_error = toString(std::move(err));
197 return createStringError(inconvertibleErrorCode(), m_setup_error->c_str());
198 }
199 return Error::success();
200 }
201
GetNumContinuousExecutionsForThread(lldb::tid_t tid) const202 size_t TraceIntelPTMultiCpuDecoder::GetNumContinuousExecutionsForThread(
203 lldb::tid_t tid) const {
204 if (!m_continuous_executions_per_thread)
205 return 0;
206 auto it = m_continuous_executions_per_thread->find(tid);
207 if (it == m_continuous_executions_per_thread->end())
208 return 0;
209 return it->second.size();
210 }
211
GetTotalContinuousExecutionsCount() const212 size_t TraceIntelPTMultiCpuDecoder::GetTotalContinuousExecutionsCount() const {
213 if (!m_continuous_executions_per_thread)
214 return 0;
215 size_t count = 0;
216 for (const auto &kv : *m_continuous_executions_per_thread)
217 count += kv.second.size();
218 return count;
219 }
220
221 size_t
GePSBBlocksCountForThread(lldb::tid_t tid) const222 TraceIntelPTMultiCpuDecoder::GePSBBlocksCountForThread(lldb::tid_t tid) const {
223 if (!m_continuous_executions_per_thread)
224 return 0;
225 size_t count = 0;
226 auto it = m_continuous_executions_per_thread->find(tid);
227 if (it == m_continuous_executions_per_thread->end())
228 return 0;
229 for (const IntelPTThreadContinousExecution &execution : it->second)
230 count += execution.intelpt_subtraces.size();
231 return count;
232 }
233
GetUnattributedPSBBlocksCount() const234 size_t TraceIntelPTMultiCpuDecoder::GetUnattributedPSBBlocksCount() const {
235 return m_unattributed_psb_blocks;
236 }
237
GetTotalPSBBlocksCount() const238 size_t TraceIntelPTMultiCpuDecoder::GetTotalPSBBlocksCount() const {
239 return m_total_psb_blocks;
240 }
241