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 
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<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 
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>>
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>>>
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 
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 
202 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 
212 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
222 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 
234 size_t TraceIntelPTMultiCpuDecoder::GetUnattributedPSBBlocksCount() const {
235   return m_unattributed_psb_blocks;
236 }
237 
238 size_t TraceIntelPTMultiCpuDecoder::GetTotalPSBBlocksCount() const {
239   return m_total_psb_blocks;
240 }
241