wallace created this revision.
wallace added a reviewer: clayborg.
Herald added subscribers: lldb-commits, dang.
Herald added a project: LLDB.
wallace requested review of this revision.
Herald added a subscriber: JDevlieghere.
This implements the interactive trace start and stop methods.
There's a lot of boilerplate code due to the gdb-remote protocol, but the main
changes are:
- Implementation of the jLLDBTraceStop packet, which is quite simple.
- Implementation of the jLLDBTraceStart packet, which accepts plug-in specific
params.
- Implementation of the jLLDBTraceQueryData, used for querying in json format
various data, in a generic way so that it can be resused by trace plug-ins. I'm
using it for querying the trace buffers and cpu config needed to decode.
- Created a set of well-defined structures that represent all packets and
params send across the gdb-remote protocol, so that serialization and
deserialization with JSON is as strongly-typed as possible.
- Created an IntelPTDecoder for live threads, that use the debugger's stop id
as checkpoint for its internal cache.
- Added tests
Depends on D90729 <https://reviews.llvm.org/D90729>.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D91679
Files:
lldb/docs/lldb-gdb-remote.txt
lldb/include/lldb/Core/PluginManager.h
lldb/include/lldb/Host/common/NativeProcessProtocol.h
lldb/include/lldb/Target/Process.h
lldb/include/lldb/Target/Trace.h
lldb/include/lldb/Utility/StringExtractorGDBRemote.h
lldb/include/lldb/Utility/TraceOptions.h
lldb/include/lldb/lldb-private-interfaces.h
lldb/source/Commands/CommandObjectThreadUtil.cpp
lldb/source/Commands/CommandObjectThreadUtil.h
lldb/source/Core/PluginManager.cpp
lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp
lldb/source/Plugins/Process/Linux/ProcessorTrace.h
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp
lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h
lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
lldb/source/Target/Trace.cpp
lldb/source/Utility/StringExtractorGDBRemote.cpp
lldb/source/Utility/TraceOptions.cpp
lldb/test/API/commands/trace/TestTraceStartStop.py
Index: lldb/test/API/commands/trace/TestTraceStartStop.py
===================================================================
--- lldb/test/API/commands/trace/TestTraceStartStop.py
+++ lldb/test/API/commands/trace/TestTraceStartStop.py
@@ -3,6 +3,8 @@
from lldbsuite.test import lldbutil
from lldbsuite.test.decorators import *
+ADDRESS_REGEX = '0x[0-9a-fA-F]*'
+
class TestTraceLoad(TestBase):
mydir = TestBase.compute_mydir(__file__)
@@ -49,20 +51,57 @@
self.expect("r")
+ # This fails because "trace start" hasn't been called yet
+ self.expect("thread trace stop", error=True,
+ substrs=["error: Process is not being traced"])
+
+
# the help command should be the intel-pt one now
self.expect("help thread trace start",
substrs=["Start tracing one or more threads with intel-pt.",
"Syntax: thread trace start [<thread-index> <thread-index> ...] [<intel-pt-options>]"])
- self.expect("thread trace start",
- patterns=["would trace tid .* with size_in_kb 4 and custom_config 0"])
-
- self.expect("thread trace start --size 20 --custom-config 1",
- patterns=["would trace tid .* with size_in_kb 20 and custom_config 1"])
-
- # This fails because "trace stop" is not yet implemented.
+ # We start tracing with a small buffer size
+ self.expect("thread trace start --size 1")
+
+ # We fail if we try to trace again
+ self.expect("thread trace start", error=True,
+ substrs=["error: tracing already active on this thread"])
+
+ # We can reconstruct the single instruction executed in the first line
+ self.expect("n")
+ self.expect("thread trace dump instructions",
+ patterns=[f'''thread #1: tid = .*, total instructions = 1
+ a.out`main \+ 4 at main.cpp:2
+ \[0\] {ADDRESS_REGEX} movl'''])
+
+ # We can reconstruct the instructions up to the second line
+ self.expect("n")
+ self.expect("thread trace dump instructions",
+ patterns=[f'''thread #1: tid = .*, total instructions = 5
+ a.out`main \+ 4 at main.cpp:2
+ \[0\] {ADDRESS_REGEX} movl .*
+ a.out`main \+ 11 at main.cpp:4
+ \[1\] {ADDRESS_REGEX} movl .*
+ \[2\] {ADDRESS_REGEX} jmp .* ; <\+28> at main.cpp:4
+ a.out`main \+ 28 at main.cpp:4
+ \[3\] {ADDRESS_REGEX} cmpl .*
+ \[4\] {ADDRESS_REGEX} jle .* ; <\+20> at main.cpp:5'''])
+
+ # We stop tracing
+ self.expect("thread trace stop")
+
+ # We can't stop twice
self.expect("thread trace stop", error=True,
- substrs=["error: Process is not being traced"])
+ substrs=["error: Process is not being traced."])
+
+ # We trace again from scratch
+ self.expect("thread trace start")
+ self.expect("n")
+ self.expect("thread trace dump instructions",
+ patterns=[f'''thread #1: tid = .*, total instructions = 1
+ a.out`main \+ 20 at main.cpp:5
+ \[0\] {ADDRESS_REGEX} xorl'''])
self.expect("c")
# Now the process has finished, so the commands should fail
Index: lldb/source/Utility/TraceOptions.cpp
===================================================================
--- lldb/source/Utility/TraceOptions.cpp
+++ lldb/source/Utility/TraceOptions.cpp
@@ -8,11 +8,45 @@
#include "lldb/Utility/TraceOptions.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StringExtractor.h"
+
using namespace lldb_private;
namespace llvm {
namespace json {
+bool fromJSON(const Value &value, TraceStopPacket &packet, Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("type", packet.type) && o.map("tid", packet.tid);
+}
+
+Value toJSON(const TraceStopPacket &packet) {
+ return Object{{"type", packet.type}, {"tid", packet.tid}};
+}
+
+Value toJSON(const TraceQueryDataSimplePacket &packet) {
+ return Object{{"query", packet.query}, {"type", packet.type}};
+}
+
+bool fromJSON(const Value &value, TraceIntelPTStartPacketParams &packet,
+ Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("bufferSizeInKB", packet.buffer_size_in_kb) &&
+ o.map("perfConfig", packet.perf_config);
+}
+
+bool fromJSON(const Value &value, TraceStartSimplePacket &packet, Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("type", packet.type) && o.map("tid", packet.tid);
+}
+
+bool fromJSON(const Value &value, TraceQueryDataSimplePacket &packet,
+ Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("type", packet.type) && o.map("query", packet.query);
+}
+
bool fromJSON(const Value &value, TraceTypeInfo &info, Path path) {
ObjectMapper o(value, path);
if (!o)
@@ -21,5 +55,55 @@
return o.map("name", info.name);
}
+bool fromJSON(const Value &value, TraceIntelPTCPUConfig &pt_cpu, Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("vendor", pt_cpu.vendor) &&
+ o.map("family", pt_cpu.family) && o.map("model", pt_cpu.model) &&
+ o.map("stepping", pt_cpu.stepping);
+}
+
+bool fromJSON(const Value &value, TraceIntelPTBuffer &response, Path path) {
+ ObjectMapper o(value, path);
+ std::string string_data;
+ if (!o || !o.map("data", string_data))
+ return false;
+
+ StringExtractor extractor(string_data);
+ response.data.resize(string_data.size() / 2);
+ MutableArrayRef<uint8_t> data_ref(response.data);
+ extractor.GetHexBytesAvail(data_ref);
+ return true;
+}
+
+bool fromJSON(const Value &value, TraceIntelPTQueryBufferParams ¶ms,
+ Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("tid", params.tid);
+}
+
+Value toJSON(const TraceIntelPTStartPacketParams &packet) {
+ return Object{{"bufferSizeInKB", packet.buffer_size_in_kb},
+ {"perfConfig", packet.perf_config}};
+}
+
+Value toJSON(const TraceIntelPTCPUConfig &cpu_config) {
+ return Object{{"family", cpu_config.family},
+ {"model", cpu_config.model},
+ {"stepping", cpu_config.stepping},
+ {"vendor", cpu_config.vendor}};
+}
+
+Value toJSON(const TraceIntelPTBuffer &buffer) {
+ StreamString stream;
+ for (const uint8_t &datum : buffer.data)
+ stream.PutHex8(datum);
+
+ return json::Object{{"data", stream.GetString().str()}};
+}
+
+Value toJSON(const TraceIntelPTQueryBufferParams ¶ms) {
+ return json::Object{{"tid", params.tid}};
+}
+
} // namespace json
} // namespace llvm
Index: lldb/source/Utility/StringExtractorGDBRemote.cpp
===================================================================
--- lldb/source/Utility/StringExtractorGDBRemote.cpp
+++ lldb/source/Utility/StringExtractorGDBRemote.cpp
@@ -310,8 +310,15 @@
return eServerPacketType_jTraceStart;
if (PACKET_STARTS_WITH("jTraceStop:"))
return eServerPacketType_jTraceStop;
+
if (PACKET_MATCHES("jLLDBTraceSupportedType"))
return eServerPacketType_jLLDBTraceSupportedType;
+ if (PACKET_STARTS_WITH("jLLDBTraceStop:"))
+ return eServerPacketType_jLLDBTraceStop;
+ if (PACKET_STARTS_WITH("jLLDBTraceStart:"))
+ return eServerPacketType_jLLDBTraceStart;
+ if (PACKET_STARTS_WITH("jLLDBTraceQueryData:"))
+ return eServerPacketType_jLLDBTraceQueryData;
break;
case 'v':
Index: lldb/source/Target/Trace.cpp
===================================================================
--- lldb/source/Target/Trace.cpp
+++ lldb/source/Target/Trace.cpp
@@ -72,6 +72,19 @@
return createInvalidPlugInError(json_session.trace.type);
}
+Expected<lldb::TraceSP> Trace::FindPlugin(llvm::StringRef plugin_name,
+ Process &process) {
+ if (!process.IsLiveDebugSession())
+ return createStringError(inconvertibleErrorCode(),
+ "Can't trace non-live processes");
+
+ ConstString name(plugin_name);
+ if (auto create_callback =
+ PluginManager::GetTraceCreateCallbackForLiveProcess(name))
+ return create_callback(process);
+ return createInvalidPlugInError(plugin_name);
+}
+
Expected<StringRef> Trace::FindPluginSchema(StringRef name) {
ConstString plugin_name(name);
StringRef schema = PluginManager::GetTraceSchema(plugin_name);
@@ -266,3 +279,13 @@
return index < end_position;
});
}
+
+Expected<std::string> Trace::DoQueryData(Process &process,
+ const json::Value &packet) {
+ return process.TraceQueryData(packet);
+}
+
+Error Trace::DoStartTracingThread(Thread &thread,
+ const llvm::json::Value &packet) {
+ return thread.GetProcess()->StartTracingThread(packet);
+}
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
@@ -19,16 +19,9 @@
class TraceIntelPTSessionFileParser : public TraceSessionFileParser {
public:
- struct JSONPTCPU {
- std::string vendor;
- int64_t family;
- int64_t model;
- int64_t stepping;
- };
-
struct JSONTraceIntelPTSettings
: TraceSessionFileParser::JSONTracePluginSettings {
- JSONPTCPU pt_cpu;
+ TraceIntelPTCPUConfig pt_cpu;
};
/// See \a TraceSessionFileParser::TraceSessionFileParser for the description
@@ -55,9 +48,9 @@
CreateTraceIntelPTInstance(const pt_cpu &pt_cpu,
std::vector<ParsedProcess> &parsed_processes);
-private:
- pt_cpu ParsePTCPU(const JSONPTCPU &pt_cpu);
+ static pt_cpu ParsePTCPU(const TraceIntelPTCPUConfig &pt_cpu);
+private:
const llvm::json::Value &m_trace_session_file;
};
@@ -67,12 +60,6 @@
namespace llvm {
namespace json {
-bool fromJSON(
- const Value &value,
- lldb_private::trace_intel_pt::TraceIntelPTSessionFileParser::JSONPTCPU
- &pt_cpu,
- Path path);
-
bool fromJSON(const Value &value,
lldb_private::trace_intel_pt::TraceIntelPTSessionFileParser::
JSONTraceIntelPTSettings &plugin_settings,
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
@@ -13,6 +13,7 @@
#include "lldb/Target/Target.h"
#include "lldb/Target/ThreadList.h"
#include "lldb/Target/ThreadTrace.h"
+#include "lldb/Utility/TraceOptions.h"
using namespace lldb;
using namespace lldb_private;
@@ -35,7 +36,8 @@
return schema;
}
-pt_cpu TraceIntelPTSessionFileParser::ParsePTCPU(const JSONPTCPU &pt_cpu) {
+pt_cpu
+TraceIntelPTSessionFileParser::ParsePTCPU(const TraceIntelPTCPUConfig &pt_cpu) {
return {pt_cpu.vendor.compare("intel") == 0 ? pcv_intel : pcv_unknown,
static_cast<uint16_t>(pt_cpu.family),
static_cast<uint8_t>(pt_cpu.model),
@@ -73,14 +75,6 @@
namespace llvm {
namespace json {
-bool fromJSON(const Value &value,
- TraceIntelPTSessionFileParser::JSONPTCPU &pt_cpu, Path path) {
- ObjectMapper o(value, path);
- return o && o.map("vendor", pt_cpu.vendor) &&
- o.map("family", pt_cpu.family) && o.map("model", pt_cpu.model) &&
- o.map("stepping", pt_cpu.stepping);
-}
-
bool fromJSON(
const Value &value,
TraceIntelPTSessionFileParser::JSONTraceIntelPTSettings &plugin_settings,
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
@@ -4,13 +4,14 @@
def thread_trace_start_intel_pt_size: Option<"size", "s">,
Group<1>,
Arg<"Value">,
- Desc<"The size of the trace in KB. The kernel rounds it down to the nearest"
- " multiple of 4. Defaults to 4.">;
- def thread_trace_start_intel_pt_custom_config: Option<"custom-config", "c">,
+ Desc<"The size of the trace in KB. It is rounded up to the nearest pwoer of"
+ " 2. Defaults to 4.">;
+ def thread_trace_start_intel_pt_custom_config: Option<"perf-config", "c">,
Group<1>,
Arg<"Value">,
- Desc<"Low level bitmask configuration for the kernel based on the values "
- "in `grep -H /sys/bus/event_source/devices/intel_pt/format/*`. "
+ Desc<"Custom low level perf event bitmask configuration for the kernel "
+ "based on the values in "
+ "`grep -H /sys/bus/event_source/devices/intel_pt/format/*`. "
"See https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/perf-intel-pt.txt"
" for more information. Defaults to 0.">;
}
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -45,8 +45,12 @@
/// \return
/// A trace instance or an error in case of failures.
static llvm::Expected<lldb::TraceSP>
- CreateInstance(const llvm::json::Value &trace_session_file,
- llvm::StringRef session_file_dir, Debugger &debugger);
+ CreateInstanceForSessionFile(const llvm::json::Value &trace_session_file,
+ llvm::StringRef session_file_dir,
+ Debugger &debugger);
+
+ static llvm::Expected<lldb::TraceSP>
+ CreateInstanceForLiveProcess(Process &process);
static ConstString GetPluginNameStatic();
@@ -64,6 +68,15 @@
size_t GetCursorPosition(const Thread &thread) override;
+ llvm::Error StopTracingThread(const Thread &thread) override;
+
+ llvm::Error StartTracingThread(Thread &thread,
+ const TraceIntelPTStartPacketParams ¶ms);
+
+ static llvm::Expected<TraceIntelPTBuffer> QueryTraceBuffer(Thread &thread);
+
+ static llvm::Expected<pt_cpu> QueryCPUConfig(Process &process);
+
private:
friend class TraceIntelPTSessionFileParser;
@@ -73,6 +86,8 @@
TraceIntelPT(const pt_cpu &pt_cpu,
const std::vector<std::shared_ptr<ThreadTrace>> &traced_threads);
+ TraceIntelPT(const pt_cpu &pt_cpu) : m_pt_cpu(pt_cpu), m_thread_decoders(){};
+
/// Decode the trace of the given thread that, i.e. recontruct the traced
/// instructions. That trace must be managed by this class.
///
@@ -86,8 +101,7 @@
const DecodedThread *Decode(const Thread &thread);
pt_cpu m_pt_cpu;
- std::map<std::pair<lldb::pid_t, lldb::tid_t>, ThreadTraceDecoder>
- m_trace_threads;
+ std::map<const Thread *, std::unique_ptr<ThreadDecoder>> m_thread_decoders;
};
} // namespace trace_intel_pt
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -28,12 +28,13 @@
void TraceIntelPT::Initialize() {
PluginManager::RegisterPlugin(
- GetPluginNameStatic(), "Intel Processor Trace", CreateInstance,
+ GetPluginNameStatic(), "Intel Processor Trace",
+ CreateInstanceForSessionFile, CreateInstanceForLiveProcess,
TraceIntelPTSessionFileParser::GetSchema(), GetStartCommand);
}
void TraceIntelPT::Terminate() {
- PluginManager::UnregisterPlugin(CreateInstance);
+ PluginManager::UnregisterPlugin(CreateInstanceForSessionFile);
}
ConstString TraceIntelPT::GetPluginNameStatic() {
@@ -55,31 +56,78 @@
void TraceIntelPT::Dump(Stream *s) const {}
-Expected<TraceSP>
-TraceIntelPT::CreateInstance(const json::Value &trace_session_file,
- StringRef session_file_dir, Debugger &debugger) {
+Expected<TraceSP> TraceIntelPT::CreateInstanceForSessionFile(
+ const json::Value &trace_session_file, StringRef session_file_dir,
+ Debugger &debugger) {
return TraceIntelPTSessionFileParser(debugger, trace_session_file,
session_file_dir)
.Parse();
}
+Error TraceIntelPT::StopTracingThread(const Thread &thread) {
+ auto it = m_thread_decoders.find(&thread);
+ if (it == m_thread_decoders.end())
+ return createStringError(inconvertibleErrorCode(),
+ "thread is not being traced");
+ if (Error err =
+ thread.GetProcess()->StopTracingThread(thread.GetID(), "intel-pt"))
+ return err;
+
+ m_thread_decoders.erase(it);
+
+ // We clear the trace instance for the target we are not tracing any threads.
+ if (m_thread_decoders.empty())
+ thread.GetProcess()->GetTarget().SetTrace(nullptr);
+ return Error::success();
+}
+
+Error TraceIntelPT::StartTracingThread(
+ Thread &thread, const TraceIntelPTStartPacketParams ¶ms) {
+ if (Error err = Trace::StartTracingThread(thread, params))
+ return err;
+ m_thread_decoders.emplace(
+ &thread, std::make_unique<LiveThreadDecoder>(thread, m_pt_cpu));
+ return Error::success();
+}
+
+Expected<TraceIntelPTBuffer> TraceIntelPT::QueryTraceBuffer(Thread &thread) {
+ TraceIntelPTQueryBufferParams params = {static_cast<int64_t>(thread.GetID())};
+ return QueryData<TraceIntelPTBuffer>(*thread.GetProcess(), "intel-pt",
+ "buffer", params);
+}
+
+Expected<pt_cpu> TraceIntelPT::QueryCPUConfig(Process &process) {
+ if (Expected<TraceIntelPTCPUConfig> cpu_config =
+ QueryData<TraceIntelPTCPUConfig>(process, "intel-pt", "pt_cpu"))
+ return TraceIntelPTSessionFileParser::ParsePTCPU(*cpu_config);
+ else
+ return cpu_config.takeError();
+}
+
+Expected<TraceSP> TraceIntelPT::CreateInstanceForLiveProcess(Process &process) {
+ Expected<pt_cpu> pt_cpu = QueryCPUConfig(process);
+ if (!pt_cpu)
+ return pt_cpu.takeError();
+
+ TraceSP instance(new TraceIntelPT(*pt_cpu));
+ process.GetTarget().SetTrace(instance);
+ return instance;
+}
+
TraceIntelPT::TraceIntelPT(
const pt_cpu &pt_cpu,
const std::vector<std::shared_ptr<ThreadTrace>> &traced_threads)
: m_pt_cpu(pt_cpu) {
for (const std::shared_ptr<ThreadTrace> &thread : traced_threads)
- m_trace_threads.emplace(
- std::piecewise_construct,
- std::forward_as_tuple(thread->GetProcess()->GetID(), thread->GetID()),
- std::forward_as_tuple(thread, pt_cpu));
+ m_thread_decoders.emplace(
+ thread.get(), std::make_unique<ThreadTraceDecoder>(thread, pt_cpu));
}
const DecodedThread *TraceIntelPT::Decode(const Thread &thread) {
- auto it = m_trace_threads.find(
- std::make_pair(thread.GetProcess()->GetID(), thread.GetID()));
- if (it == m_trace_threads.end())
+ auto it = m_thread_decoders.find(&thread);
+ if (it == m_thread_decoders.end())
return nullptr;
- return &it->second.Decode();
+ return &it->second->Decode();
}
size_t TraceIntelPT::GetCursorPosition(const Thread &thread) {
Index: lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
+++ lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
@@ -18,24 +18,33 @@
namespace lldb_private {
namespace trace_intel_pt {
-/// \a lldb_private::ThreadTrace decoder that stores the output from decoding,
-/// avoiding recomputations, as decoding is expensive.
-class ThreadTraceDecoder {
+/// Base class that handles the decoding of a thread and its trace buffer/ file.
+class ThreadDecoder {
+public:
+ virtual ~ThreadDecoder() = default;
+
+ /// Decode the thread and store the result internally, to avoid
+ /// recomputations.
+ ///
+ /// \return
+ /// A \a DecodedThread instance.
+ virtual const DecodedThread &Decode() = 0;
+};
+
+/// Decoder implementation for \a lldb_private::ThreadTrace, which are non-live
+/// processes that come trace session files.
+class ThreadTraceDecoder : public ThreadDecoder {
public:
/// \param[in] trace_thread
/// The thread whose trace file will be decoded.
///
/// \param[in] pt_cpu
- /// The libipt cpu used when recording the trace.
+ /// The libipt cpu used when decoding the trace.
ThreadTraceDecoder(const std::shared_ptr<ThreadTrace> &trace_thread,
const pt_cpu &pt_cpu)
: m_trace_thread(trace_thread), m_pt_cpu(pt_cpu), m_decoded_thread() {}
- /// Decode the thread and store the result internally.
- ///
- /// \return
- /// A \a DecodedThread instance.
- const DecodedThread &Decode();
+ const DecodedThread &Decode() override;
private:
ThreadTraceDecoder(const ThreadTraceDecoder &other) = delete;
@@ -46,6 +55,31 @@
llvm::Optional<DecodedThread> m_decoded_thread;
};
+class LiveThreadDecoder : public ThreadDecoder {
+public:
+ /// \param[in] thread
+ /// The thread whose traces will be decoded.
+ ///
+ /// \param[in] pt_cpu
+ /// The libipt cpu used when decoding the trace.
+ LiveThreadDecoder(Thread &thread, const pt_cpu &pt_cpu);
+
+ /// Decode the current trace for the thread.
+ ///
+ /// Internally the result is saved to avoid recomputations. If the debugger's
+ /// stop id has changed between calls, the cached result is discarded.
+ const DecodedThread &Decode() override;
+
+private:
+ LiveThreadDecoder(const LiveThreadDecoder &other) = delete;
+ LiveThreadDecoder &operator=(const LiveThreadDecoder &other) = delete;
+
+ lldb::ThreadSP m_thread_sp;
+ pt_cpu m_pt_cpu;
+ llvm::Optional<DecodedThread> m_decoded_thread;
+ uint32_t m_stop_id;
+};
+
} // namespace trace_intel_pt
} // namespace lldb_private
Index: lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
+++ lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
@@ -10,10 +10,12 @@
#include "llvm/Support/MemoryBuffer.h"
+#include "TraceIntelPT.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/Section.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/ThreadTrace.h"
+#include "lldb/Utility/StringExtractor.h"
using namespace lldb;
using namespace lldb_private;
@@ -166,14 +168,7 @@
static std::vector<IntelPTInstruction>
CreateDecoderAndDecode(Process &process, const pt_cpu &pt_cpu,
- const FileSpec &trace_file) {
- ErrorOr<std::unique_ptr<MemoryBuffer>> trace_or_error =
- MemoryBuffer::getFile(trace_file.GetPath());
- if (std::error_code err = trace_or_error.getError())
- return makeInstructionListFromError(errorCodeToError(err));
-
- MemoryBuffer &trace = **trace_or_error;
-
+ MutableArrayRef<uint8_t> buffer) {
pt_config config;
pt_config_init(&config);
config.cpu = pt_cpu;
@@ -181,12 +176,8 @@
if (int errcode = pt_cpu_errata(&config.errata, &config.cpu))
return makeInstructionListFromError(make_error<IntelPTError>(errcode));
- // The libipt library does not modify the trace buffer, hence the following
- // cast is safe.
- config.begin =
- reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferStart()));
- config.end =
- reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferEnd()));
+ config.begin = buffer.data();
+ config.end = buffer.data() + buffer.size();
pt_insn_decoder *decoder = pt_insn_alloc_decoder(&config);
if (!decoder)
@@ -204,6 +195,33 @@
return instructions;
}
+static std::vector<IntelPTInstruction>
+CreateDecoderAndDecode(Process &process, const pt_cpu &pt_cpu,
+ const FileSpec &trace_file) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> trace_or_error =
+ MemoryBuffer::getFile(trace_file.GetPath());
+ if (std::error_code err = trace_or_error.getError())
+ return makeInstructionListFromError(errorCodeToError(err));
+
+ MemoryBuffer &trace = **trace_or_error;
+ MutableArrayRef<uint8_t> trace_data(
+ // The libipt library does not modify the trace buffer, hence the
+ // following cast is safe.
+ reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferStart())),
+ trace.getBufferSize());
+ return CreateDecoderAndDecode(process, pt_cpu, trace_data);
+}
+
+static std::vector<IntelPTInstruction>
+CreateDecoderAndDecode(Thread &thread, const pt_cpu &pt_cpu) {
+ if (Expected<TraceIntelPTBuffer> buffer =
+ TraceIntelPT::QueryTraceBuffer(thread))
+ return CreateDecoderAndDecode(*thread.GetProcess(), pt_cpu,
+ MutableArrayRef<uint8_t>(buffer->data));
+ else
+ return makeInstructionListFromError(buffer.takeError());
+}
+
const DecodedThread &ThreadTraceDecoder::Decode() {
if (!m_decoded_thread.hasValue()) {
m_decoded_thread = DecodedThread(
@@ -213,3 +231,21 @@
return *m_decoded_thread;
}
+
+LiveThreadDecoder::LiveThreadDecoder(Thread &thread, const pt_cpu &pt_cpu)
+ : m_thread_sp(thread.shared_from_this()), m_pt_cpu(pt_cpu),
+ m_decoded_thread(), m_stop_id(thread.GetProcess()->GetStopID()) {}
+
+const DecodedThread &LiveThreadDecoder::Decode() {
+ uint32_t new_stop_id = m_thread_sp->GetProcess()->GetStopID();
+ if (new_stop_id != m_stop_id) {
+ m_stop_id = new_stop_id;
+ m_decoded_thread = None;
+ }
+
+ if (!m_decoded_thread.hasValue())
+ m_decoded_thread =
+ DecodedThread(CreateDecoderAndDecode(*m_thread_sp, m_pt_cpu));
+
+ return *m_decoded_thread;
+}
Index: lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h
+++ lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h
@@ -16,7 +16,7 @@
namespace lldb_private {
namespace trace_intel_pt {
-class CommandObjectTraceStartIntelPT : public CommandObjectIterateOverThreads {
+class CommandObjectTraceStartIntelPT : public CommandObjecThreadTraceStart {
public:
class CommandOptions : public Options {
public:
@@ -31,13 +31,13 @@
llvm::ArrayRef<OptionDefinition> GetDefinitions() override;
- size_t m_size_in_kb;
- uint32_t m_custom_config;
+ size_t m_buffer_size_in_kb;
+ uint32_t m_perf_config;
};
CommandObjectTraceStartIntelPT(CommandInterpreter &interpreter)
- : CommandObjectIterateOverThreads(
- interpreter, "thread trace start",
+ : CommandObjecThreadTraceStart(
+ "intel-pt", interpreter, "thread trace start",
"Start tracing one or more threads with intel-pt. "
"Defaults to the current thread. Thread indices can be "
"specified as arguments.\n Use the thread-index \"all\" to trace "
Index: lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp
+++ lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp
@@ -8,7 +8,9 @@
#include "CommandObjectTraceStartIntelPT.h"
+#include "TraceIntelPT.h"
#include "lldb/Host/OptionParser.h"
+#include "lldb/Target/Process.h"
#include "lldb/Target/Trace.h"
using namespace lldb;
@@ -33,17 +35,17 @@
error.SetErrorStringWithFormat("invalid integer value for option '%s'",
option_arg.str().c_str());
else
- m_size_in_kb = size_in_kb;
+ m_buffer_size_in_kb = size_in_kb;
break;
}
case 'c': {
- int32_t custom_config;
- if (option_arg.empty() || option_arg.getAsInteger(0, custom_config) ||
- custom_config < 0)
+ int32_t perf_config;
+ if (option_arg.empty() || option_arg.getAsInteger(0, perf_config) ||
+ perf_config < 0)
error.SetErrorStringWithFormat("invalid integer value for option '%s'",
option_arg.str().c_str());
else
- m_custom_config = custom_config;
+ m_perf_config = perf_config;
break;
}
default:
@@ -54,8 +56,8 @@
void CommandObjectTraceStartIntelPT::CommandOptions::OptionParsingStarting(
ExecutionContext *execution_context) {
- m_size_in_kb = 4;
- m_custom_config = 0;
+ m_buffer_size_in_kb = 4;
+ m_perf_config = 0;
}
llvm::ArrayRef<OptionDefinition>
@@ -65,9 +67,19 @@
bool CommandObjectTraceStartIntelPT::HandleOneThread(
lldb::tid_t tid, CommandReturnObject &result) {
- result.AppendMessageWithFormat(
- "would trace tid %" PRIu64 " with size_in_kb %zu and custom_config %d\n",
- tid, m_options.m_size_in_kb, m_options.m_custom_config);
- result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ TraceIntelPTStartPacketParams options;
+ options.buffer_size_in_kb = m_options.m_buffer_size_in_kb;
+ options.perf_config = m_options.m_perf_config;
+
+ Thread &thread =
+ *m_exe_ctx.GetProcessRef().GetThreadList().FindThreadByID(tid);
+ TraceIntelPT *trace =
+ (TraceIntelPT *)m_exe_ctx.GetTargetRef().GetTrace().get();
+
+ if (Error err = trace->StartTracingThread(thread, options))
+ result.SetError(toString(std::move(err)));
+ else
+ result.SetStatus(eReturnStatusSuccessFinishResult);
return result.Succeeded();
}
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -177,6 +177,14 @@
llvm::Expected<TraceTypeInfo> GetSupportedTraceType() override;
+ llvm::Error StopTracingThread(lldb::tid_t tid,
+ llvm::StringRef trace_type) override;
+
+ llvm::Error StartTracingThread(const llvm::json::Value &options) override;
+
+ llvm::Expected<std::string>
+ TraceQueryData(const llvm::json::Value &query) override;
+
Status GetTraceConfig(lldb::user_id_t uid, TraceOptions &options) override;
Status GetWatchpointSupportInfo(uint32_t &num, bool &after) override;
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -1228,6 +1228,21 @@
return m_gdb_comm.SendGetSupportedTraceType();
}
+llvm::Error ProcessGDBRemote::StopTracingThread(lldb::tid_t tid,
+ llvm::StringRef trace_type) {
+ return m_gdb_comm.SendTraceStop(tid, trace_type);
+}
+
+llvm::Error
+ProcessGDBRemote::StartTracingThread(const llvm::json::Value &options) {
+ return m_gdb_comm.SendTraceStart(options);
+}
+
+llvm::Expected<std::string>
+ProcessGDBRemote::TraceQueryData(const llvm::json::Value &query) {
+ return m_gdb_comm.SendTraceQueryData(query);
+}
+
void ProcessGDBRemote::DidExit() {
// When we exit, disconnect from the GDB server communications
m_gdb_comm.Disconnect();
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -166,6 +166,12 @@
PacketResult Handle_jLLDBTraceSupportedType(StringExtractorGDBRemote &packet);
+ PacketResult Handle_jLLDBTraceStart(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_jLLDBTraceStop(StringExtractorGDBRemote &packet);
+
+ PacketResult Handle_jLLDBTraceQueryData(StringExtractorGDBRemote &packet);
+
PacketResult Handle_QRestoreRegisterState(StringExtractorGDBRemote &packet);
PacketResult Handle_vAttach(StringExtractorGDBRemote &packet);
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -194,6 +194,15 @@
RegisterMemberFunctionHandler(
StringExtractorGDBRemote::eServerPacketType_jLLDBTraceSupportedType,
&GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceSupportedType);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_jLLDBTraceStart,
+ &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceStart);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_jLLDBTraceStop,
+ &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceStop);
+ RegisterMemberFunctionHandler(
+ StringExtractorGDBRemote::eServerPacketType_jLLDBTraceQueryData,
+ &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceQueryData);
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_g,
&GDBRemoteCommunicationServerLLGS::Handle_g);
@@ -1256,6 +1265,111 @@
return SendPacketNoLock(escaped_response.GetString());
}
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceStop(
+ StringExtractorGDBRemote &packet) {
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(Status("Process not running."));
+
+ packet.ConsumeFront("jLLDBTraceStop:");
+ Expected<TraceStopPacket> stop_packet =
+ json::parse<TraceStopPacket>(packet.Peek());
+ if (!stop_packet)
+ return SendErrorResponse(stop_packet.takeError());
+
+ if (stop_packet->type != "intel-pt")
+ return SendErrorResponse(
+ Status("Unsupported tracing type '%s'", stop_packet->type.c_str()));
+
+ if (Error err = m_debugged_process_up->StopIntelPTTrace(stop_packet->tid))
+ return SendErrorResponse(std::move(err));
+
+ return SendOKResponse();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceStart(
+ StringExtractorGDBRemote &packet) {
+
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(Status("Process not running."));
+
+ packet.ConsumeFront("jLLDBTraceStart:");
+ Expected<TraceStartSimplePacket> start_packet =
+ json::parse<TraceStartSimplePacket>(packet.Peek());
+ if (!start_packet)
+ return SendErrorResponse(start_packet.takeError());
+
+ if (start_packet->type != "intel-pt")
+ return SendErrorResponse(
+ Status("Unsupported tracing type '%s'", start_packet->type.c_str()));
+
+ auto intel_pt_start_packet =
+ json::parse<TraceIntelPTStartPacket>(packet.Peek());
+ if (!intel_pt_start_packet)
+ return SendErrorResponse(intel_pt_start_packet.takeError());
+
+ if (Error err = m_debugged_process_up->StartIntelPTTrace(
+ intel_pt_start_packet->tid,
+ static_cast<size_t>(intel_pt_start_packet->params.buffer_size_in_kb),
+ static_cast<uint32_t>(intel_pt_start_packet->params.perf_config)))
+ return SendErrorResponse(std::move(err));
+
+ return SendOKResponse();
+}
+
+template <typename T> static Expected<json::Value> toJSON(Expected<T> obj) {
+ if (obj)
+ return json::toJSON(*obj);
+ else
+ return obj.takeError();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceQueryData(
+ StringExtractorGDBRemote &packet) {
+
+ // Fail if we don't have a current process.
+ if (!m_debugged_process_up ||
+ (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
+ return SendErrorResponse(Status("Process not running."));
+
+ packet.ConsumeFront("jLLDBTraceQueryData:");
+ llvm::Expected<TraceQueryDataSimplePacket> query_data_packet =
+ llvm::json::parse<TraceQueryDataSimplePacket>(packet.Peek());
+ if (!query_data_packet)
+ return SendErrorResponse(Status(query_data_packet.takeError()));
+
+ auto handle_intelpt_buffer_query = [&] {
+ if (auto buffer_packet =
+ json::parse<TraceIntelPTQueryBufferPacket>(packet.Peek()))
+ return SendJSONResponse(toJSON(
+ m_debugged_process_up->GetIntelPTBuffer(buffer_packet->params.tid)));
+ else
+ return SendErrorResponse(buffer_packet.takeError());
+ };
+
+ auto handle_intelpt_cpu_config_query = [&] {
+ return SendJSONResponse(
+ toJSON(m_debugged_process_up->GetIntelPTCPUConfig()));
+ };
+
+ if (query_data_packet->type == "intel-pt") {
+ if (query_data_packet->query == "buffer")
+ return handle_intelpt_buffer_query();
+ else if (query_data_packet->query == "pt_cpu")
+ return handle_intelpt_cpu_config_query();
+ }
+
+ return SendErrorResponse(Status(
+ "Unsupported query '%s' for tracing type '%s'",
+ query_data_packet->query.c_str(), query_data_packet->type.c_str()));
+}
+
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerLLGS::Handle_jTraceConfigRead(
StringExtractorGDBRemote &packet) {
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h
@@ -73,6 +73,13 @@
PacketResult SendOKResponse();
+ /// Serialize and send a JSON object response.
+ PacketResult SendJSONResponse(const llvm::json::Value &value);
+
+ /// Serialize and send a JSON object response, or respond with an error if the
+ /// input object is an \a llvm::Error.
+ PacketResult SendJSONResponse(llvm::Expected<llvm::json::Value> value);
+
private:
GDBRemoteCommunicationServer(const GDBRemoteCommunicationServer &) = delete;
const GDBRemoteCommunicationServer &
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
@@ -16,11 +16,13 @@
#include "lldb/Utility/StreamString.h"
#include "lldb/Utility/StringExtractorGDBRemote.h"
#include "lldb/Utility/UnimplementedError.h"
+#include "llvm/Support/JSON.h"
#include <cstring>
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::process_gdb_remote;
+using namespace llvm;
GDBRemoteCommunicationServer::GDBRemoteCommunicationServer(
const char *comm_name, const char *listener_name)
@@ -151,3 +153,21 @@
bool GDBRemoteCommunicationServer::HandshakeWithClient() {
return GetAck() == PacketResult::Success;
}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServer::SendJSONResponse(const json::Value &value) {
+ std::string json_string;
+ raw_string_ostream os(json_string);
+ os << value;
+ os.flush();
+ StreamGDBRemote escaped_response;
+ escaped_response.PutEscapedBytes(json_string.c_str(), json_string.size());
+ return SendPacketNoLock(escaped_response.GetString());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServer::SendJSONResponse(Expected<json::Value> value) {
+ if (!value)
+ return SendErrorResponse(value.takeError());
+ return SendJSONResponse(*value);
+}
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -522,6 +522,13 @@
llvm::Expected<TraceTypeInfo> SendGetSupportedTraceType();
+ llvm::Error SendTraceStart(const llvm::json::Value &options);
+
+ llvm::Error SendTraceStop(lldb::tid_t tid, llvm::StringRef trace_type);
+
+ llvm::Expected<std::string>
+ SendTraceQueryData(const llvm::json::Value &query);
+
protected:
LazyBool m_supports_not_sending_acks;
LazyBool m_supports_thread_suffix;
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -3480,6 +3480,94 @@
"failed to send packet: jLLDBTraceSupportedType");
}
+llvm::Error
+GDBRemoteCommunicationClient::SendTraceStop(lldb::tid_t tid,
+ llvm::StringRef trace_type) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+ StreamGDBRemote escaped_packet;
+ escaped_packet.PutCString("jLLDBTraceStop:");
+
+ TraceStopPacket packet(trace_type, tid);
+
+ std::string json_string;
+ llvm::raw_string_ostream os(json_string);
+ os << llvm::json::toJSON(packet);
+ os.flush();
+
+ escaped_packet.PutEscapedBytes(json_string.c_str(), json_string.size());
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response,
+ true) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ if (response.IsOKResponse())
+ return llvm::Error::success();
+ return response.GetStatus().ToError();
+ }
+ LLDB_LOG(log, "failed to send packet: jLLDBTraceStop");
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "failed to send packet: jLLDBTraceStop '%s'",
+ escaped_packet.GetData());
+}
+
+llvm::Error
+GDBRemoteCommunicationClient::SendTraceStart(const llvm::json::Value &options) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+ StreamGDBRemote escaped_packet;
+ escaped_packet.PutCString("jLLDBTraceStart:");
+
+ std::string json_string;
+ llvm::raw_string_ostream os(json_string);
+ os << options;
+ os.flush();
+
+ escaped_packet.PutEscapedBytes(json_string.c_str(), json_string.size());
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response,
+ true) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ if (response.IsOKResponse())
+ return llvm::Error::success();
+ return response.GetStatus().ToError();
+ }
+ LLDB_LOG(log, "failed to send packet: jLLDBTraceStart");
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "failed to send packet: jLLDBTraceStart '%s'",
+ escaped_packet.GetData());
+}
+
+llvm::Expected<std::string> GDBRemoteCommunicationClient::SendTraceQueryData(
+ const llvm::json::Value &query) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+ StreamGDBRemote escaped_packet;
+ escaped_packet.PutCString("jLLDBTraceQueryData:");
+
+ std::string json_string;
+ llvm::raw_string_ostream os(json_string);
+ os << query;
+ os.flush();
+
+ escaped_packet.PutEscapedBytes(json_string.c_str(), json_string.size());
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response,
+ true) ==
+ GDBRemoteCommunication::PacketResult::Success) {
+ if (!response.IsNormalResponse())
+ return response.GetStatus().ToError();
+ return std::string(response.Peek());
+ }
+ LLDB_LOG(log, "failed to send packet: jLLDBTraceQueryData");
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "failed to send packet: jLLDBTraceQueryData '%s'",
+ escaped_packet.GetData());
+}
+
Status
GDBRemoteCommunicationClient::SendGetTraceConfigPacket(lldb::user_id_t uid,
TraceOptions &options) {
Index: lldb/source/Plugins/Process/Linux/ProcessorTrace.h
===================================================================
--- lldb/source/Plugins/Process/Linux/ProcessorTrace.h
+++ lldb/source/Plugins/Process/Linux/ProcessorTrace.h
@@ -79,8 +79,25 @@
void SetTraceID(lldb::user_id_t traceid) { m_traceid = traceid; }
- Status StartTrace(lldb::pid_t pid, lldb::tid_t tid,
- const TraceOptions &config);
+ /// Start tracing a thread
+ ///
+ /// \param[in] pid
+ /// The pid of the process whose thread will be traced.
+ ///
+ /// \param[in] tid
+ /// The tid of the thread to trace.
+ ///
+ /// \param[in] buffer_size_in_kb
+ /// See \a NativeProcessProtocol::StartIntelPTTrace.
+ ///
+ /// \param[in] perf_config
+ /// See \a NativeProcessProtocol::StartIntelPTTrace.
+ ///
+ /// \return
+ /// \a llvm::Error::success if tracing was successful, or an
+ /// \a llvm::Error otherwise.
+ llvm::Error StartTrace(lldb::pid_t pid, lldb::tid_t tid,
+ size_t buffer_size_in_kb, uint32_t perf_config = 0);
llvm::MutableArrayRef<uint8_t> GetAuxBuffer();
llvm::MutableArrayRef<uint8_t> GetDataBuffer();
@@ -99,10 +116,27 @@
static Status GetCPUType(TraceOptions &config);
+ /// See \a NativeProcessProtocol::GetIntelPTCPUConfig.
+ static llvm::Expected<TraceIntelPTCPUConfig> GetIntelPTCPUConfig();
+
static llvm::Expected<ProcessorTraceMonitorUP>
Create(lldb::pid_t pid, lldb::tid_t tid, const TraceOptions &config,
bool useProcessSettings);
+ /// Start tracing a thread.
+ ///
+ /// See \a StartTrace.
+ ///
+ /// \return
+ /// A \a ProcessorTraceMonitorUP instance if tracing was successful, or
+ /// an \a llvm::Error otherwise.
+ static llvm::Expected<ProcessorTraceMonitorUP>
+ Create(lldb::pid_t pid, lldb::tid_t tid, size_t buffer_size_in_kb,
+ uint32_t perf_config);
+
+ /// See \a NativeProcessProtocol::GetIntelPTBuffer.
+ llvm::Expected<TraceIntelPTBuffer> GetIntelPTBuffer();
+
Status ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer,
size_t offset = 0);
Index: lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp
===================================================================
--- lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp
+++ lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp
@@ -16,6 +16,7 @@
#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
#include "ProcessorTrace.h"
#include "lldb/Host/linux/Support.h"
+#include "lldb/Utility/StreamString.h"
#include <sys/ioctl.h>
#include <sys/syscall.h>
@@ -67,27 +68,22 @@
bool ProcessorTraceMonitor::IsSupported() { return (bool)GetOSEventType(); }
-Status ProcessorTraceMonitor::StartTrace(lldb::pid_t pid, lldb::tid_t tid,
- const TraceOptions &config) {
+Error ProcessorTraceMonitor::StartTrace(lldb::pid_t pid, lldb::tid_t tid,
+ size_t buffer_size_in_kb,
+ uint32_t perf_config) {
#ifndef PERF_ATTR_SIZE_VER5
llvm_unreachable("perf event not supported");
#else
- Status error;
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
LLDB_LOG(log, "called thread id {0}", tid);
uint64_t page_size = getpagesize();
- uint64_t bufsize = config.getTraceBufferSize();
- uint64_t metabufsize = config.getMetaDataBufferSize();
+ uint64_t buffer_size = buffer_size_in_kb * 1024;
uint64_t numpages = static_cast<uint64_t>(
- llvm::PowerOf2Floor((bufsize + page_size - 1) / page_size));
+ llvm::PowerOf2Floor((buffer_size + page_size - 1) / page_size));
numpages = std::max<uint64_t>(1, numpages);
- bufsize = page_size * numpages;
-
- numpages = static_cast<uint64_t>(
- llvm::PowerOf2Floor((metabufsize + page_size - 1) / page_size));
- metabufsize = page_size * numpages;
+ buffer_size = page_size * numpages;
perf_event_attr attr;
memset(&attr, 0, sizeof(attr));
@@ -98,66 +94,58 @@
attr.exclude_hv = 1;
attr.exclude_idle = 1;
attr.mmap = 1;
+ attr.config = perf_config;
Expected<uint32_t> intel_pt_type = GetOSEventType();
- if (!intel_pt_type) {
- error = intel_pt_type.takeError();
- return error;
- }
+ if (!intel_pt_type)
+ return intel_pt_type.takeError();
LLDB_LOG(log, "intel pt type {0}", *intel_pt_type);
attr.type = *intel_pt_type;
- LLDB_LOG(log, "meta buffer size {0}", metabufsize);
- LLDB_LOG(log, "buffer size {0} ", bufsize);
-
- if (error.Fail()) {
- LLDB_LOG(log, "Status in custom config");
-
- return error;
- }
+ LLDB_LOG(log, "buffer size {0} ", buffer_size);
errno = 0;
auto fd =
syscall(SYS_perf_event_open, &attr, static_cast<::tid_t>(tid), -1, -1, 0);
if (fd == -1) {
LLDB_LOG(log, "syscall error {0}", errno);
- error.SetErrorString("perf event syscall Failed");
- return error;
+ return createStringError(inconvertibleErrorCode(),
+ "perf event syscall failed");
}
m_fd = std::unique_ptr<int, file_close>(new int(fd), file_close());
errno = 0;
auto base =
- mmap(nullptr, (metabufsize + page_size), PROT_WRITE, MAP_SHARED, fd, 0);
+ mmap(nullptr, (buffer_size + page_size), PROT_WRITE, MAP_SHARED, fd, 0);
if (base == MAP_FAILED) {
LLDB_LOG(log, "mmap base error {0}", errno);
- error.SetErrorString("Meta buffer allocation failed");
- return error;
+ return createStringError(inconvertibleErrorCode(),
+ "Meta buffer allocation failed");
}
m_mmap_meta = std::unique_ptr<perf_event_mmap_page, munmap_delete>(
reinterpret_cast<perf_event_mmap_page *>(base),
- munmap_delete(metabufsize + page_size));
+ munmap_delete(buffer_size + page_size));
m_mmap_meta->aux_offset = m_mmap_meta->data_offset + m_mmap_meta->data_size;
- m_mmap_meta->aux_size = bufsize;
+ m_mmap_meta->aux_size = buffer_size;
errno = 0;
- auto mmap_aux = mmap(nullptr, bufsize, PROT_READ, MAP_SHARED, fd,
+ auto mmap_aux = mmap(nullptr, buffer_size, PROT_READ, MAP_SHARED, fd,
static_cast<long int>(m_mmap_meta->aux_offset));
if (mmap_aux == MAP_FAILED) {
LLDB_LOG(log, "second mmap done {0}", errno);
- error.SetErrorString("Trace buffer allocation failed");
- return error;
+ return createStringError(inconvertibleErrorCode(),
+ "Trace buffer allocation failed");
}
m_mmap_aux = std::unique_ptr<uint8_t, munmap_delete>(
- reinterpret_cast<uint8_t *>(mmap_aux), munmap_delete(bufsize));
- return error;
+ reinterpret_cast<uint8_t *>(mmap_aux), munmap_delete(buffer_size));
+ return Error::success();
#endif
}
@@ -180,19 +168,17 @@
#endif
}
-Status ProcessorTraceMonitor::GetCPUType(TraceOptions &config) {
-
- Status error;
- uint64_t cpu_family = -1;
- uint64_t model = -1;
- uint64_t stepping = -1;
+Expected<TraceIntelPTCPUConfig> ProcessorTraceMonitor::GetIntelPTCPUConfig() {
+ int64_t cpu_family = -1;
+ int64_t model = -1;
+ int64_t stepping = -1;
std::string vendor_id;
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
auto BufferOrError = getProcFile("cpuinfo");
if (!BufferOrError)
- return BufferOrError.getError();
+ return Status(BufferOrError.getError()).ToError();
LLDB_LOG(log, "GetCPUType Function");
@@ -226,29 +212,51 @@
}
LLDB_LOG(log, "{0}:{1}:{2}:{3}", cpu_family, model, stepping, vendor_id);
- if ((cpu_family != static_cast<uint64_t>(-1)) &&
- (model != static_cast<uint64_t>(-1)) &&
- (stepping != static_cast<uint64_t>(-1)) && (!vendor_id.empty())) {
- auto params_dict = std::make_shared<StructuredData::Dictionary>();
- params_dict->AddIntegerItem("cpu_family", cpu_family);
- params_dict->AddIntegerItem("cpu_model", model);
- params_dict->AddIntegerItem("cpu_stepping", stepping);
- params_dict->AddStringItem("cpu_vendor", vendor_id);
+ if ((cpu_family != -1) && (model != -1) && (stepping != -1) &&
+ (!vendor_id.empty())) {
+ return TraceIntelPTCPUConfig{cpu_family, model, stepping,
+ vendor_id == "GenuineIntel" ? "intel"
+ : "unknown"};
+ }
+ }
+ return createStringError(inconvertibleErrorCode(), "cpu info not found");
+}
- llvm::StringRef intel_custom_params_key("intel-pt");
+Status ProcessorTraceMonitor::GetCPUType(TraceOptions &config) {
+ Expected<TraceIntelPTCPUConfig> cpu_info_or_err = GetIntelPTCPUConfig();
+ if (!cpu_info_or_err)
+ return Status(cpu_info_or_err.takeError());
+ TraceIntelPTCPUConfig &cpu_info = *cpu_info_or_err;
+
+ auto params_dict = std::make_shared<StructuredData::Dictionary>();
+ params_dict->AddIntegerItem("cpu_family", cpu_info.family);
+ params_dict->AddIntegerItem("cpu_model", cpu_info.model);
+ params_dict->AddIntegerItem("cpu_stepping", cpu_info.stepping);
+ params_dict->AddStringItem("cpu_vendor", cpu_info.vendor);
+
+ llvm::StringRef intel_custom_params_key("intel-pt");
+
+ auto intel_custom_params = std::make_shared<StructuredData::Dictionary>();
+ intel_custom_params->AddItem(
+ intel_custom_params_key,
+ StructuredData::ObjectSP(std::move(params_dict)));
+
+ config.setTraceParams(intel_custom_params);
+ return Status();
+}
- auto intel_custom_params = std::make_shared<StructuredData::Dictionary>();
- intel_custom_params->AddItem(
- intel_custom_params_key,
- StructuredData::ObjectSP(std::move(params_dict)));
+llvm::Expected<ProcessorTraceMonitorUP>
+ProcessorTraceMonitor::Create(lldb::pid_t pid, lldb::tid_t tid,
+ size_t buffer_size_in_kb, uint32_t config) {
+ ProcessorTraceMonitorUP pt_monitor_up(new ProcessorTraceMonitor);
- config.setTraceParams(intel_custom_params);
- return error; // we are done
- }
- }
+ if (llvm::Error err =
+ pt_monitor_up->StartTrace(pid, tid, buffer_size_in_kb, config))
+ return std::move(err);
- error.SetErrorString("cpu info not found");
- return error;
+ pt_monitor_up->SetThreadID(tid);
+ pt_monitor_up->SetTraceID(m_trace_num++);
+ return std::move(pt_monitor_up);
}
llvm::Expected<ProcessorTraceMonitorUP>
@@ -266,7 +274,7 @@
ProcessorTraceMonitorUP pt_monitor_up(new ProcessorTraceMonitor);
- error = pt_monitor_up->StartTrace(pid, tid, config);
+ error = pt_monitor_up->StartTrace(pid, tid, config.getTraceBufferSize());
if (error.Fail())
return error.ToError();
@@ -281,6 +289,15 @@
return std::move(pt_monitor_up);
}
+Expected<TraceIntelPTBuffer> ProcessorTraceMonitor::GetIntelPTBuffer() {
+ TraceIntelPTBuffer buffer(m_mmap_meta->aux_size);
+ MutableArrayRef<uint8_t> buffer_ref(buffer.data);
+ Status error = ReadPerfTraceAux(buffer_ref, 0);
+ if (error.Fail())
+ return error.ToError();
+ return std::move(buffer);
+}
+
Status
ProcessorTraceMonitor::ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer,
size_t offset) {
Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
+++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
@@ -101,6 +101,15 @@
return getProcFile(GetID(), "auxv");
}
+ llvm::Error StopIntelPTTrace(lldb::tid_t tid) override;
+
+ llvm::Error StartIntelPTTrace(lldb::tid_t tid, size_t buffer_size_in_kb,
+ uint32_t perf_config) override;
+
+ llvm::Expected<TraceIntelPTBuffer> GetIntelPTBuffer(lldb::tid_t tid) override;
+
+ llvm::Expected<TraceIntelPTCPUConfig> GetIntelPTCPUConfig() override;
+
lldb::user_id_t StartTrace(const TraceOptions &config,
Status &error) override;
Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -2033,6 +2033,51 @@
return m_pt_proces_trace_id;
}
+Error NativeProcessLinux::StopIntelPTTrace(lldb::tid_t tid) {
+ const auto &trace_instance = m_processor_trace_monitor.find(tid);
+ if (trace_instance == m_processor_trace_monitor.end())
+ return createStringError(inconvertibleErrorCode(), "thread not traced");
+
+ return StopProcessorTracingOnThread(trace_instance->second->GetTraceID(), tid)
+ .ToError();
+}
+
+Error NativeProcessLinux::StartIntelPTTrace(lldb::tid_t tid,
+ size_t buffer_size_in_kb,
+ uint32_t perf_config) {
+ if (!GetThreadByID(tid))
+ return createStringError(inconvertibleErrorCode(), "invalid thread id");
+
+ const auto &trace_instance = m_processor_trace_monitor.find(tid);
+ if (trace_instance != m_processor_trace_monitor.end())
+ return createStringError(inconvertibleErrorCode(),
+ "tracing already active on this thread");
+
+ Expected<ProcessorTraceMonitorUP> trace_monitor =
+ ProcessorTraceMonitor::Create(GetID(), tid, buffer_size_in_kb,
+ perf_config);
+ if (!trace_monitor)
+ return trace_monitor.takeError();
+
+ m_processor_trace_monitor.try_emplace(tid, std::move(*trace_monitor));
+ return Error::success();
+}
+
+Expected<TraceIntelPTBuffer>
+NativeProcessLinux::GetIntelPTBuffer(lldb::tid_t tid) {
+ const auto &trace_instance = m_processor_trace_monitor.find(tid);
+ if (trace_instance == m_processor_trace_monitor.end())
+ return createStringError(inconvertibleErrorCode(),
+ "thread not currently traced");
+
+ return trace_instance->second->GetIntelPTBuffer();
+}
+
+llvm::Expected<TraceIntelPTCPUConfig>
+NativeProcessLinux::GetIntelPTCPUConfig() {
+ return ProcessorTraceMonitor::GetIntelPTCPUConfig();
+}
+
lldb::user_id_t NativeProcessLinux::StartTrace(const TraceOptions &config,
Status &error) {
if (config.getType() != TraceType::eTraceTypeProcessorTrace)
Index: lldb/source/Core/PluginManager.cpp
===================================================================
--- lldb/source/Core/PluginManager.cpp
+++ lldb/source/Core/PluginManager.cpp
@@ -1008,16 +1008,21 @@
#pragma mark Trace
-struct TraceInstance : public PluginInstance<TraceCreateInstance> {
- TraceInstance(ConstString name, std::string description,
- CallbackType create_callback, llvm::StringRef schema,
- TraceGetStartCommand get_start_command)
- : PluginInstance<TraceCreateInstance>(name, std::move(description),
- create_callback),
- schema(schema), get_start_command(get_start_command) {}
+struct TraceInstance
+ : public PluginInstance<TraceCreateInstanceForSessionFile> {
+ TraceInstance(
+ ConstString name, std::string description,
+ CallbackType create_callback_for_session_file,
+ TraceCreateInstanceForLiveProcess create_callback_for_live_process,
+ llvm::StringRef schema, TraceGetStartCommand get_start_command)
+ : PluginInstance<TraceCreateInstanceForSessionFile>(
+ name, std::move(description), create_callback_for_session_file),
+ schema(schema), get_start_command(get_start_command),
+ create_callback_for_live_process(create_callback_for_live_process) {}
llvm::StringRef schema;
TraceGetStartCommand get_start_command;
+ TraceCreateInstanceForLiveProcess create_callback_for_live_process;
};
typedef PluginInstances<TraceInstance> TraceInstances;
@@ -1027,23 +1032,35 @@
return g_instances;
}
-bool PluginManager::RegisterPlugin(ConstString name, const char *description,
- TraceCreateInstance create_callback,
- llvm::StringRef schema,
- TraceGetStartCommand get_start_command) {
+bool PluginManager::RegisterPlugin(
+ ConstString name, const char *description,
+ TraceCreateInstanceForSessionFile create_callback_for_session_file,
+ TraceCreateInstanceForLiveProcess create_callback_for_live_process,
+ llvm::StringRef schema, TraceGetStartCommand get_start_command) {
return GetTracePluginInstances().RegisterPlugin(
- name, description, create_callback, schema, get_start_command);
+ name, description, create_callback_for_session_file,
+ create_callback_for_live_process, schema, get_start_command);
}
-bool PluginManager::UnregisterPlugin(TraceCreateInstance create_callback) {
- return GetTracePluginInstances().UnregisterPlugin(create_callback);
+bool PluginManager::UnregisterPlugin(
+ TraceCreateInstanceForSessionFile create_callback_for_session_file) {
+ return GetTracePluginInstances().UnregisterPlugin(
+ create_callback_for_session_file);
}
-TraceCreateInstance
+TraceCreateInstanceForSessionFile
PluginManager::GetTraceCreateCallback(ConstString plugin_name) {
return GetTracePluginInstances().GetCallbackForName(plugin_name);
}
+TraceCreateInstanceForLiveProcess
+PluginManager::GetTraceCreateCallbackForLiveProcess(ConstString plugin_name) {
+ for (const TraceInstance &instance : GetTracePluginInstances().GetInstances())
+ if (instance.name == plugin_name)
+ return instance.create_callback_for_live_process;
+ return nullptr;
+}
+
llvm::StringRef PluginManager::GetTraceSchema(ConstString plugin_name) {
for (const TraceInstance &instance : GetTracePluginInstances().GetInstances())
if (instance.name == plugin_name)
Index: lldb/source/Commands/CommandObjectThreadUtil.h
===================================================================
--- lldb/source/Commands/CommandObjectThreadUtil.h
+++ lldb/source/Commands/CommandObjectThreadUtil.h
@@ -76,6 +76,23 @@
bool m_add_return = true;
};
+/// Base class that should be extended by Trace plug-ins to implement
+/// thread-level tracing.
+class CommandObjecThreadTraceStart : public CommandObjectIterateOverThreads {
+public:
+ CommandObjecThreadTraceStart(llvm::StringRef trace_plugin_name,
+ CommandInterpreter &interpreter,
+ const char *name, const char *help,
+ const char *syntax, uint32_t flags)
+ : CommandObjectIterateOverThreads(interpreter, name, help, syntax, flags),
+ m_trace_plugin_name(trace_plugin_name.str()) {}
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override;
+
+private:
+ std::string m_trace_plugin_name;
+};
+
} // namespace lldb_private
#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTTHREADUTIL_H
Index: lldb/source/Commands/CommandObjectThreadUtil.cpp
===================================================================
--- lldb/source/Commands/CommandObjectThreadUtil.cpp
+++ lldb/source/Commands/CommandObjectThreadUtil.cpp
@@ -156,3 +156,20 @@
}
return true;
}
+
+bool CommandObjecThreadTraceStart::DoExecute(Args &command,
+ CommandReturnObject &result) {
+ ProcessSP process_sp = m_exe_ctx.GetProcessSP();
+ /// We create a new Trace instance so that plug-ins can assume it exists.
+ if (!process_sp->GetTarget().GetTrace()) {
+ Expected<TraceSP> trace =
+ Trace::FindPlugin(m_trace_plugin_name, *process_sp);
+ if (!trace) {
+ result.SetStatus(eReturnStatusFailed);
+ result.AppendErrorWithFormat("couldn't start tracing the process. %s",
+ toString(trace.takeError()).c_str());
+ return false;
+ }
+ }
+ return CommandObjectIterateOverThreads::DoExecute(command, result);
+}
Index: lldb/include/lldb/lldb-private-interfaces.h
===================================================================
--- lldb/include/lldb/lldb-private-interfaces.h
+++ lldb/include/lldb/lldb-private-interfaces.h
@@ -111,9 +111,11 @@
const char *repl_options);
typedef int (*ComparisonFunction)(const void *, const void *);
typedef void (*DebuggerInitializeCallback)(Debugger &debugger);
-typedef llvm::Expected<lldb::TraceSP> (*TraceCreateInstance)(
+typedef llvm::Expected<lldb::TraceSP> (*TraceCreateInstanceForSessionFile)(
const llvm::json::Value &trace_session_file,
llvm::StringRef session_file_dir, lldb_private::Debugger &debugger);
+typedef llvm::Expected<lldb::TraceSP> (*TraceCreateInstanceForLiveProcess)(
+ Process &process);
typedef lldb::CommandObjectSP (*TraceGetStartCommand)(
CommandInterpreter &interpreter);
Index: lldb/include/lldb/Utility/TraceOptions.h
===================================================================
--- lldb/include/lldb/Utility/TraceOptions.h
+++ lldb/include/lldb/Utility/TraceOptions.h
@@ -68,11 +68,175 @@
/// the lldb-server.
StructuredData::DictionarySP m_trace_params;
};
-}
+
+/// jLLDBTraceStop gdb-remote packet structures
+/// \{
+struct TraceStopPacket {
+ TraceStopPacket() {}
+
+ TraceStopPacket(llvm::StringRef type, int64_t tid) : type(type), tid(tid) {}
+
+ std::string type;
+ int64_t tid;
+};
+///}
+
+/// jLLDBTraceStart gdb-remote packet structures
+/// \{
+struct TraceStartSimplePacket {
+ TraceStartSimplePacket() {}
+
+ TraceStartSimplePacket(llvm::StringRef type, int64_t tid)
+ : type(type), tid(tid) {}
+
+ std::string type;
+ int64_t tid;
+};
+
+template <typename TParams> struct TraceStartPacket : TraceStartSimplePacket {
+ TraceStartPacket() {}
+
+ TraceStartPacket(llvm::StringRef type, int64_t tid, const TParams ¶ms)
+ : TraceStartSimplePacket(type, tid), params(params) {}
+
+ TParams params;
+};
+
+struct TraceIntelPTStartPacketParams {
+ int64_t buffer_size_in_kb;
+ int64_t perf_config;
+};
+
+using TraceIntelPTStartPacket = TraceStartPacket<TraceIntelPTStartPacketParams>;
+/// \}
+
+/// jTraceQueryData gdb-remote packet structures
+/// \{
+struct TraceQueryDataSimplePacket {
+ TraceQueryDataSimplePacket() {}
+
+ TraceQueryDataSimplePacket(llvm::StringRef type, llvm::StringRef query)
+ : type(type), query(query) {}
+ std::string type;
+ std::string query;
+};
+
+template <typename TParams>
+struct TraceQueryDataPacket : TraceQueryDataSimplePacket {
+ TraceQueryDataPacket() {}
+
+ TraceQueryDataPacket(llvm::StringRef type, llvm::StringRef query,
+ const TParams ¶ms)
+ : TraceQueryDataSimplePacket(type, query), params(params) {}
+
+ TParams params;
+};
+
+struct TraceIntelPTQueryBufferParams {
+ int64_t tid;
+};
+
+struct TraceIntelPTBuffer {
+ TraceIntelPTBuffer() : data() {}
+
+ TraceIntelPTBuffer(size_t size) : data(size) {}
+
+ std::vector<uint8_t> data;
+};
+
+struct TraceIntelPTCPUConfig {
+ int64_t family;
+ int64_t model;
+ int64_t stepping;
+ std::string vendor;
+};
+
+using TraceIntelPTQueryBufferPacket =
+ TraceQueryDataPacket<TraceIntelPTQueryBufferParams>;
+/// \}
+
+} // namespace lldb_private
namespace llvm {
namespace json {
+/// jLLDBTraceStop
+/// \{
+bool fromJSON(const Value &value, lldb_private::TraceStopPacket &packet,
+ Path path);
+
+Value toJSON(const lldb_private::TraceStopPacket &packet);
+///}
+
+/// jLLDBTraceStart
+/// \{
+bool fromJSON(const Value &value, lldb_private::TraceStartSimplePacket &packet,
+ Path path);
+
+bool fromJSON(const Value &value,
+ lldb_private::TraceIntelPTStartPacketParams &packet, Path path);
+
+Value toJSON(const lldb_private::TraceIntelPTStartPacketParams &packet);
+
+template <typename TParams>
+bool fromJSON(const Value &value,
+ lldb_private::TraceStartPacket<TParams> &packet, Path path) {
+ ObjectMapper o(value, path);
+ return o &&
+ fromJSON(value, (lldb_private::TraceStartSimplePacket &)packet,
+ path) &&
+ o.map("params", packet.params);
+}
+
+template <typename TParams>
+Value toJSON(const lldb_private::TraceStartPacket<TParams> &packet) {
+ return Object{{"tid", packet.tid},
+ {"type", packet.type},
+ {"params", toJSON(packet.params)}};
+}
+/// \}
+
+/// jLLDBTraceQueryData
+/// \{
+bool fromJSON(const Value &value,
+ lldb_private::TraceQueryDataSimplePacket &packet, Path path);
+
+Value toJSON(const lldb_private::TraceQueryDataSimplePacket &packet);
+
+bool fromJSON(const Value &value,
+ lldb_private::TraceIntelPTQueryBufferParams ¶ms, Path path);
+
+Value toJSON(const lldb_private::TraceIntelPTQueryBufferParams &buffer);
+
+template <typename TParams>
+bool fromJSON(const Value &value,
+ lldb_private::TraceQueryDataPacket<TParams> &packet, Path path) {
+ ObjectMapper o(value, path);
+ return o &&
+ fromJSON(value, (lldb_private::TraceQueryDataSimplePacket &)packet,
+ path) &&
+ o.map("params", packet.params);
+}
+
+template <typename TParams>
+Value toJSON(const lldb_private::TraceQueryDataPacket<TParams> &packet) {
+ return llvm::json::Object{{"query", packet.query},
+ {"type", packet.type},
+ {"params", toJSON(packet.params)}};
+}
+
+bool fromJSON(const Value &value, lldb_private::TraceIntelPTBuffer &response,
+ Path path);
+
+Value toJSON(const lldb_private::TraceIntelPTBuffer &buffer);
+
+bool fromJSON(const Value &value, lldb_private::TraceIntelPTCPUConfig &pt_cpu,
+ Path path);
+
+Value toJSON(const lldb_private::TraceIntelPTCPUConfig &cpu_config);
+
+/// \}
+
bool fromJSON(const Value &value, lldb_private::TraceTypeInfo &info, Path path);
} // namespace json
Index: lldb/include/lldb/Utility/StringExtractorGDBRemote.h
===================================================================
--- lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -168,6 +168,9 @@
eServerPacketType_jTraceStop, // deprecated
eServerPacketType_jTraceConfigRead, // deprecated
+ eServerPacketType_jLLDBTraceQueryData,
+ eServerPacketType_jLLDBTraceStart,
+ eServerPacketType_jLLDBTraceStop,
eServerPacketType_jLLDBTraceSupportedType,
};
Index: lldb/include/lldb/Target/Trace.h
===================================================================
--- lldb/include/lldb/Target/Trace.h
+++ lldb/include/lldb/Target/Trace.h
@@ -12,7 +12,9 @@
#include "llvm/Support/JSON.h"
#include "lldb/Core/PluginInterface.h"
+#include "lldb/Target/Thread.h"
#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/TraceOptions.h"
#include "lldb/Utility/UnimplementedError.h"
#include "lldb/lldb-private.h"
@@ -97,6 +99,21 @@
FindPlugin(Debugger &debugger, const llvm::json::Value &trace_session_file,
llvm::StringRef session_file_dir);
+ /// Find a trace plug-in to trace a live process.
+ ///
+ /// \param[in] plugin_name
+ /// Plug-in name to search.
+ ///
+ /// \param[in] process
+ /// Live process to trace.
+ ///
+ /// \return
+ /// A \a TraceSP instance, or an \a llvm::Error if the plug-in name
+ /// doesn't match any registered plug-ins or tracing couldn't be
+ /// started.
+ static llvm::Expected<lldb::TraceSP> FindPlugin(llvm::StringRef plugin_name,
+ Process &process);
+
/// Get the schema of a Trace plug-in given its name.
///
/// \param[in] plugin_name
@@ -196,8 +213,102 @@
/// \return
/// The total number of instructions in the trace.
virtual size_t GetInstructionCount(const Thread &thread) = 0;
+
+protected:
+ /// Start tracing a thread.
+ ///
+ /// Base implementation that should be invoked by plug-ins to start tracing.
+ ///
+ /// \param[in] thread
+ /// The thread to trace.
+ ///
+ /// \param[in] params
+ /// json-serializable params specific to the thread plug-in.
+ ///
+ /// \return
+ /// \a llvm::Error::success if the operation was successful, or
+ /// \a llvm::Error otherwise.
+ template <typename TParams>
+ llvm::Error StartTracingThread(Thread &thread, const TParams ¶ms);
+
+ /// Query tracing-related data.
+ ///
+ /// Base implementation that should be invoked by plug-ins to query data.
+ ///
+ /// \param[in] process
+ /// The process that owns the queried data.
+ ///
+ /// \param[in] trace_type
+ /// The tracing technology, e.g. intel-pt, arm-coresight, etc.
+ ///
+ /// \param[in] query
+ /// An identifier for the requested data.
+ ///
+ /// \param[in] params
+ /// JSON-serializable params specific to the query.
+ ///
+ /// \return
+ /// A JSON-serializable templated object holding the requested data, or
+ /// an \a llvm::Error in case of failures.
+ template <typename TResponse, typename TParams>
+ static llvm::Expected<TResponse>
+ QueryData(Process &process, llvm::StringRef trace_type, llvm::StringRef query,
+ const TParams ¶ms);
+
+ /// Version of \a Query Data that doesn't require parameters for the query.
+ template <typename TResponse>
+ static llvm::Expected<TResponse> QueryData(Process &process,
+ llvm::StringRef trace_type,
+ llvm::StringRef query);
+
+private:
+ /// Helper class to reduce the implementation of the templated \a
+ /// StartTracingThread.
+ llvm::Error DoStartTracingThread(Thread &thread,
+ const llvm::json::Value &packet);
+
+ /// Helper class to reduce the implementation of the templated \a QueryData.
+ static llvm::Expected<std::string>
+ DoQueryData(Process &process, const llvm::json::Value &packet);
};
+template <typename TParams>
+llvm::Error Trace::StartTracingThread(Thread &thread, const TParams ¶ms) {
+ TraceStartPacket<TParams> packet(GetPluginName().GetStringRef(),
+ thread.GetID(), params);
+ return DoStartTracingThread(thread, llvm::json::toJSON(packet));
+}
+
+template <typename TResponse, typename TParams>
+llvm::Expected<TResponse>
+Trace::QueryData(Process &process, llvm::StringRef trace_type,
+ llvm::StringRef query, const TParams ¶ms) {
+ TraceQueryDataPacket<TParams> packet(trace_type, query, params);
+
+ llvm::Expected<std::string> response =
+ DoQueryData(process, llvm::json::toJSON(packet));
+
+ if (response)
+ return llvm::json::parse<TResponse>(*response);
+ else
+ return response.takeError();
+}
+
+template <typename TResponse>
+llvm::Expected<TResponse> Trace::QueryData(Process &process,
+ llvm::StringRef trace_type,
+ llvm::StringRef query) {
+ TraceQueryDataSimplePacket packet(trace_type, query);
+
+ llvm::Expected<std::string> response =
+ DoQueryData(process, llvm::json::toJSON(packet));
+
+ if (response)
+ return llvm::json::parse<TResponse>(*response);
+ else
+ return response.takeError();
+}
+
} // namespace lldb_private
#endif // LLDB_TARGET_TRACE_H
Index: lldb/include/lldb/Target/Process.h
===================================================================
--- lldb/include/lldb/Target/Process.h
+++ lldb/include/lldb/Target/Process.h
@@ -2555,6 +2555,48 @@
/// not supported for the inferior.
virtual llvm::Expected<TraceTypeInfo> GetSupportedTraceType();
+ /// Start tracing a thread. It fails if the thread is already being traced.
+ ///
+ /// \param[in] options
+ /// JSON structure with the parameters to start the tracing.
+ ///
+ /// \return
+ /// \a llvm::Error::success if the operation was successful, or an
+ /// \a llvm::Error otherwise.
+ virtual llvm::Error StartTracingThread(const llvm::json::Value &options) {
+ return llvm::make_error<UnimplementedError>();
+ }
+
+ /// Stop tracing a thread.
+ ///
+ /// \param[in] tid
+ /// The thread id to stop tracing.
+ ///
+ /// \param[in] trace_type
+ /// The trace technology name to start tracing with, e.g. intel-pt,
+ /// arm-coresight, etc.
+ ///
+ /// \return
+ /// \a llvm::Error::success if stopping was successful, or an
+ /// \a llvm::Error otherwise.
+ virtual llvm::Error StopTracingThread(lldb::tid_t tid,
+ llvm::StringRef trace_type) {
+ return llvm::make_error<UnimplementedError>();
+ }
+
+ /// Query tracing-related data
+ ///
+ /// \param[in] query
+ /// JSON structure with the parameters that describe the query.
+ ///
+ /// \return
+ /// An unparsed JSON string with the queried data, or an \a llvm::Error
+ /// in case of failures.
+ virtual llvm::Expected<std::string>
+ TraceQueryData(const llvm::json::Value &query) {
+ return llvm::make_error<UnimplementedError>();
+ }
+
// This calls a function of the form "void * (*)(void)".
bool CallVoidArgVoidPtrReturn(const Address *address,
lldb::addr_t &returned_func,
Index: lldb/include/lldb/Host/common/NativeProcessProtocol.h
===================================================================
--- lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -306,6 +306,67 @@
MainLoop &mainloop) const = 0;
};
+ /// Intel PT interface
+ /// \{
+
+ /// Stop tracing a thread.
+ ///
+ /// \param[in] tid
+ /// The thread id to stop tracing.
+ ///
+ /// \return
+ /// \a llvm::Error::success if stopping was successful, or an
+ /// \a llvm::Error otherwise.
+ virtual llvm::Error StopIntelPTTrace(lldb::tid_t tid) {
+ return llvm::make_error<UnimplementedError>();
+ }
+
+ /// Start tracing a thread. It fails if the thread is already being traced.
+ ///
+ /// \param[in] tid
+ /// The thread id to start tracing.
+ ///
+ /// \param[in] buffer_size_in_kb
+ /// The size of the circular trace buffer. It is rounded up to the nearest
+ /// power of 2.
+ ///
+ /// \param[in] perf_config
+ /// Low level perf event config. See
+ /// https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/perf-intel-pt.txt
+ /// for more information
+ ///
+ /// \return
+ /// \a llvm::Error::success if the operation was successful, or an
+ /// \a llvm::Error otherwise.
+ virtual llvm::Error StartIntelPTTrace(lldb::tid_t tid,
+ size_t buffer_size_in_kb,
+ uint32_t perf_config = 0) {
+ return llvm::make_error<UnimplementedError>();
+ }
+
+ /// Get the trace buffer for a thread.
+ ///
+ /// \param[in] tid
+ /// The thread id of the thread whose trace buffer is requested.
+ ///
+ /// \return
+ /// A \a TraceIntelPTBuffer object, or an \a llvm::Error in case of
+ /// failures.
+ virtual llvm::Expected<TraceIntelPTBuffer> GetIntelPTBuffer(lldb::tid_t tid) {
+ return llvm::make_error<UnimplementedError>();
+ }
+
+ /// Get the Intel CPU config for the current host, which is used for decoding
+ /// trace buffers.
+ ///
+ /// \return
+ /// A \a TraceIntelPTCPUConfig object, or an \a llvm::Error in case of
+ /// failures.
+ virtual llvm::Expected<TraceIntelPTCPUConfig> GetIntelPTCPUConfig() {
+ return llvm::make_error<UnimplementedError>();
+ }
+ /// \}
+
/// StartTracing API for starting a tracing instance with the
/// TraceOptions on a specific thread or process.
///
Index: lldb/include/lldb/Core/PluginManager.h
===================================================================
--- lldb/include/lldb/Core/PluginManager.h
+++ lldb/include/lldb/Core/PluginManager.h
@@ -331,14 +331,20 @@
GetSymbolVendorCreateCallbackAtIndex(uint32_t idx);
// Trace
- static bool RegisterPlugin(ConstString name, const char *description,
- TraceCreateInstance create_callback,
- llvm::StringRef schema,
- TraceGetStartCommand get_start_command);
+ static bool RegisterPlugin(
+ ConstString name, const char *description,
+ TraceCreateInstanceForSessionFile create_callback_for_session_file,
+ TraceCreateInstanceForLiveProcess create_callback_for_live_process,
+ llvm::StringRef schema, TraceGetStartCommand get_start_command);
+
+ static bool
+ UnregisterPlugin(TraceCreateInstanceForSessionFile create_callback);
- static bool UnregisterPlugin(TraceCreateInstance create_callback);
+ static TraceCreateInstanceForSessionFile
+ GetTraceCreateCallback(ConstString plugin_name);
- static TraceCreateInstance GetTraceCreateCallback(ConstString plugin_name);
+ static TraceCreateInstanceForLiveProcess
+ GetTraceCreateCallbackForLiveProcess(ConstString plugin_name);
static lldb::CommandObjectSP
GetTraceStartCommand(llvm::StringRef plugin_name,
Index: lldb/docs/lldb-gdb-remote.txt
===================================================================
--- lldb/docs/lldb-gdb-remote.txt
+++ lldb/docs/lldb-gdb-remote.txt
@@ -261,6 +261,110 @@
send packet: jLLDBTraceSupportedType
read packet: {"name": <name>, "description", <description>}/E<error code>;AAAAAAAAA
+//----------------------------------------------------------------------
+// jLLDBTraceStart
+//
+// BRIEF
+// Start tracing a thread using a provided tracing technology. The input
+// is specified as a JSON object. If tracing started succesfully, an OK
+// response is returned, or an error otherwise.
+//
+// SCHEMA
+// The schema for the input is
+//
+// {
+// "type": <tracing technology name, e.g. intel-pt, arm-coresight>
+// "tid": <thread id trace in decimal>
+// "params": {
+// <JSON object with parameters specific to the selected tracing technology>
+// }
+// }
+//
+// intel-pt
+// The schema for the "params" field for the intel-pt tracing technology is
+//
+// {
+// "bufferSizeInKB": <trace buffer size in KB in decimal>
+// "perf_config": <custom low-level perf event intel-pt config in decimal>
+// }
+//----------------------------------------------------------------------
+
+send packet: jLLDBTraceStart:{"type":<type>,"tid":<tid>,"params":<params>}]
+read packet: OK/E<error code>;AAAAAAAAA
+
+//----------------------------------------------------------------------
+// jLLDBTraceStop
+//
+// BRIEF
+// Stop tracing a thread using a provided tracing technology. The input
+// is specified as a JSON object. If tracing was stopped succesfully, an OK
+// response is returned, or an error otherwise.
+//
+// SCHEMA
+// The schema for the input is
+//
+// {
+// "type": <tracing technology name, e.g. intel-pt, arm-coresight>
+// "tid": <thread id trace in decimal>
+// }
+//----------------------------------------------------------------------
+
+send packet: jLLDBTraceStop:{"type":<type>,"tid":<tid>}]
+read packet: OK/E<error code>;AAAAAAAAA
+
+//----------------------------------------------------------------------
+// jLLDBTraceQueryData
+//
+// BRIEF
+// Get custom data given a trace technology and a query identifier. The input
+// is specified as a JSON object. The response is a JSON object that
+// depends on the queried data, or an error in case of failures.
+//
+// SCHEMA
+// The schema for the input is
+//
+// {
+// "type": <tracing technology name, e.g. intel-pt, arm-coresight>
+// "query": <identifier for the data>
+// "params": {
+// <optional JSON object with parameters specific to the queried data>
+// }
+// }
+//
+// intel-pt query "pt_cpu"
+//
+// This query doesn't not have any input params.
+//
+// The schema for the return value is
+//
+// {
+// "vendor": "intel" | "unknown",
+// "family": <decimal>,
+// "model": <decimal>,
+// "stepping": <decimal>
+// }
+//
+// intel-pt query "buffer"
+//
+// The schema for the "params" field in this query is
+//
+// {
+// "tid": <thread id trace in decimal>
+// }
+//
+// The schema for the return value is
+//
+// {
+// "data": <contents of the trace buffer in HEX8 format>
+// }
+//----------------------------------------------------------------------
+
+send packet: jLLDBTraceQueryData:{"type":<type>,"query":<query>}]
+read packet: {return value}/E<error code>;AAAAAAAAA
+
+send packet: jLLDBTraceQueryData:{"type":<type>,"query":<query>,"params":<params>}]
+read packet: {return value}/E<error code>;AAAAAAAAA
+
//----------------------------------------------------------------------
// jTraceStart:
//
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits