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