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<IntelPTMultiCoreTraceUP> IntelPTMultiCoreTrace::StartOnAllCores(
37     const TraceIntelPTStartRequest &request) {
38   Expected<ArrayRef<core_id_t>> core_ids = GetAvailableLogicalCoreIDs();
39   if (!core_ids)
40     return core_ids.takeError();
41 
42   if (IsTotalBufferLimitReached(*core_ids, request))
43     return createStringError(
44         inconvertibleErrorCode(),
45         "The process can't be traced because the process trace size limit "
46         "has been reached. Consider retracing with a higher limit.");
47 
48   llvm::DenseMap<core_id_t, IntelPTSingleBufferTraceUP> buffers;
49   for (core_id_t core_id : *core_ids) {
50     if (Expected<IntelPTSingleBufferTraceUP> core_trace =
51             IntelPTSingleBufferTrace::Start(request, /*tid=*/None, core_id,
52                                             TraceCollectionState::Paused))
53       buffers.try_emplace(core_id, std::move(*core_trace));
54     else
55       return IncludePerfEventParanoidMessageInError(core_trace.takeError());
56   }
57 
58   return IntelPTMultiCoreTraceUP(new IntelPTMultiCoreTrace(std::move(buffers)));
59 }
60 
61 void IntelPTMultiCoreTrace::ForEachCore(
62     std::function<void(core_id_t core_id, IntelPTSingleBufferTrace &core_trace)>
63         callback) {
64   for (auto &it : m_traces_per_core)
65     callback(it.first, *it.second);
66 }
67 
68 void IntelPTMultiCoreTrace::OnProcessStateChanged(lldb::StateType state) {
69   if (m_process_state == state)
70     return;
71   switch (state) {
72   case eStateStopped:
73   case eStateExited: {
74     ForEachCore([](core_id_t core_id, IntelPTSingleBufferTrace &core_trace) {
75       if (Error err =
76               core_trace.ChangeCollectionState(TraceCollectionState::Paused)) {
77         LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), std::move(err),
78                        "Unable to pause the core trace for core {0}", core_id);
79       }
80     });
81     break;
82   }
83   case eStateRunning: {
84     ForEachCore([](core_id_t core_id, IntelPTSingleBufferTrace &core_trace) {
85       if (Error err =
86               core_trace.ChangeCollectionState(TraceCollectionState::Running)) {
87         LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), std::move(err),
88                        "Unable to resume the core trace for core {0}", core_id);
89       }
90     });
91     break;
92   }
93   default:
94     break;
95   }
96 }
97