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 Expected<IntelPTProcessTraceUP>
37 IntelPTMultiCoreTrace::StartOnAllCores(const TraceIntelPTStartRequest &request,
38                                        NativeProcessProtocol &process) {
39   Expected<ArrayRef<core_id_t>> core_ids = GetAvailableLogicalCoreIDs();
40   if (!core_ids)
41     return core_ids.takeError();
42 
43   if (IsTotalBufferLimitReached(*core_ids, request))
44     return createStringError(
45         inconvertibleErrorCode(),
46         "The process can't be traced because the process trace size limit "
47         "has been reached. Consider retracing with a higher limit.");
48 
49   llvm::DenseMap<core_id_t, IntelPTSingleBufferTraceUP> buffers;
50   for (core_id_t core_id : *core_ids) {
51     if (Expected<IntelPTSingleBufferTraceUP> core_trace =
52             IntelPTSingleBufferTrace::Start(request, /*tid=*/None, core_id,
53                                             TraceCollectionState::Paused))
54       buffers.try_emplace(core_id, std::move(*core_trace));
55     else
56       return IncludePerfEventParanoidMessageInError(core_trace.takeError());
57   }
58 
59   return IntelPTProcessTraceUP(
60       new IntelPTMultiCoreTrace(std::move(buffers), process));
61 }
62 
63 void IntelPTMultiCoreTrace::ForEachCore(
64     std::function<void(core_id_t core_id, IntelPTSingleBufferTrace &core_trace)>
65         callback) {
66   for (auto &it : m_traces_per_core)
67     callback(it.first, *it.second);
68 }
69 
70 void IntelPTMultiCoreTrace::OnProcessStateChanged(lldb::StateType state) {
71   if (m_process_state == state)
72     return;
73   switch (state) {
74   case eStateStopped:
75   case eStateExited: {
76     ForEachCore([](core_id_t core_id, IntelPTSingleBufferTrace &core_trace) {
77       if (Error err =
78               core_trace.ChangeCollectionState(TraceCollectionState::Paused)) {
79         LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), std::move(err),
80                        "Unable to pause the core trace for core {0}", core_id);
81       }
82     });
83     break;
84   }
85   case eStateRunning: {
86     ForEachCore([](core_id_t core_id, IntelPTSingleBufferTrace &core_trace) {
87       if (Error err =
88               core_trace.ChangeCollectionState(TraceCollectionState::Running)) {
89         LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), std::move(err),
90                        "Unable to resume the core trace for core {0}", core_id);
91       }
92     });
93     break;
94   }
95   default:
96     break;
97   }
98 }
99 
100 TraceGetStateResponse IntelPTMultiCoreTrace::GetState() {
101   TraceGetStateResponse state;
102 
103   for (size_t i = 0; m_process.GetThreadAtIndex(i); i++)
104     state.traced_threads.push_back(
105         TraceThreadState{m_process.GetThreadAtIndex(i)->GetID(), {}});
106 
107   state.cores.emplace();
108   ForEachCore([&](lldb::core_id_t core_id,
109                   const IntelPTSingleBufferTrace &core_trace) {
110     state.cores->push_back(
111         {core_id,
112          {{IntelPTDataKinds::kTraceBuffer, core_trace.GetTraceBufferSize()}}});
113   });
114 
115   return state;
116 }
117 
118 bool IntelPTMultiCoreTrace::TracesThread(lldb::tid_t tid) const {
119   // All the process' threads are being traced automatically.
120   return (bool)m_process.GetThreadByID(tid);
121 }
122 
123 llvm::Error IntelPTMultiCoreTrace::TraceStart(lldb::tid_t tid) {
124   // This instance is already tracing all threads automatically.
125   return llvm::Error::success();
126 }
127 
128 Error IntelPTMultiCoreTrace::TraceStop(lldb::tid_t tid) {
129   return createStringError(inconvertibleErrorCode(),
130                            "Can't stop tracing an individual thread when "
131                            "per-core process tracing is enabled.");
132 }
133 
134 Expected<std::vector<uint8_t>>
135 IntelPTMultiCoreTrace::GetBinaryData(const TraceGetBinaryDataRequest &request) {
136   return createStringError(inconvertibleErrorCode(), "Unimplemented");
137 }
138