1 //===-- IntelPTMultiCoreTrace.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 "IntelPTMultiCoreTrace.h"
10 
11 #include "Procfs.h"
12 
13 #include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
14 
15 using namespace lldb;
16 using namespace lldb_private;
17 using namespace process_linux;
18 using namespace llvm;
19 
20 static bool IsTotalBufferLimitReached(ArrayRef<core_id_t> cores,
21                                       const TraceIntelPTStartRequest &request) {
22   uint64_t required = cores.size() * request.trace_buffer_size;
23   uint64_t limit = request.process_buffer_size_limit.getValueOr(
24       std::numeric_limits<uint64_t>::max());
25   return required > limit;
26 }
27 
28 static Error IncludePerfEventParanoidMessageInError(Error &&error) {
29   return createStringError(
30       inconvertibleErrorCode(),
31       "%s\nYou might need to rerun as sudo or to set "
32       "/proc/sys/kernel/perf_event_paranoid to a value of 0 or -1.",
33       toString(std::move(error)).c_str());
34 }
35 
36 static Expected<PerfEvent> CreateContextSwitchTracePerfEvent(
37     bool disabled, lldb::core_id_t core_id,
38     IntelPTSingleBufferTrace &intelpt_core_trace) {
39   Log *log = GetLog(POSIXLog::Trace);
40 #ifndef PERF_ATTR_SIZE_VER5
41   return createStringError(inconvertibleErrorCode(),
42                            "Intel PT Linux perf event not supported");
43 #else
44   perf_event_attr attr;
45   memset(&attr, 0, sizeof(attr));
46   attr.size = sizeof(attr);
47   attr.sample_period = 0;
48   attr.sample_type = PERF_SAMPLE_TID | PERF_SAMPLE_TIME;
49   attr.type = PERF_TYPE_SOFTWARE;
50   attr.context_switch = 1;
51   attr.exclude_kernel = 1;
52   attr.sample_id_all = 1;
53   attr.exclude_hv = 1;
54   attr.disabled = disabled;
55 
56   // The given perf configuration will product context switch records of 32
57   // bytes each. Assuming that every context switch will be emitted twice (one
58   // for context switch ins and another one for context switch outs), and that a
59   // context switch will happen at least every half a millisecond per core, we
60   // need 500 * 32 bytes (~16 KB) for a trace of one second, which is much more
61   // than what a regular intel pt trace can get. Pessimistically we pick as
62   // 32KiB for the size of our context switch trace.
63 
64   uint64_t data_buffer_size = 32768;
65   uint64_t data_buffer_numpages = data_buffer_size / getpagesize();
66 
67   LLDB_LOG(log, "Will create context switch trace buffer of size {0}",
68            data_buffer_size);
69 
70   if (Expected<PerfEvent> perf_event = PerfEvent::Init(
71           attr, /*pid=*/None, core_id,
72           intelpt_core_trace.GetPerfEvent().GetFd(), /*flags=*/0)) {
73     if (Error mmap_err = perf_event->MmapMetadataAndBuffers(
74             data_buffer_numpages, 0, /*data_buffer_write=*/false)) {
75       return std::move(mmap_err);
76     }
77     return perf_event;
78   } else {
79     return perf_event.takeError();
80   }
81 #endif
82 }
83 
84 Expected<IntelPTProcessTraceUP>
85 IntelPTMultiCoreTrace::StartOnAllCores(const TraceIntelPTStartRequest &request,
86                                        NativeProcessProtocol &process) {
87   Expected<ArrayRef<core_id_t>> core_ids = GetAvailableLogicalCoreIDs();
88   if (!core_ids)
89     return core_ids.takeError();
90 
91   if (IsTotalBufferLimitReached(*core_ids, request))
92     return createStringError(
93         inconvertibleErrorCode(),
94         "The process can't be traced because the process trace size limit "
95         "has been reached. Consider retracing with a higher limit.");
96 
97   DenseMap<core_id_t, std::pair<IntelPTSingleBufferTrace, ContextSwitchTrace>>
98       traces;
99 
100   for (core_id_t core_id : *core_ids) {
101     Expected<IntelPTSingleBufferTrace> core_trace =
102         IntelPTSingleBufferTrace::Start(request, /*tid=*/None, core_id,
103                                         /*disabled=*/true);
104     if (!core_trace)
105       return IncludePerfEventParanoidMessageInError(core_trace.takeError());
106 
107     if (Expected<PerfEvent> context_switch_trace =
108             CreateContextSwitchTracePerfEvent(/*disabled=*/true, core_id,
109                                               core_trace.get())) {
110       traces.try_emplace(core_id,
111                          std::make_pair(std::move(*core_trace),
112                                         std::move(*context_switch_trace)));
113     } else {
114       return context_switch_trace.takeError();
115     }
116   }
117 
118   return IntelPTProcessTraceUP(
119       new IntelPTMultiCoreTrace(std::move(traces), process));
120 }
121 
122 void IntelPTMultiCoreTrace::ForEachCore(
123     std::function<void(core_id_t core_id, IntelPTSingleBufferTrace &core_trace)>
124         callback) {
125   for (auto &it : m_traces_per_core)
126     callback(it.first, it.second.first);
127 }
128 
129 void IntelPTMultiCoreTrace::ForEachCore(
130     std::function<void(core_id_t core_id,
131                        IntelPTSingleBufferTrace &intelpt_trace,
132                        PerfEvent &context_switch_trace)>
133         callback) {
134   for (auto &it : m_traces_per_core)
135     callback(it.first, it.second.first, it.second.second);
136 }
137 
138 void IntelPTMultiCoreTrace::ProcessDidStop() {
139   ForEachCore([](core_id_t core_id, IntelPTSingleBufferTrace &core_trace) {
140     if (Error err = core_trace.Pause()) {
141       LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), std::move(err),
142                      "Unable to pause the core trace for core {0}", core_id);
143     }
144   });
145 }
146 
147 void IntelPTMultiCoreTrace::ProcessWillResume() {
148   ForEachCore([](core_id_t core_id, IntelPTSingleBufferTrace &core_trace) {
149     if (Error err = core_trace.Resume()) {
150       LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), std::move(err),
151                      "Unable to resume the core trace for core {0}", core_id);
152     }
153   });
154 }
155 
156 TraceIntelPTGetStateResponse IntelPTMultiCoreTrace::GetState() {
157   TraceIntelPTGetStateResponse state;
158 
159   for (size_t i = 0; m_process.GetThreadAtIndex(i); i++)
160     state.traced_threads.push_back(
161         TraceThreadState{m_process.GetThreadAtIndex(i)->GetID(), {}});
162 
163   state.cores.emplace();
164   ForEachCore([&](lldb::core_id_t core_id,
165                   const IntelPTSingleBufferTrace &core_trace,
166                   const PerfEvent &context_switch_trace) {
167     state.cores->push_back(
168         {core_id,
169          {{IntelPTDataKinds::kTraceBuffer, core_trace.GetTraceBufferSize()},
170           {IntelPTDataKinds::kPerfContextSwitchTrace,
171            context_switch_trace.GetEffectiveDataBufferSize()}}});
172   });
173 
174   return state;
175 }
176 
177 bool IntelPTMultiCoreTrace::TracesThread(lldb::tid_t tid) const {
178   // All the process' threads are being traced automatically.
179   return (bool)m_process.GetThreadByID(tid);
180 }
181 
182 llvm::Error IntelPTMultiCoreTrace::TraceStart(lldb::tid_t tid) {
183   // This instance is already tracing all threads automatically.
184   return llvm::Error::success();
185 }
186 
187 Error IntelPTMultiCoreTrace::TraceStop(lldb::tid_t tid) {
188   return createStringError(inconvertibleErrorCode(),
189                            "Can't stop tracing an individual thread when "
190                            "per-core process tracing is enabled.");
191 }
192 
193 Expected<Optional<std::vector<uint8_t>>>
194 IntelPTMultiCoreTrace::TryGetBinaryData(
195     const TraceGetBinaryDataRequest &request) {
196   if (!request.core_id)
197     return None;
198   auto it = m_traces_per_core.find(*request.core_id);
199   if (it == m_traces_per_core.end())
200     return createStringError(
201         inconvertibleErrorCode(),
202         formatv("Core {0} is not being traced", *request.core_id));
203 
204   if (request.kind == IntelPTDataKinds::kTraceBuffer)
205     return it->second.first.GetTraceBuffer(request.offset, request.size);
206   if (request.kind == IntelPTDataKinds::kPerfContextSwitchTrace)
207     return it->second.second.ReadFlushedOutDataCyclicBuffer(request.offset,
208                                                             request.size);
209   return None;
210 }
211