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