xref: /llvm-project/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp (revision 2fe8327406050d2585d2ced910a678e28caefcf5)
11f49714dSWalter Erquinigo //===-- IntelPTMultiCoreTrace.cpp -----------------------------------------===//
21f49714dSWalter Erquinigo //
31f49714dSWalter Erquinigo // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
41f49714dSWalter Erquinigo // See https://llvm.org/LICENSE.txt for license information.
51f49714dSWalter Erquinigo // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
61f49714dSWalter Erquinigo //
71f49714dSWalter Erquinigo //===----------------------------------------------------------------------===//
81f49714dSWalter Erquinigo 
91f49714dSWalter Erquinigo #include "IntelPTMultiCoreTrace.h"
101637545fSWalter Erquinigo #include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
11c4fb631cSWalter Erquinigo #include "Procfs.h"
12f190ce62SKazu Hirata #include <optional>
131637545fSWalter Erquinigo 
141f49714dSWalter Erquinigo using namespace lldb;
151f49714dSWalter Erquinigo using namespace lldb_private;
161f49714dSWalter Erquinigo using namespace process_linux;
171f49714dSWalter Erquinigo using namespace llvm;
181f49714dSWalter Erquinigo 
IsTotalBufferLimitReached(ArrayRef<cpu_id_t> cores,const TraceIntelPTStartRequest & request)196a5355e8SWalter Erquinigo static bool IsTotalBufferLimitReached(ArrayRef<cpu_id_t> cores,
201f49714dSWalter Erquinigo                                       const TraceIntelPTStartRequest &request) {
216a5355e8SWalter Erquinigo   uint64_t required = cores.size() * request.ipt_trace_size;
22aa88161bSKazu Hirata   uint64_t limit = request.process_buffer_size_limit.value_or(
231f49714dSWalter Erquinigo       std::numeric_limits<uint64_t>::max());
241f49714dSWalter Erquinigo   return required > limit;
251f49714dSWalter Erquinigo }
261f49714dSWalter Erquinigo 
IncludePerfEventParanoidMessageInError(Error && error)271f49714dSWalter Erquinigo static Error IncludePerfEventParanoidMessageInError(Error &&error) {
281f49714dSWalter Erquinigo   return createStringError(
291f49714dSWalter Erquinigo       inconvertibleErrorCode(),
301f49714dSWalter Erquinigo       "%s\nYou might need to rerun as sudo or to set "
31c4fb631cSWalter Erquinigo       "/proc/sys/kernel/perf_event_paranoid to a value of 0 or -1. You can use "
32c4fb631cSWalter Erquinigo       "`sudo sysctl -w kernel.perf_event_paranoid=-1` for that.",
331f49714dSWalter Erquinigo       toString(std::move(error)).c_str());
341f49714dSWalter Erquinigo }
351f49714dSWalter Erquinigo 
3603cc58ffSWalter Erquinigo Expected<std::unique_ptr<IntelPTMultiCoreTrace>>
StartOnAllCores(const TraceIntelPTStartRequest & request,NativeProcessProtocol & process,std::optional<int> cgroup_fd)371f56f7fcSWalter Erquinigo IntelPTMultiCoreTrace::StartOnAllCores(const TraceIntelPTStartRequest &request,
38d30fd5c3SGaurav Gaur                                        NativeProcessProtocol &process,
39daa6305cSFangrui Song                                        std::optional<int> cgroup_fd) {
406a5355e8SWalter Erquinigo   Expected<ArrayRef<cpu_id_t>> cpu_ids = GetAvailableLogicalCoreIDs();
416a5355e8SWalter Erquinigo   if (!cpu_ids)
426a5355e8SWalter Erquinigo     return cpu_ids.takeError();
431f49714dSWalter Erquinigo 
446a5355e8SWalter Erquinigo   if (IsTotalBufferLimitReached(*cpu_ids, request))
451f49714dSWalter Erquinigo     return createStringError(
461f49714dSWalter Erquinigo         inconvertibleErrorCode(),
471f49714dSWalter Erquinigo         "The process can't be traced because the process trace size limit "
481f49714dSWalter Erquinigo         "has been reached. Consider retracing with a higher limit.");
491f49714dSWalter Erquinigo 
506a5355e8SWalter Erquinigo   DenseMap<cpu_id_t, std::pair<IntelPTSingleBufferTrace, ContextSwitchTrace>>
51a7582059SWalter Erquinigo       traces;
52a7582059SWalter Erquinigo 
536a5355e8SWalter Erquinigo   for (cpu_id_t cpu_id : *cpu_ids) {
54a7582059SWalter Erquinigo     Expected<IntelPTSingleBufferTrace> core_trace =
55343523d0SKazu Hirata         IntelPTSingleBufferTrace::Start(request, /*tid=*/std::nullopt, cpu_id,
56d30fd5c3SGaurav Gaur                                         /*disabled=*/true, cgroup_fd);
57a7582059SWalter Erquinigo     if (!core_trace)
581f49714dSWalter Erquinigo       return IncludePerfEventParanoidMessageInError(core_trace.takeError());
59a7582059SWalter Erquinigo 
60a7582059SWalter Erquinigo     if (Expected<PerfEvent> context_switch_trace =
616a5355e8SWalter Erquinigo             CreateContextSwitchTracePerfEvent(cpu_id,
6203cc58ffSWalter Erquinigo                                               &core_trace->GetPerfEvent())) {
636a5355e8SWalter Erquinigo       traces.try_emplace(cpu_id,
64a7582059SWalter Erquinigo                          std::make_pair(std::move(*core_trace),
65a7582059SWalter Erquinigo                                         std::move(*context_switch_trace)));
66a7582059SWalter Erquinigo     } else {
67a7582059SWalter Erquinigo       return context_switch_trace.takeError();
68a7582059SWalter Erquinigo     }
691f49714dSWalter Erquinigo   }
701f49714dSWalter Erquinigo 
7103cc58ffSWalter Erquinigo   return std::unique_ptr<IntelPTMultiCoreTrace>(
72d30fd5c3SGaurav Gaur       new IntelPTMultiCoreTrace(std::move(traces), process, (bool)cgroup_fd));
731f49714dSWalter Erquinigo }
741f49714dSWalter Erquinigo 
ForEachCore(std::function<void (cpu_id_t cpu_id,IntelPTSingleBufferTrace & core_trace)> callback)751f49714dSWalter Erquinigo void IntelPTMultiCoreTrace::ForEachCore(
766a5355e8SWalter Erquinigo     std::function<void(cpu_id_t cpu_id, IntelPTSingleBufferTrace &core_trace)>
771f49714dSWalter Erquinigo         callback) {
781f49714dSWalter Erquinigo   for (auto &it : m_traces_per_core)
79a7582059SWalter Erquinigo     callback(it.first, it.second.first);
801f49714dSWalter Erquinigo }
811637545fSWalter Erquinigo 
ForEachCore(std::function<void (cpu_id_t cpu_id,IntelPTSingleBufferTrace & intelpt_trace,ContextSwitchTrace & context_switch_trace)> callback)82a7582059SWalter Erquinigo void IntelPTMultiCoreTrace::ForEachCore(
836a5355e8SWalter Erquinigo     std::function<void(cpu_id_t cpu_id, IntelPTSingleBufferTrace &intelpt_trace,
8403cc58ffSWalter Erquinigo                        ContextSwitchTrace &context_switch_trace)>
85a7582059SWalter Erquinigo         callback) {
86a7582059SWalter Erquinigo   for (auto &it : m_traces_per_core)
87a7582059SWalter Erquinigo     callback(it.first, it.second.first, it.second.second);
88a7582059SWalter Erquinigo }
89a7582059SWalter Erquinigo 
ProcessDidStop()90a7582059SWalter Erquinigo void IntelPTMultiCoreTrace::ProcessDidStop() {
916a5355e8SWalter Erquinigo   ForEachCore([](cpu_id_t cpu_id, IntelPTSingleBufferTrace &core_trace) {
92a7582059SWalter Erquinigo     if (Error err = core_trace.Pause()) {
931637545fSWalter Erquinigo       LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), std::move(err),
946a5355e8SWalter Erquinigo                      "Unable to pause the core trace for core {0}", cpu_id);
951637545fSWalter Erquinigo     }
961637545fSWalter Erquinigo   });
971637545fSWalter Erquinigo }
98a7582059SWalter Erquinigo 
ProcessWillResume()99a7582059SWalter Erquinigo void IntelPTMultiCoreTrace::ProcessWillResume() {
1006a5355e8SWalter Erquinigo   ForEachCore([](cpu_id_t cpu_id, IntelPTSingleBufferTrace &core_trace) {
101a7582059SWalter Erquinigo     if (Error err = core_trace.Resume()) {
1021637545fSWalter Erquinigo       LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), std::move(err),
1036a5355e8SWalter Erquinigo                      "Unable to resume the core trace for core {0}", cpu_id);
1041637545fSWalter Erquinigo     }
1051637545fSWalter Erquinigo   });
1061637545fSWalter Erquinigo }
1071f56f7fcSWalter Erquinigo 
GetState()1081f2d49a8SWalter Erquinigo TraceIntelPTGetStateResponse IntelPTMultiCoreTrace::GetState() {
1091f2d49a8SWalter Erquinigo   TraceIntelPTGetStateResponse state;
110d30fd5c3SGaurav Gaur   state.using_cgroup_filtering = m_using_cgroup_filtering;
1111f56f7fcSWalter Erquinigo 
112e095cddbSMichał Górny   for (NativeThreadProtocol &thread : m_process.Threads())
113c4fb631cSWalter Erquinigo     state.traced_threads.push_back(TraceThreadState{thread.GetID(), {}});
1141f56f7fcSWalter Erquinigo 
1156a5355e8SWalter Erquinigo   state.cpus.emplace();
1166a5355e8SWalter Erquinigo   ForEachCore([&](lldb::cpu_id_t cpu_id,
117a7582059SWalter Erquinigo                   const IntelPTSingleBufferTrace &core_trace,
11803cc58ffSWalter Erquinigo                   const ContextSwitchTrace &context_switch_trace) {
1196a5355e8SWalter Erquinigo     state.cpus->push_back(
1206a5355e8SWalter Erquinigo         {cpu_id,
1216a5355e8SWalter Erquinigo          {{IntelPTDataKinds::kIptTrace, core_trace.GetIptTraceSize()},
122a7582059SWalter Erquinigo           {IntelPTDataKinds::kPerfContextSwitchTrace,
123a7582059SWalter Erquinigo            context_switch_trace.GetEffectiveDataBufferSize()}}});
1241f56f7fcSWalter Erquinigo   });
1251f56f7fcSWalter Erquinigo 
1261f56f7fcSWalter Erquinigo   return state;
1271f56f7fcSWalter Erquinigo }
1281f56f7fcSWalter Erquinigo 
TracesThread(lldb::tid_t tid) const1291f56f7fcSWalter Erquinigo bool IntelPTMultiCoreTrace::TracesThread(lldb::tid_t tid) const {
1301f56f7fcSWalter Erquinigo   // All the process' threads are being traced automatically.
1311f56f7fcSWalter Erquinigo   return (bool)m_process.GetThreadByID(tid);
1321f56f7fcSWalter Erquinigo }
1331f56f7fcSWalter Erquinigo 
TraceStart(lldb::tid_t tid)1341f56f7fcSWalter Erquinigo llvm::Error IntelPTMultiCoreTrace::TraceStart(lldb::tid_t tid) {
13503cc58ffSWalter Erquinigo   // All the process' threads are being traced automatically.
13603cc58ffSWalter Erquinigo   if (!TracesThread(tid))
13703cc58ffSWalter Erquinigo     return createStringError(
13803cc58ffSWalter Erquinigo         inconvertibleErrorCode(),
13903cc58ffSWalter Erquinigo         "Thread %" PRIu64 " is not part of the target process", tid);
14003cc58ffSWalter Erquinigo   return Error::success();
1411f56f7fcSWalter Erquinigo }
1421f56f7fcSWalter Erquinigo 
TraceStop(lldb::tid_t tid)1431f56f7fcSWalter Erquinigo Error IntelPTMultiCoreTrace::TraceStop(lldb::tid_t tid) {
1441f56f7fcSWalter Erquinigo   return createStringError(inconvertibleErrorCode(),
1451f56f7fcSWalter Erquinigo                            "Can't stop tracing an individual thread when "
1469f45f23dSWalter Erquinigo                            "per-cpu process tracing is enabled.");
1471f56f7fcSWalter Erquinigo }
1481f56f7fcSWalter Erquinigo 
149*2fe83274SKazu Hirata Expected<std::optional<std::vector<uint8_t>>>
TryGetBinaryData(const TraceGetBinaryDataRequest & request)150fc5ef57cSWalter Erquinigo IntelPTMultiCoreTrace::TryGetBinaryData(
151fc5ef57cSWalter Erquinigo     const TraceGetBinaryDataRequest &request) {
1526a5355e8SWalter Erquinigo   if (!request.cpu_id)
153343523d0SKazu Hirata     return std::nullopt;
1546a5355e8SWalter Erquinigo   auto it = m_traces_per_core.find(*request.cpu_id);
155fc5ef57cSWalter Erquinigo   if (it == m_traces_per_core.end())
156fc5ef57cSWalter Erquinigo     return createStringError(
157fc5ef57cSWalter Erquinigo         inconvertibleErrorCode(),
1586a5355e8SWalter Erquinigo         formatv("Core {0} is not being traced", *request.cpu_id));
159fc5ef57cSWalter Erquinigo 
1606a5355e8SWalter Erquinigo   if (request.kind == IntelPTDataKinds::kIptTrace)
1616a5355e8SWalter Erquinigo     return it->second.first.GetIptTrace();
162fc5ef57cSWalter Erquinigo   if (request.kind == IntelPTDataKinds::kPerfContextSwitchTrace)
163561a61fbSWalter Erquinigo     return it->second.second.GetReadOnlyDataBuffer();
164343523d0SKazu Hirata   return std::nullopt;
1651f56f7fcSWalter Erquinigo }
166