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