forked from OSchip/llvm-project
[trace][intelpt] Support system-wide tracing [4] - Support per core tracing on lldb-server
This diffs implements per-core tracing on lldb-server. It also includes tests that ensure that tracing can be initiated from the client and that the jLLDBGetState ppacket returns the list of trace buffers per core. This doesn't include any decoder changes. Finally, this makes some little changes here and there improving the existing code. A specific piece of code that can't reliably be tested is when tracing per core fails due to permissions. In this case we add a troubleshooting message and this is the manual test: ``` /proc/sys/kernel/perf_event_paranoid set to 1 (lldb) process trace start --per-core-tracing error: perf event syscall failed: Permission denied You might need that /proc/sys/kernel/perf_event_paranoid has a value of 0 or -1. `` Differential Revision: https://reviews.llvm.org/D124858
This commit is contained in:
parent
8d53f2fc0d
commit
1f49714d3e
|
@ -245,7 +245,7 @@ read packet: OK
|
|||
// OUTPUT SCHEMA
|
||||
// {
|
||||
// "name": <string>,
|
||||
// Tracing technology name, e.g. intel-pt, arm-coresight.
|
||||
// Tracing technology name, e.g. intel-pt, arm-etm.
|
||||
// "description": <string>,
|
||||
// Description for this technology.
|
||||
// }
|
||||
|
@ -280,7 +280,7 @@ read packet: {"name":<name>, "description":<description>}/E<error code>;AAAAAAAA
|
|||
// INPUT SCHEMA
|
||||
// {
|
||||
// "type": <string>,
|
||||
// Tracing technology name, e.g. intel-pt, arm-coresight.
|
||||
// Tracing technology name, e.g. intel-pt, arm-etm.
|
||||
//
|
||||
// /* thread tracing only */
|
||||
// "tids"?: [<decimal integer>],
|
||||
|
@ -369,8 +369,9 @@ read packet: {"name":<name>, "description":<description>}/E<error code>;AAAAAAAA
|
|||
// /* process tracing only */
|
||||
// "processBufferSizeLimit": <decimal integer>,
|
||||
// Maximum total buffer size per process in bytes.
|
||||
// This limit applies to the sum of the sizes of all trace buffers for
|
||||
// the current process, excluding the ones started with "thread tracing".
|
||||
// This limit applies to the sum of the sizes of all thread or core
|
||||
// buffers for the current process, excluding the ones started with
|
||||
// "thread tracing".
|
||||
//
|
||||
// If "perCoreTracing" is false, whenever a thread is attempted to be
|
||||
// traced due to "process tracing" and the limit would be reached, the
|
||||
|
@ -421,7 +422,7 @@ read packet: OK/E<error code>;AAAAAAAAA
|
|||
//
|
||||
// {
|
||||
// "type": <string>
|
||||
// Tracing technology name, e.g. intel-pt, arm-coresight.
|
||||
// Tracing technology name, e.g. intel-pt, arm-etm.
|
||||
//
|
||||
// /* thread trace stopping only */
|
||||
// "tids": [<decimal integer>]
|
||||
|
@ -455,7 +456,7 @@ read packet: OK/E<error code>;AAAAAAAAA
|
|||
// INPUT SCHEMA
|
||||
// {
|
||||
// "type": <string>
|
||||
// Tracing technology name, e.g. intel-pt, arm-coresight.
|
||||
// Tracing technology name, e.g. intel-pt, arm-etm.
|
||||
// }
|
||||
//
|
||||
// OUTPUT SCHEMA
|
||||
|
@ -481,10 +482,23 @@ read packet: OK/E<error code>;AAAAAAAAA
|
|||
// Size in bytes of this thread data.
|
||||
// },
|
||||
// ],
|
||||
// "cores"?: [
|
||||
// "id": <decimal integer>,
|
||||
// Identifier for this CPU logical core.
|
||||
// "binaryData": [
|
||||
// {
|
||||
// "kind": <string>,
|
||||
// Identifier for some binary data related to this thread to
|
||||
// fetch with the jLLDBTraceGetBinaryData packet.
|
||||
// "size": <decimal integer>,
|
||||
// Size in bytes of this cpu core data.
|
||||
// },
|
||||
// ]
|
||||
// ],
|
||||
// "counters"?: {
|
||||
// "info_kind": {...parameters specific to the provided counter info kind},
|
||||
// Each entry includes information related to counters associated with the trace.
|
||||
// They are described below.
|
||||
// Each entry includes information related to counters associated with
|
||||
// the trace. They are described below.
|
||||
// }
|
||||
// }
|
||||
//
|
||||
|
@ -494,6 +508,12 @@ read packet: OK/E<error code>;AAAAAAAAA
|
|||
//
|
||||
// INTEL PT
|
||||
//
|
||||
// If per-core process tracing is enabled, "tracedThreads" will contain all
|
||||
// the threads of the process without any trace buffers. Besides that, the
|
||||
// "cores" field will also be returned with per core trace buffers.
|
||||
// A side effect of per-core tracing is that all the threads of unrelated
|
||||
// processes will also be traced, thus polluting the tracing data.
|
||||
//
|
||||
// Binary data kinds:
|
||||
// - traceBuffer: trace buffer for a thread or a core.
|
||||
// - procfsCpuInfo: contents of the /proc/cpuinfo file.
|
||||
|
@ -536,9 +556,11 @@ read packet: {...object}/E<error code>;AAAAAAAAA
|
|||
//
|
||||
// {
|
||||
// "type": <string>,
|
||||
// Tracing technology name, e.g. intel-pt, arm-coresight.
|
||||
// Tracing technology name, e.g. intel-pt, arm-etm.
|
||||
// "kind": <string>,
|
||||
// Identifier for the data.
|
||||
// "coreId": <Optional decimal>,
|
||||
// Core id in decimal if the data belongs to a CPU core.
|
||||
// "tid"?: <Optional decimal>,
|
||||
// Tid in decimal if the data belongs to a thread.
|
||||
// "offset": <decimal>,
|
||||
|
|
|
@ -109,7 +109,7 @@ llvm::json::Value toJSON(const TraceBinaryData &packet);
|
|||
struct TraceThreadState {
|
||||
lldb::tid_t tid;
|
||||
/// List of binary data objects for this thread.
|
||||
std::vector<TraceBinaryData> binaryData;
|
||||
std::vector<TraceBinaryData> binary_data;
|
||||
};
|
||||
|
||||
bool fromJSON(const llvm::json::Value &value, TraceThreadState &packet,
|
||||
|
@ -117,6 +117,17 @@ bool fromJSON(const llvm::json::Value &value, TraceThreadState &packet,
|
|||
|
||||
llvm::json::Value toJSON(const TraceThreadState &packet);
|
||||
|
||||
struct TraceCoreState {
|
||||
lldb::core_id_t core_id;
|
||||
/// List of binary data objects for this core.
|
||||
std::vector<TraceBinaryData> binary_data;
|
||||
};
|
||||
|
||||
bool fromJSON(const llvm::json::Value &value, TraceCoreState &packet,
|
||||
llvm::json::Path path);
|
||||
|
||||
llvm::json::Value toJSON(const TraceCoreState &packet);
|
||||
|
||||
/// Interface for different algorithms used to convert trace
|
||||
/// counters into different units.
|
||||
template <typename ToType> class TraceCounterConversion {
|
||||
|
@ -143,8 +154,9 @@ using TraceTscConversionUP =
|
|||
std::unique_ptr<TraceCounterConversion<std::chrono::nanoseconds>>;
|
||||
|
||||
struct TraceGetStateResponse {
|
||||
std::vector<TraceThreadState> tracedThreads;
|
||||
std::vector<TraceBinaryData> processBinaryData;
|
||||
std::vector<TraceThreadState> traced_threads;
|
||||
std::vector<TraceBinaryData> process_binary_data;
|
||||
llvm::Optional<std::vector<TraceCoreState>> cores;
|
||||
};
|
||||
|
||||
bool fromJSON(const llvm::json::Value &value, TraceGetStateResponse &packet,
|
||||
|
|
|
@ -48,6 +48,8 @@ struct TraceIntelPTStartRequest : TraceStartRequest {
|
|||
|
||||
/// Whether to have a trace buffer per thread or per cpu core.
|
||||
llvm::Optional<bool> per_core_tracing;
|
||||
|
||||
bool IsPerCoreTracing() const;
|
||||
};
|
||||
|
||||
bool fromJSON(const llvm::json::Value &value, TraceIntelPTStartRequest &packet,
|
||||
|
|
|
@ -89,6 +89,7 @@ typedef int32_t break_id_t;
|
|||
typedef int32_t watch_id_t;
|
||||
typedef void *opaque_compiler_type_t;
|
||||
typedef uint64_t queue_id_t;
|
||||
typedef uint32_t core_id_t; // CPU core id
|
||||
} // namespace lldb
|
||||
|
||||
#endif // LLDB_LLDB_TYPES_H
|
||||
|
|
|
@ -33,6 +33,19 @@ class TraceIntelPTTestCaseBase(TestBase):
|
|||
if 'intel-pt' not in configuration.enabled_plugins:
|
||||
self.skipTest("The intel-pt test plugin is not enabled")
|
||||
|
||||
def skipIfPerCoreTracingIsNotSupported(self):
|
||||
def is_supported():
|
||||
try:
|
||||
with open("/proc/sys/kernel/perf_event_paranoid", "r") as permissions:
|
||||
value = int(permissions.readlines()[0])
|
||||
if value <= 0:
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
if not is_supported():
|
||||
self.skipTest("Per core tracing is not supported. You need "
|
||||
"/proc/sys/kernel/perf_event_paranoid to be 0 or -1.")
|
||||
|
||||
def getTraceOrCreate(self):
|
||||
if not self.target().GetTrace().IsValid():
|
||||
error = lldb.SBError()
|
||||
|
@ -110,7 +123,7 @@ class TraceIntelPTTestCaseBase(TestBase):
|
|||
else:
|
||||
self.expect("process trace stop")
|
||||
|
||||
def traceStopThread(self, thread=None, error=False):
|
||||
def traceStopThread(self, thread=None, error=False, substrs=None):
|
||||
if self.USE_SB_API:
|
||||
thread = thread if thread is not None else self.thread()
|
||||
self.assertSBError(self.target().GetTrace().Stop(thread), error)
|
||||
|
@ -119,4 +132,4 @@ class TraceIntelPTTestCaseBase(TestBase):
|
|||
command = "thread trace stop"
|
||||
if thread is not None:
|
||||
command += " " + str(thread.GetIndexID())
|
||||
self.expect(command, error=error)
|
||||
self.expect(command, error=error, substrs=substrs)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
add_lldb_library(lldbPluginProcessLinux
|
||||
IntelPTCollector.cpp
|
||||
IntelPTSingleBufferTrace.cpp
|
||||
IntelPTMultiCoreTrace.cpp
|
||||
NativeProcessLinux.cpp
|
||||
NativeRegisterContextLinux.cpp
|
||||
NativeRegisterContextLinux_arm.cpp
|
||||
|
|
|
@ -92,17 +92,21 @@ void IntelPTThreadTraceCollection::Clear() {
|
|||
m_total_buffer_size = 0;
|
||||
}
|
||||
|
||||
/// IntelPTProcessTrace
|
||||
size_t IntelPTThreadTraceCollection::GetTracedThreadsCount() const {
|
||||
return m_thread_traces.size();
|
||||
}
|
||||
|
||||
bool IntelPTProcessTrace::TracesThread(lldb::tid_t tid) const {
|
||||
/// IntelPTPerThreadProcessTrace
|
||||
|
||||
bool IntelPTPerThreadProcessTrace::TracesThread(lldb::tid_t tid) const {
|
||||
return m_thread_traces.TracesThread(tid);
|
||||
}
|
||||
|
||||
Error IntelPTProcessTrace::TraceStop(lldb::tid_t tid) {
|
||||
Error IntelPTPerThreadProcessTrace::TraceStop(lldb::tid_t tid) {
|
||||
return m_thread_traces.TraceStop(tid);
|
||||
}
|
||||
|
||||
Error IntelPTProcessTrace::TraceStart(lldb::tid_t tid) {
|
||||
Error IntelPTPerThreadProcessTrace::TraceStart(lldb::tid_t tid) {
|
||||
if (m_thread_traces.GetTotalBufferSize() +
|
||||
m_tracing_params.trace_buffer_size >
|
||||
static_cast<size_t>(*m_tracing_params.process_buffer_size_limit))
|
||||
|
@ -117,13 +121,14 @@ Error IntelPTProcessTrace::TraceStart(lldb::tid_t tid) {
|
|||
}
|
||||
|
||||
const IntelPTThreadTraceCollection &
|
||||
IntelPTProcessTrace::GetThreadTraces() const {
|
||||
IntelPTPerThreadProcessTrace::GetThreadTraces() const {
|
||||
return m_thread_traces;
|
||||
}
|
||||
|
||||
/// IntelPTCollector
|
||||
|
||||
IntelPTCollector::IntelPTCollector() {
|
||||
IntelPTCollector::IntelPTCollector(NativeProcessProtocol &process)
|
||||
: m_process(process) {
|
||||
if (Expected<LinuxPerfZeroTscConversion> tsc_conversion =
|
||||
LoadPerfTscConversionParameters())
|
||||
m_tsc_conversion =
|
||||
|
@ -134,8 +139,13 @@ IntelPTCollector::IntelPTCollector() {
|
|||
}
|
||||
|
||||
Error IntelPTCollector::TraceStop(lldb::tid_t tid) {
|
||||
if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid))
|
||||
return m_process_trace->TraceStop(tid);
|
||||
if (m_per_thread_process_trace_up &&
|
||||
m_per_thread_process_trace_up->TracesThread(tid))
|
||||
return m_per_thread_process_trace_up->TraceStop(tid);
|
||||
else if (m_per_core_process_trace_up)
|
||||
return createStringError(inconvertibleErrorCode(),
|
||||
"Can't stop tracing an individual thread when "
|
||||
"per-core process tracing is enabled.");
|
||||
return m_thread_traces.TraceStop(tid);
|
||||
}
|
||||
|
||||
|
@ -152,26 +162,60 @@ Error IntelPTCollector::TraceStop(const TraceStopRequest &request) {
|
|||
}
|
||||
}
|
||||
|
||||
Error IntelPTCollector::TraceStart(
|
||||
const TraceIntelPTStartRequest &request,
|
||||
const std::vector<lldb::tid_t> &process_threads) {
|
||||
Expected<IntelPTPerThreadProcessTraceUP>
|
||||
IntelPTPerThreadProcessTrace::Start(const TraceIntelPTStartRequest &request,
|
||||
ArrayRef<lldb::tid_t> current_tids) {
|
||||
IntelPTPerThreadProcessTraceUP trace(
|
||||
new IntelPTPerThreadProcessTrace(request));
|
||||
|
||||
Error error = Error::success();
|
||||
for (lldb::tid_t tid : current_tids)
|
||||
error = joinErrors(std::move(error), trace->TraceStart(tid));
|
||||
if (error)
|
||||
return std::move(error);
|
||||
return trace;
|
||||
}
|
||||
|
||||
Error IntelPTCollector::TraceStart(const TraceIntelPTStartRequest &request) {
|
||||
if (request.IsProcessTracing()) {
|
||||
if (IsProcessTracingEnabled()) {
|
||||
return createStringError(
|
||||
inconvertibleErrorCode(),
|
||||
"Process currently traced. Stop process tracing first");
|
||||
}
|
||||
if (request.per_core_tracing.getValueOr(false)) {
|
||||
return createStringError(inconvertibleErrorCode(),
|
||||
"Per-core tracing is not supported.");
|
||||
}
|
||||
m_process_trace = IntelPTProcessTrace(request);
|
||||
if (request.IsPerCoreTracing()) {
|
||||
if (m_thread_traces.GetTracedThreadsCount() > 0)
|
||||
return createStringError(
|
||||
inconvertibleErrorCode(),
|
||||
"Threads currently traced. Stop tracing them first.");
|
||||
if (Expected<IntelPTMultiCoreTraceUP> trace =
|
||||
IntelPTMultiCoreTrace::StartOnAllCores(request)) {
|
||||
m_per_core_process_trace_up = std::move(*trace);
|
||||
return Error::success();
|
||||
} else {
|
||||
return trace.takeError();
|
||||
}
|
||||
} else {
|
||||
std::vector<lldb::tid_t> process_threads;
|
||||
for (size_t i = 0; m_process.GetThreadAtIndex(i); i++)
|
||||
process_threads.push_back(m_process.GetThreadAtIndex(i)->GetID());
|
||||
|
||||
Error error = Error::success();
|
||||
for (lldb::tid_t tid : process_threads)
|
||||
error = joinErrors(std::move(error), m_process_trace->TraceStart(tid));
|
||||
return error;
|
||||
// per-thread process tracing
|
||||
if (Expected<IntelPTPerThreadProcessTraceUP> trace =
|
||||
IntelPTPerThreadProcessTrace::Start(request, process_threads)) {
|
||||
m_per_thread_process_trace_up = std::move(trace.get());
|
||||
return Error::success();
|
||||
} else {
|
||||
return trace.takeError();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// individual thread tracing
|
||||
if (m_per_core_process_trace_up)
|
||||
return createStringError(inconvertibleErrorCode(),
|
||||
"Process currently traced with per-core "
|
||||
"tracing. Stop process tracing first");
|
||||
|
||||
Error error = Error::success();
|
||||
for (int64_t tid : *request.tids)
|
||||
error = joinErrors(std::move(error),
|
||||
|
@ -181,14 +225,16 @@ Error IntelPTCollector::TraceStart(
|
|||
}
|
||||
|
||||
Error IntelPTCollector::OnThreadCreated(lldb::tid_t tid) {
|
||||
if (!IsProcessTracingEnabled())
|
||||
return Error::success();
|
||||
return m_process_trace->TraceStart(tid);
|
||||
if (m_per_thread_process_trace_up)
|
||||
return m_per_thread_process_trace_up->TraceStart(tid);
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error IntelPTCollector::OnThreadDestroyed(lldb::tid_t tid) {
|
||||
if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid))
|
||||
return m_process_trace->TraceStop(tid);
|
||||
if (m_per_thread_process_trace_up &&
|
||||
m_per_thread_process_trace_up->TracesThread(tid))
|
||||
return m_per_thread_process_trace_up->TraceStop(tid);
|
||||
else if (m_thread_traces.TracesThread(tid))
|
||||
return m_thread_traces.TraceStop(tid);
|
||||
return Error::success();
|
||||
|
@ -200,26 +246,42 @@ Expected<json::Value> IntelPTCollector::GetState() const {
|
|||
return cpu_info.takeError();
|
||||
|
||||
TraceGetStateResponse state;
|
||||
state.processBinaryData.push_back(
|
||||
state.process_binary_data.push_back(
|
||||
{IntelPTDataKinds::kProcFsCpuInfo, cpu_info->size()});
|
||||
|
||||
std::vector<TraceThreadState> thread_states =
|
||||
m_thread_traces.GetThreadStates();
|
||||
state.tracedThreads.insert(state.tracedThreads.end(), thread_states.begin(),
|
||||
thread_states.end());
|
||||
state.traced_threads.insert(state.traced_threads.end(), thread_states.begin(),
|
||||
thread_states.end());
|
||||
|
||||
if (IsProcessTracingEnabled()) {
|
||||
thread_states = m_process_trace->GetThreadTraces().GetThreadStates();
|
||||
state.tracedThreads.insert(state.tracedThreads.end(), thread_states.begin(),
|
||||
thread_states.end());
|
||||
if (m_per_thread_process_trace_up) {
|
||||
thread_states =
|
||||
m_per_thread_process_trace_up->GetThreadTraces().GetThreadStates();
|
||||
state.traced_threads.insert(state.traced_threads.end(),
|
||||
thread_states.begin(), thread_states.end());
|
||||
} else if (m_per_core_process_trace_up) {
|
||||
for (size_t i = 0; m_process.GetThreadAtIndex(i); i++)
|
||||
state.traced_threads.push_back(
|
||||
TraceThreadState{m_process.GetThreadAtIndex(i)->GetID(), {}});
|
||||
|
||||
state.cores.emplace();
|
||||
m_per_core_process_trace_up->ForEachCore(
|
||||
[&](lldb::core_id_t core_id,
|
||||
const IntelPTSingleBufferTrace &core_trace) {
|
||||
state.cores->push_back({core_id,
|
||||
{{IntelPTDataKinds::kTraceBuffer,
|
||||
core_trace.GetTraceBufferSize()}}});
|
||||
});
|
||||
}
|
||||
return toJSON(state);
|
||||
}
|
||||
|
||||
Expected<const IntelPTSingleBufferTrace &>
|
||||
IntelPTCollector::GetTracedThread(lldb::tid_t tid) const {
|
||||
if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid))
|
||||
return m_process_trace->GetThreadTraces().GetTracedThread(tid);
|
||||
if (m_per_thread_process_trace_up &&
|
||||
m_per_thread_process_trace_up->TracesThread(tid))
|
||||
return m_per_thread_process_trace_up->GetThreadTraces().GetTracedThread(
|
||||
tid);
|
||||
return m_thread_traces.GetTracedThread(tid);
|
||||
}
|
||||
|
||||
|
@ -239,7 +301,10 @@ IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) const
|
|||
request.kind.c_str());
|
||||
}
|
||||
|
||||
void IntelPTCollector::ClearProcessTracing() { m_process_trace = None; }
|
||||
void IntelPTCollector::ClearProcessTracing() {
|
||||
m_per_thread_process_trace_up.reset();
|
||||
m_per_core_process_trace_up.reset();
|
||||
}
|
||||
|
||||
bool IntelPTCollector::IsSupported() {
|
||||
if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType()) {
|
||||
|
@ -251,7 +316,8 @@ bool IntelPTCollector::IsSupported() {
|
|||
}
|
||||
|
||||
bool IntelPTCollector::IsProcessTracingEnabled() const {
|
||||
return (bool)m_process_trace;
|
||||
return (bool)m_per_thread_process_trace_up ||
|
||||
(bool)m_per_core_process_trace_up;
|
||||
}
|
||||
|
||||
void IntelPTCollector::Clear() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//===-- IntelPTCollector.h -------------------------------------- -*- C++ -*-===//
|
||||
//===-- IntelPTCollector.h ------------------------------------ -*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
|
@ -11,8 +11,10 @@
|
|||
|
||||
#include "Perf.h"
|
||||
|
||||
#include "IntelPTMultiCoreTrace.h"
|
||||
#include "IntelPTSingleBufferTrace.h"
|
||||
|
||||
#include "lldb/Host/common/NativeProcessProtocol.h"
|
||||
#include "lldb/Utility/Status.h"
|
||||
#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
|
||||
#include "lldb/lldb-types.h"
|
||||
|
@ -47,17 +49,37 @@ public:
|
|||
|
||||
llvm::Error TraceStop(lldb::tid_t tid);
|
||||
|
||||
size_t GetTracedThreadsCount() const;
|
||||
|
||||
private:
|
||||
llvm::DenseMap<lldb::tid_t, IntelPTSingleBufferTraceUP> m_thread_traces;
|
||||
/// Total actual thread buffer size in bytes
|
||||
size_t m_total_buffer_size = 0;
|
||||
};
|
||||
|
||||
/// Manages a "process trace" instance.
|
||||
class IntelPTProcessTrace {
|
||||
class IntelPTPerThreadProcessTrace;
|
||||
using IntelPTPerThreadProcessTraceUP =
|
||||
std::unique_ptr<IntelPTPerThreadProcessTrace>;
|
||||
|
||||
/// Manages a "process trace" instance by tracing each thread individually.
|
||||
class IntelPTPerThreadProcessTrace {
|
||||
public:
|
||||
IntelPTProcessTrace(const TraceIntelPTStartRequest &request)
|
||||
: m_tracing_params(request) {}
|
||||
/// Start tracing the current process by tracing each of its tids
|
||||
/// individually.
|
||||
///
|
||||
/// \param[in] request
|
||||
/// Intel PT configuration parameters.
|
||||
///
|
||||
/// \param[in] current_tids
|
||||
/// List of tids currently alive. In the future, whenever a new thread is
|
||||
/// spawned, they should be traced by calling the \a TraceStart(tid) method.
|
||||
///
|
||||
/// \return
|
||||
/// An \a IntelPTMultiCoreTrace instance if tracing was successful, or
|
||||
/// an \a llvm::Error otherwise.
|
||||
static llvm::Expected<IntelPTPerThreadProcessTraceUP>
|
||||
Start(const TraceIntelPTStartRequest &request,
|
||||
llvm::ArrayRef<lldb::tid_t> current_tids);
|
||||
|
||||
bool TracesThread(lldb::tid_t tid) const;
|
||||
|
||||
|
@ -68,6 +90,9 @@ public:
|
|||
llvm::Error TraceStop(lldb::tid_t tid);
|
||||
|
||||
private:
|
||||
IntelPTPerThreadProcessTrace(const TraceIntelPTStartRequest &request)
|
||||
: m_tracing_params(request) {}
|
||||
|
||||
IntelPTThreadTraceCollection m_thread_traces;
|
||||
/// Params used to trace threads when the user started "process tracing".
|
||||
TraceIntelPTStartRequest m_tracing_params;
|
||||
|
@ -76,7 +101,9 @@ private:
|
|||
/// Main class that manages intel-pt process and thread tracing.
|
||||
class IntelPTCollector {
|
||||
public:
|
||||
IntelPTCollector();
|
||||
/// \param[in] process
|
||||
/// Process to be traced.
|
||||
IntelPTCollector(NativeProcessProtocol &process);
|
||||
|
||||
static bool IsSupported();
|
||||
|
||||
|
@ -90,11 +117,7 @@ public:
|
|||
llvm::Error TraceStop(const TraceStopRequest &request);
|
||||
|
||||
/// Implementation of the jLLDBTraceStart packet
|
||||
///
|
||||
/// \param[in] process_threads
|
||||
/// A list of all threads owned by the process.
|
||||
llvm::Error TraceStart(const TraceIntelPTStartRequest &request,
|
||||
const std::vector<lldb::tid_t> &process_threads);
|
||||
llvm::Error TraceStart(const TraceIntelPTStartRequest &request);
|
||||
|
||||
/// Implementation of the jLLDBTraceGetState packet
|
||||
llvm::Expected<llvm::json::Value> GetState() const;
|
||||
|
@ -120,11 +143,21 @@ private:
|
|||
|
||||
void ClearProcessTracing();
|
||||
|
||||
NativeProcessProtocol &m_process;
|
||||
/// Threads traced due to "thread tracing"
|
||||
IntelPTThreadTraceCollection m_thread_traces;
|
||||
/// Threads traced due to "process tracing". Only one active "process tracing"
|
||||
/// instance is assumed for a single process.
|
||||
llvm::Optional<IntelPTProcessTrace> m_process_trace;
|
||||
|
||||
/// Only one of the following "process tracing" handlers can be active at a
|
||||
/// given time.
|
||||
///
|
||||
/// \{
|
||||
/// Threads traced due to per-thread "process tracing". This might be \b
|
||||
/// nullptr.
|
||||
IntelPTPerThreadProcessTraceUP m_per_thread_process_trace_up;
|
||||
/// Cores traced due to per-core "process tracing". This might be \b nullptr.
|
||||
IntelPTMultiCoreTraceUP m_per_core_process_trace_up;
|
||||
/// \}
|
||||
|
||||
/// TSC to wall time conversion.
|
||||
TraceTscConversionUP m_tsc_conversion;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
//===-- IntelPTMultiCoreTrace.cpp -----------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "IntelPTMultiCoreTrace.h"
|
||||
|
||||
#include "Procfs.h"
|
||||
|
||||
using namespace lldb;
|
||||
using namespace lldb_private;
|
||||
using namespace process_linux;
|
||||
using namespace llvm;
|
||||
|
||||
static bool IsTotalBufferLimitReached(ArrayRef<core_id_t> cores,
|
||||
const TraceIntelPTStartRequest &request) {
|
||||
uint64_t required = cores.size() * request.trace_buffer_size;
|
||||
uint64_t limit = request.process_buffer_size_limit.getValueOr(
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
return required > limit;
|
||||
}
|
||||
|
||||
static Error IncludePerfEventParanoidMessageInError(Error &&error) {
|
||||
return createStringError(
|
||||
inconvertibleErrorCode(),
|
||||
"%s\nYou might need to rerun as sudo or to set "
|
||||
"/proc/sys/kernel/perf_event_paranoid to a value of 0 or -1.",
|
||||
toString(std::move(error)).c_str());
|
||||
}
|
||||
|
||||
Expected<IntelPTMultiCoreTraceUP> IntelPTMultiCoreTrace::StartOnAllCores(
|
||||
const TraceIntelPTStartRequest &request) {
|
||||
Expected<ArrayRef<core_id_t>> core_ids = GetAvailableLogicalCoreIDs();
|
||||
if (!core_ids)
|
||||
return core_ids.takeError();
|
||||
|
||||
if (IsTotalBufferLimitReached(*core_ids, request))
|
||||
return createStringError(
|
||||
inconvertibleErrorCode(),
|
||||
"The process can't be traced because the process trace size limit "
|
||||
"has been reached. Consider retracing with a higher limit.");
|
||||
|
||||
llvm::DenseMap<core_id_t, IntelPTSingleBufferTraceUP> buffers;
|
||||
for (core_id_t core_id : *core_ids) {
|
||||
if (Expected<IntelPTSingleBufferTraceUP> core_trace =
|
||||
IntelPTSingleBufferTrace::Start(request, /*tid=*/None, core_id))
|
||||
buffers.try_emplace(core_id, std::move(*core_trace));
|
||||
else
|
||||
return IncludePerfEventParanoidMessageInError(core_trace.takeError());
|
||||
}
|
||||
|
||||
return IntelPTMultiCoreTraceUP(new IntelPTMultiCoreTrace(std::move(buffers)));
|
||||
}
|
||||
|
||||
void IntelPTMultiCoreTrace::ForEachCore(
|
||||
std::function<void(core_id_t core_id,
|
||||
const IntelPTSingleBufferTrace &core_trace)>
|
||||
callback) {
|
||||
for (auto &it : m_traces_per_core)
|
||||
callback(it.first, *it.second);
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
//===-- IntelPTMultiCoreTrace.h ------------------------------- -*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef liblldb_IntelPTMultiCoreTrace_H_
|
||||
#define liblldb_IntelPTMultiCoreTrace_H_
|
||||
|
||||
#include "IntelPTSingleBufferTrace.h"
|
||||
|
||||
#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
|
||||
#include "lldb/lldb-types.h"
|
||||
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace lldb_private {
|
||||
namespace process_linux {
|
||||
|
||||
class IntelPTMultiCoreTrace;
|
||||
using IntelPTMultiCoreTraceUP = std::unique_ptr<IntelPTMultiCoreTrace>;
|
||||
|
||||
class IntelPTMultiCoreTrace {
|
||||
public:
|
||||
/// Start tracing all CPU cores.
|
||||
///
|
||||
/// \param[in] request
|
||||
/// Intel PT configuration parameters.
|
||||
///
|
||||
/// \return
|
||||
/// An \a IntelPTMultiCoreTrace instance if tracing was successful, or
|
||||
/// an \a llvm::Error otherwise.
|
||||
static llvm::Expected<IntelPTMultiCoreTraceUP>
|
||||
StartOnAllCores(const TraceIntelPTStartRequest &request);
|
||||
|
||||
/// Execute the provided callback on each core that is being traced.
|
||||
///
|
||||
/// \param[in] callback.core_id
|
||||
/// The core id that is being traced.
|
||||
///
|
||||
/// \param[in] callback.core_trace
|
||||
/// The single-buffer trace instance for the given core.
|
||||
void
|
||||
ForEachCore(std::function<void(lldb::core_id_t core_id,
|
||||
const IntelPTSingleBufferTrace &core_trace)>
|
||||
callback);
|
||||
|
||||
private:
|
||||
IntelPTMultiCoreTrace(
|
||||
llvm::DenseMap<lldb::core_id_t, IntelPTSingleBufferTraceUP>
|
||||
&&traces_per_core)
|
||||
: m_traces_per_core(std::move(traces_per_core)) {}
|
||||
|
||||
llvm::DenseMap<lldb::core_id_t, IntelPTSingleBufferTraceUP> m_traces_per_core;
|
||||
};
|
||||
|
||||
} // namespace process_linux
|
||||
} // namespace lldb_private
|
||||
|
||||
#endif // liblldb_IntelPTMultiCoreTrace_H_
|
|
@ -260,14 +260,16 @@ IntelPTSingleBufferTrace::GetTraceBuffer(size_t offset, size_t size) const {
|
|||
|
||||
Expected<IntelPTSingleBufferTraceUP>
|
||||
IntelPTSingleBufferTrace::Start(const TraceIntelPTStartRequest &request,
|
||||
lldb::tid_t tid) {
|
||||
Optional<lldb::tid_t> tid,
|
||||
Optional<core_id_t> core_id) {
|
||||
#ifndef PERF_ATTR_SIZE_VER5
|
||||
return createStringError(inconvertibleErrorCode(),
|
||||
"Intel PT Linux perf event not supported");
|
||||
#else
|
||||
Log *log = GetLog(POSIXLog::Trace);
|
||||
|
||||
LLDB_LOG(log, "Will start tracing thread id {0}", tid);
|
||||
LLDB_LOG(log, "Will start tracing thread id {0} and cpu id {1}", tid,
|
||||
core_id);
|
||||
|
||||
if (__builtin_popcount(request.trace_buffer_size) != 1 ||
|
||||
request.trace_buffer_size < 4096) {
|
||||
|
@ -291,13 +293,13 @@ IntelPTSingleBufferTrace::Start(const TraceIntelPTStartRequest &request,
|
|||
LLDB_LOG(log, "Will create trace buffer of size {0}",
|
||||
request.trace_buffer_size);
|
||||
|
||||
if (Expected<PerfEvent> perf_event = PerfEvent::Init(*attr, tid)) {
|
||||
if (Expected<PerfEvent> perf_event = PerfEvent::Init(*attr, tid, core_id)) {
|
||||
if (Error mmap_err = perf_event->MmapMetadataAndBuffers(buffer_numpages,
|
||||
buffer_numpages)) {
|
||||
return std::move(mmap_err);
|
||||
}
|
||||
return IntelPTSingleBufferTraceUP(
|
||||
new IntelPTSingleBufferTrace(std::move(*perf_event), tid));
|
||||
new IntelPTSingleBufferTrace(std::move(*perf_event)));
|
||||
} else {
|
||||
return perf_event.takeError();
|
||||
}
|
||||
|
|
|
@ -23,10 +23,8 @@ namespace process_linux {
|
|||
|
||||
llvm::Expected<uint32_t> GetIntelPTOSEventType();
|
||||
|
||||
class IntelPTTrace;
|
||||
class IntelPTSingleBufferTrace;
|
||||
|
||||
using IntelPTThreadTraceUP = std::unique_ptr<IntelPTTrace>;
|
||||
using IntelPTSingleBufferTraceUP = std::unique_ptr<IntelPTSingleBufferTrace>;
|
||||
|
||||
/// This class wraps a single perf event collecting intel pt data in a single
|
||||
|
@ -39,13 +37,19 @@ public:
|
|||
/// Intel PT configuration parameters.
|
||||
///
|
||||
/// \param[in] tid
|
||||
/// The tid of the thread to be traced.
|
||||
/// The tid of the thread to be traced. If \b None, then this traces all
|
||||
/// threads of all processes.
|
||||
///
|
||||
/// \param[in] core_id
|
||||
/// The CPU core id where to trace. If \b None, then this traces all CPUs.
|
||||
///
|
||||
/// \return
|
||||
/// A \a IntelPTSingleBufferTrace instance if tracing was successful, or
|
||||
/// an \a llvm::Error otherwise.
|
||||
static llvm::Expected<IntelPTSingleBufferTraceUP>
|
||||
Start(const TraceIntelPTStartRequest &request, lldb::tid_t tid);
|
||||
Start(const TraceIntelPTStartRequest &request,
|
||||
llvm::Optional<lldb::tid_t> tid,
|
||||
llvm::Optional<lldb::core_id_t> core_id = llvm::None);
|
||||
|
||||
/// \return
|
||||
/// The bytes requested by a jLLDBTraceGetBinaryData packet that was routed
|
||||
|
@ -80,10 +84,7 @@ private:
|
|||
///
|
||||
/// \param[in] perf_event
|
||||
/// perf event configured for IntelPT.
|
||||
///
|
||||
/// \param[in] tid
|
||||
/// The thread being traced.
|
||||
IntelPTSingleBufferTrace(PerfEvent &&perf_event, lldb::tid_t tid)
|
||||
IntelPTSingleBufferTrace(PerfEvent &&perf_event)
|
||||
: m_perf_event(std::move(perf_event)) {}
|
||||
|
||||
/// perf event configured for IntelPT.
|
||||
|
|
|
@ -312,7 +312,7 @@ NativeProcessLinux::NativeProcessLinux(::pid_t pid, int terminal_fd,
|
|||
const ArchSpec &arch, MainLoop &mainloop,
|
||||
llvm::ArrayRef<::pid_t> tids)
|
||||
: NativeProcessELF(pid, terminal_fd, delegate), m_arch(arch),
|
||||
m_main_loop(mainloop) {
|
||||
m_main_loop(mainloop), m_intel_pt_collector(*this) {
|
||||
if (m_terminal_fd != -1) {
|
||||
Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK);
|
||||
assert(status.Success());
|
||||
|
@ -1967,10 +1967,7 @@ Error NativeProcessLinux::TraceStart(StringRef json_request, StringRef type) {
|
|||
if (Expected<TraceIntelPTStartRequest> request =
|
||||
json::parse<TraceIntelPTStartRequest>(json_request,
|
||||
"TraceIntelPTStartRequest")) {
|
||||
std::vector<lldb::tid_t> process_threads;
|
||||
for (auto &thread : m_threads)
|
||||
process_threads.push_back(thread->GetID());
|
||||
return m_intel_pt_collector.TraceStart(*request, process_threads);
|
||||
return m_intel_pt_collector.TraceStart(*request);
|
||||
} else
|
||||
return request.takeError();
|
||||
}
|
||||
|
|
|
@ -117,10 +117,13 @@ void resource_handle::FileDescriptorDeleter::operator()(long *ptr) {
|
|||
}
|
||||
|
||||
llvm::Expected<PerfEvent> PerfEvent::Init(perf_event_attr &attr,
|
||||
lldb::pid_t pid, int cpu,
|
||||
int group_fd, unsigned long flags) {
|
||||
Optional<lldb::pid_t> pid,
|
||||
Optional<lldb::core_id_t> cpu,
|
||||
Optional<int> group_fd,
|
||||
unsigned long flags) {
|
||||
errno = 0;
|
||||
long fd = syscall(SYS_perf_event_open, &attr, pid, cpu, group_fd, flags);
|
||||
long fd = syscall(SYS_perf_event_open, &attr, pid.getValueOr(-1),
|
||||
cpu.getValueOr(-1), group_fd.getValueOr(-1), flags);
|
||||
if (fd == -1) {
|
||||
std::string err_msg =
|
||||
llvm::formatv("perf event syscall failed: {0}", std::strerror(errno));
|
||||
|
@ -130,8 +133,9 @@ llvm::Expected<PerfEvent> PerfEvent::Init(perf_event_attr &attr,
|
|||
}
|
||||
|
||||
llvm::Expected<PerfEvent> PerfEvent::Init(perf_event_attr &attr,
|
||||
lldb::pid_t pid) {
|
||||
return Init(attr, pid, -1, -1, 0);
|
||||
Optional<lldb::pid_t> pid,
|
||||
Optional<lldb::core_id_t> cpu) {
|
||||
return Init(attr, pid, cpu, -1, 0);
|
||||
}
|
||||
|
||||
llvm::Expected<resource_handle::MmapUP>
|
||||
|
|
|
@ -109,13 +109,16 @@ public:
|
|||
/// Configuration information for the event.
|
||||
///
|
||||
/// \param[in] pid
|
||||
/// The process to be monitored by the event.
|
||||
/// The process or thread to be monitored by the event. If \b None, then
|
||||
/// all processes and threads are monitored.
|
||||
///
|
||||
/// \param[in] cpu
|
||||
/// The cpu to be monitored by the event.
|
||||
/// The cpu to be monitored by the event. If \b None, then all cpus are
|
||||
/// monitored.
|
||||
///
|
||||
/// \param[in] group_fd
|
||||
/// File descriptor of the group leader.
|
||||
/// File descriptor of the group leader. If \b None, then this perf_event
|
||||
/// doesn't belong to a preexisting group.
|
||||
///
|
||||
/// \param[in] flags
|
||||
/// Bitmask of additional configuration flags.
|
||||
|
@ -123,8 +126,10 @@ public:
|
|||
/// \return
|
||||
/// If the perf_event_open syscall was successful, a minimal \a PerfEvent
|
||||
/// instance, or an \a llvm::Error otherwise.
|
||||
static llvm::Expected<PerfEvent> Init(perf_event_attr &attr, lldb::pid_t pid,
|
||||
int cpu, int group_fd,
|
||||
static llvm::Expected<PerfEvent> Init(perf_event_attr &attr,
|
||||
llvm::Optional<lldb::pid_t> pid,
|
||||
llvm::Optional<lldb::core_id_t> cpu,
|
||||
llvm::Optional<int> group_fd,
|
||||
unsigned long flags);
|
||||
|
||||
/// Create a new performance monitoring event via the perf_event_open syscall
|
||||
|
@ -137,8 +142,11 @@ public:
|
|||
/// Configuration information for the event.
|
||||
///
|
||||
/// \param[in] pid
|
||||
/// The process to be monitored by the event.
|
||||
static llvm::Expected<PerfEvent> Init(perf_event_attr &attr, lldb::pid_t pid);
|
||||
/// The process or thread to be monitored by the event. If \b None, then
|
||||
/// all threads and processes are monitored.
|
||||
static llvm::Expected<PerfEvent>
|
||||
Init(perf_event_attr &attr, llvm::Optional<lldb::pid_t> pid,
|
||||
llvm::Optional<lldb::core_id_t> core = llvm::None);
|
||||
|
||||
/// Mmap the metadata page and the data and aux buffers of the perf event and
|
||||
/// expose them through \a PerfEvent::GetMetadataPage() , \a
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "lldb/Host/linux/Support.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
|
||||
using namespace lldb;
|
||||
using namespace lldb_private;
|
||||
using namespace process_linux;
|
||||
using namespace llvm;
|
||||
|
@ -29,18 +30,18 @@ Expected<ArrayRef<uint8_t>> lldb_private::process_linux::GetProcfsCpuInfo() {
|
|||
return *cpu_info;
|
||||
}
|
||||
|
||||
Expected<std::vector<int>>
|
||||
Expected<std::vector<core_id_t>>
|
||||
lldb_private::process_linux::GetAvailableLogicalCoreIDs(StringRef cpuinfo) {
|
||||
SmallVector<StringRef, 8> lines;
|
||||
cpuinfo.split(lines, "\n", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
|
||||
std::vector<int> logical_cores;
|
||||
std::vector<core_id_t> logical_cores;
|
||||
|
||||
for (StringRef line : lines) {
|
||||
std::pair<StringRef, StringRef> key_value = line.split(':');
|
||||
auto key = key_value.first.trim();
|
||||
auto val = key_value.second.trim();
|
||||
if (key == "processor") {
|
||||
int processor;
|
||||
core_id_t processor;
|
||||
if (val.getAsInteger(10, processor))
|
||||
return createStringError(
|
||||
inconvertibleErrorCode(),
|
||||
|
@ -51,17 +52,17 @@ lldb_private::process_linux::GetAvailableLogicalCoreIDs(StringRef cpuinfo) {
|
|||
return logical_cores;
|
||||
}
|
||||
|
||||
llvm::Expected<llvm::ArrayRef<int>>
|
||||
llvm::Expected<llvm::ArrayRef<core_id_t>>
|
||||
lldb_private::process_linux::GetAvailableLogicalCoreIDs() {
|
||||
static Optional<std::vector<int>> logical_cores_ids;
|
||||
static Optional<std::vector<core_id_t>> logical_cores_ids;
|
||||
if (!logical_cores_ids) {
|
||||
// We find the actual list of core ids by parsing /proc/cpuinfo
|
||||
Expected<ArrayRef<uint8_t>> cpuinfo = GetProcfsCpuInfo();
|
||||
if (!cpuinfo)
|
||||
return cpuinfo.takeError();
|
||||
|
||||
Expected<std::vector<int>> core_ids = GetAvailableLogicalCoreIDs(StringRef(
|
||||
reinterpret_cast<const char *>(cpuinfo->data()), cpuinfo->size()));
|
||||
Expected<std::vector<core_id_t>> core_ids = GetAvailableLogicalCoreIDs(
|
||||
StringRef(reinterpret_cast<const char *>(cpuinfo->data())));
|
||||
if (!core_ids)
|
||||
return core_ids.takeError();
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include <sys/ptrace.h>
|
||||
|
||||
#include "lldb/lldb-types.h"
|
||||
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
#include <vector>
|
||||
|
@ -43,13 +45,13 @@ llvm::Expected<llvm::ArrayRef<uint8_t>> GetProcfsCpuInfo();
|
|||
/// \return
|
||||
/// A list of available logical core ids given the contents of
|
||||
/// /proc/cpuinfo.
|
||||
llvm::Expected<std::vector<int>>
|
||||
llvm::Expected<std::vector<lldb::core_id_t>>
|
||||
GetAvailableLogicalCoreIDs(llvm::StringRef cpuinfo);
|
||||
|
||||
/// \return
|
||||
/// A list with all the logical cores available in the system and cache it
|
||||
/// if errors didn't happen.
|
||||
llvm::Expected<llvm::ArrayRef<int>> GetAvailableLogicalCoreIDs();
|
||||
llvm::Expected<llvm::ArrayRef<lldb::core_id_t>> GetAvailableLogicalCoreIDs();
|
||||
|
||||
} // namespace process_linux
|
||||
} // namespace lldb_private
|
||||
|
|
|
@ -252,7 +252,7 @@ void TraceIntelPT::DoRefreshLiveProcessState(
|
|||
return;
|
||||
}
|
||||
|
||||
for (const TraceThreadState &thread_state : state->tracedThreads) {
|
||||
for (const TraceThreadState &thread_state : state->traced_threads) {
|
||||
ThreadSP thread_sp =
|
||||
m_live_process->GetThreadList().FindThreadByID(thread_state.tid);
|
||||
m_thread_decoders.emplace(
|
||||
|
@ -354,9 +354,9 @@ llvm::Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
|
|||
|
||||
Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
|
||||
StructuredData::ObjectSP configuration) {
|
||||
size_t trace_buffer_size = kDefaultTraceBufferSize;
|
||||
uint64_t trace_buffer_size = kDefaultTraceBufferSize;
|
||||
bool enable_tsc = kDefaultEnableTscValue;
|
||||
Optional<size_t> psb_period = kDefaultPsbPeriod;
|
||||
Optional<uint64_t> psb_period = kDefaultPsbPeriod;
|
||||
|
||||
if (configuration) {
|
||||
if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) {
|
||||
|
|
|
@ -57,8 +57,8 @@ let Command = "process trace start intel pt" in {
|
|||
Group<1>,
|
||||
Arg<"Value">,
|
||||
Desc<"Maximum total trace size per process in bytes. This limit applies to "
|
||||
"the sum of the sizes of all thread traces of this process, excluding "
|
||||
"the ones created with the \"thread trace start\" command. "
|
||||
"the sum of the sizes of all thread and core traces of this process, "
|
||||
"excluding the ones created with the \"thread trace start\" command. "
|
||||
"Whenever a thread is attempted to be traced due to this command and "
|
||||
"the limit would be reached, the process is stopped with a "
|
||||
"\"processor trace\" reason, so that the user can retrace the process "
|
||||
|
|
|
@ -199,12 +199,12 @@ void Trace::RefreshLiveProcessState() {
|
|||
}
|
||||
|
||||
for (const TraceThreadState &thread_state :
|
||||
live_process_state->tracedThreads) {
|
||||
for (const TraceBinaryData &item : thread_state.binaryData)
|
||||
live_process_state->traced_threads) {
|
||||
for (const TraceBinaryData &item : thread_state.binary_data)
|
||||
m_live_thread_data[thread_state.tid][item.kind] = item.size;
|
||||
}
|
||||
|
||||
for (const TraceBinaryData &item : live_process_state->processBinaryData)
|
||||
for (const TraceBinaryData &item : live_process_state->process_binary_data)
|
||||
m_live_process_data[item.kind] = item.size;
|
||||
|
||||
DoRefreshLiveProcessState(std::move(live_process_state));
|
||||
|
|
|
@ -87,24 +87,42 @@ json::Value toJSON(const TraceBinaryData &packet) {
|
|||
bool fromJSON(const json::Value &value, TraceThreadState &packet, Path path) {
|
||||
ObjectMapper o(value, path);
|
||||
return o && o.map("tid", packet.tid) &&
|
||||
o.map("binaryData", packet.binaryData);
|
||||
o.map("binaryData", packet.binary_data);
|
||||
}
|
||||
|
||||
json::Value toJSON(const TraceThreadState &packet) {
|
||||
return json::Value(
|
||||
Object{{"tid", packet.tid}, {"binaryData", packet.binaryData}});
|
||||
Object{{"tid", packet.tid}, {"binaryData", packet.binary_data}});
|
||||
}
|
||||
|
||||
bool fromJSON(const json::Value &value, TraceGetStateResponse &packet,
|
||||
Path path) {
|
||||
ObjectMapper o(value, path);
|
||||
return o && o.map("tracedThreads", packet.tracedThreads) &&
|
||||
o.map("processBinaryData", packet.processBinaryData);
|
||||
return o && o.map("tracedThreads", packet.traced_threads) &&
|
||||
o.map("processBinaryData", packet.process_binary_data) &&
|
||||
o.map("cores", packet.cores);
|
||||
}
|
||||
|
||||
json::Value toJSON(const TraceGetStateResponse &packet) {
|
||||
return json::Value(Object{{"tracedThreads", packet.tracedThreads},
|
||||
{"processBinaryData", packet.processBinaryData}});
|
||||
return json::Value(Object{{"tracedThreads", packet.traced_threads},
|
||||
{"processBinaryData", packet.process_binary_data},
|
||||
{"cores", packet.cores}});
|
||||
}
|
||||
|
||||
bool fromJSON(const json::Value &value, TraceCoreState &packet,
|
||||
json::Path path) {
|
||||
ObjectMapper o(value, path);
|
||||
int64_t core_id;
|
||||
if (!o || !o.map("coreId", core_id) ||
|
||||
!o.map("binaryData", packet.binary_data))
|
||||
return false;
|
||||
packet.core_id = static_cast<lldb::core_id_t>(core_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
json::Value toJSON(const TraceCoreState &packet) {
|
||||
return json::Value(
|
||||
Object{{"coreId", packet.core_id}, {"binaryData", packet.binary_data}});
|
||||
}
|
||||
/// \}
|
||||
|
||||
|
|
|
@ -16,6 +16,10 @@ namespace lldb_private {
|
|||
const char *IntelPTDataKinds::kProcFsCpuInfo = "procfsCpuInfo";
|
||||
const char *IntelPTDataKinds::kTraceBuffer = "traceBuffer";
|
||||
|
||||
bool TraceIntelPTStartRequest::IsPerCoreTracing() const {
|
||||
return per_core_tracing.getValueOr(false);
|
||||
}
|
||||
|
||||
bool fromJSON(const json::Value &value, TraceIntelPTStartRequest &packet,
|
||||
Path path) {
|
||||
ObjectMapper o(value, path);
|
||||
|
|
|
@ -30,6 +30,8 @@ class TestTraceStartStopMultipleThreads(TraceIntelPTTestCaseBase):
|
|||
self.expect("continue")
|
||||
self.expect("thread trace dump instructions", substrs=['main.cpp:4'])
|
||||
|
||||
self.traceStopProcess()
|
||||
|
||||
@skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
|
||||
@testSBAPIAndCommands
|
||||
def testStartMultipleLiveThreadsWithStops(self):
|
||||
|
@ -65,6 +67,8 @@ class TestTraceStartStopMultipleThreads(TraceIntelPTTestCaseBase):
|
|||
|
||||
self.expect("thread trace dump instructions 2", substrs=['not traced'])
|
||||
|
||||
self.traceStopProcess()
|
||||
|
||||
@skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
|
||||
@testSBAPIAndCommands
|
||||
def testStartMultipleLiveThreadsWithStops(self):
|
||||
|
@ -100,6 +104,8 @@ class TestTraceStartStopMultipleThreads(TraceIntelPTTestCaseBase):
|
|||
self.expect("thread trace dump instructions 1", substrs=['not traced'])
|
||||
self.expect("thread trace dump instructions 2", substrs=['not traced'])
|
||||
|
||||
self.traceStopProcess()
|
||||
|
||||
@skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
|
||||
def testStartMultipleLiveThreadsWithThreadStartAll(self):
|
||||
self.build()
|
||||
|
@ -156,6 +162,8 @@ class TestTraceStartStopMultipleThreads(TraceIntelPTTestCaseBase):
|
|||
@skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
|
||||
@testSBAPIAndCommands
|
||||
def testStartPerCoreSession(self):
|
||||
self.skipIfPerCoreTracingIsNotSupported()
|
||||
|
||||
self.build()
|
||||
exe = self.getBuildArtifact("a.out")
|
||||
self.dbg.CreateTarget(exe)
|
||||
|
@ -163,6 +171,32 @@ class TestTraceStartStopMultipleThreads(TraceIntelPTTestCaseBase):
|
|||
self.expect("b main")
|
||||
self.expect("r")
|
||||
|
||||
self.traceStartProcess(
|
||||
error=True, perCoreTracing=True,
|
||||
substrs=["Per-core tracing is not supported"])
|
||||
# We should fail if we hit the total buffer limit. Useful if the number
|
||||
# of cores is huge.
|
||||
self.traceStartProcess(error="True", processBufferSizeLimit=100,
|
||||
perCoreTracing=True,
|
||||
substrs=["The process can't be traced because the process trace size "
|
||||
"limit has been reached. Consider retracing with a higher limit."])
|
||||
|
||||
self.traceStartProcess(perCoreTracing=True)
|
||||
self.traceStopProcess()
|
||||
|
||||
self.traceStartProcess(perCoreTracing=True)
|
||||
# We can't support multiple per-core tracing sessions.
|
||||
self.traceStartProcess(error=True, perCoreTracing=True,
|
||||
substrs=["Process currently traced. Stop process tracing first"])
|
||||
|
||||
# We can't support tracing per thread is per core is enabled.
|
||||
self.traceStartThread(
|
||||
error="True",
|
||||
substrs=["Process currently traced with per-core tracing. Stop process tracing first"])
|
||||
|
||||
# We can't stop individual thread when per core is enabled.
|
||||
self.traceStopThread(error="True",
|
||||
substrs=["Can't stop tracing an individual thread when per-core process tracing is enabled"])
|
||||
|
||||
# The GetState packet should return trace buffers per core and at least one traced thread
|
||||
self.expect("""process plugin packet send 'jLLDBTraceGetState:{"type":"intel-pt"}]'""",
|
||||
substrs=['''[{"kind":"traceBuffer","size":4096}],"coreId":''', '"tid":'])
|
||||
|
||||
self.traceStopProcess()
|
||||
|
|
|
@ -18,7 +18,7 @@ using namespace process_linux;
|
|||
using namespace llvm;
|
||||
|
||||
TEST(Perf, HardcodedLogicalCoreIDs) {
|
||||
Expected<std::vector<int>> core_ids =
|
||||
Expected<std::vector<lldb::core_id_t>> core_ids =
|
||||
GetAvailableLogicalCoreIDs(R"(processor : 13
|
||||
vendor_id : GenuineIntel
|
||||
cpu family : 6
|
||||
|
@ -98,7 +98,7 @@ TEST(Perf, RealLogicalCoreIDs) {
|
|||
GTEST_SKIP() << toString(buffer_or_error.takeError());
|
||||
|
||||
// At this point we shouldn't fail parsing the core ids
|
||||
Expected<ArrayRef<int>> core_ids = GetAvailableLogicalCoreIDs();
|
||||
Expected<ArrayRef<lldb::core_id_t>> core_ids = GetAvailableLogicalCoreIDs();
|
||||
ASSERT_TRUE((bool)core_ids);
|
||||
ASSERT_GT((int)core_ids->size(), 0) << "We must see at least one core";
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue