[Lldb-commits] [lldb] 0b69756 - [trace][intel-pt] Implement trace start and trace stop
Walter Erquinigo via lldb-commits
lldb-commits at lists.llvm.org
Tue Mar 30 17:31:45 PDT 2021
Author: Walter Erquinigo
Date: 2021-03-30T17:31:37-07:00
New Revision: 0b69756110db444282c40ea16929186b2910c3b1
URL: https://github.com/llvm/llvm-project/commit/0b69756110db444282c40ea16929186b2910c3b1
DIFF: https://github.com/llvm/llvm-project/commit/0b69756110db444282c40ea16929186b2910c3b1.diff
LOG: [trace][intel-pt] Implement trace start and trace stop
This implements the interactive trace start and stop methods.
This diff ended up being much larger than I anticipated because, by doing it, I found that I had implemented in the beginning many things in a non optimal way. In any case, the code is much better now.
There's a lot of boilerplate code due to the gdb-remote protocol, but the main changes are:
- New tracing packets: jLLDBTraceStop, jLLDBTraceStart, jLLDBTraceGetBinaryData. The gdb-remote packet definitions are quite comprehensive.
- Implementation of the "process trace start|stop" and "thread trace start|stop" commands.
- Implementaiton of an API in Trace.h to interact with live traces.
- Created an IntelPTDecoder for live threads, that use the debugger's stop id as checkpoint for its internal cache.
- Added a functionality to stop the process in case "process tracing" is enabled and a new thread can't traced.
- Added tests
I have some ideas to unify the code paths for post mortem and live threads, but I'll do that in another diff.
Differential Revision: https://reviews.llvm.org/D91679
Added:
lldb/include/lldb/Target/ThreadPostMortemTrace.h
lldb/include/lldb/Utility/TraceGDBRemotePackets.h
lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
lldb/source/Plugins/Process/Linux/IntelPTManager.cpp
lldb/source/Plugins/Process/Linux/IntelPTManager.h
lldb/source/Plugins/Trace/intel-pt/forward-declarations.h
lldb/source/Target/ThreadPostMortemTrace.cpp
lldb/source/Utility/TraceGDBRemotePackets.cpp
lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
lldb/test/API/commands/trace/multiple-threads/Makefile
lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py
lldb/test/API/commands/trace/multiple-threads/main.cpp
lldb/unittests/Process/Linux/IntelPTManagerTests.cpp
Modified:
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/ProcessTrace.h
lldb/include/lldb/Target/StopInfo.h
lldb/include/lldb/Target/Target.h
lldb/include/lldb/Target/Trace.h
lldb/include/lldb/Target/TraceSessionFileParser.h
lldb/include/lldb/Utility/StringExtractorGDBRemote.h
lldb/include/lldb/Utility/TraceOptions.h
lldb/include/lldb/lldb-enumerations.h
lldb/include/lldb/lldb-forward.h
lldb/include/lldb/lldb-private-interfaces.h
lldb/source/API/SBThread.cpp
lldb/source/API/SBTrace.cpp
lldb/source/Commands/CommandObjectProcess.cpp
lldb/source/Commands/CommandObjectThread.cpp
lldb/source/Commands/CommandObjectThreadUtil.cpp
lldb/source/Commands/CommandObjectThreadUtil.h
lldb/source/Commands/CommandObjectTrace.cpp
lldb/source/Commands/CommandObjectTrace.h
lldb/source/Core/PluginManager.cpp
lldb/source/Interpreter/CommandInterpreter.cpp
lldb/source/Plugins/Process/Linux/CMakeLists.txt
lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
lldb/source/Plugins/Process/Linux/NativeThreadLinux.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/DecodedThread.cpp
lldb/source/Plugins/Trace/intel-pt/DecodedThread.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/CMakeLists.txt
lldb/source/Target/Process.cpp
lldb/source/Target/ProcessTrace.cpp
lldb/source/Target/StopInfo.cpp
lldb/source/Target/Target.cpp
lldb/source/Target/Thread.cpp
lldb/source/Target/Trace.cpp
lldb/source/Target/TraceSessionFileParser.cpp
lldb/source/Utility/CMakeLists.txt
lldb/source/Utility/StringExtractorGDBRemote.cpp
lldb/test/API/commands/trace/TestTraceLoad.py
lldb/test/API/commands/trace/TestTraceSchema.py
lldb/test/API/commands/trace/TestTraceStartStop.py
lldb/test/API/commands/trace/intelpt-trace-multi-file/multi-file-no-ld.json
lldb/test/API/commands/trace/intelpt-trace/trace.json
lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json
lldb/test/API/commands/trace/intelpt-trace/trace_bad.json
lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json
lldb/test/API/commands/trace/intelpt-trace/trace_bad3.json
lldb/test/API/commands/trace/intelpt-trace/trace_bad4.json
lldb/test/API/commands/trace/intelpt-trace/trace_bad5.json
lldb/test/API/commands/trace/intelpt-trace/trace_bad_image.json
lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json
lldb/tools/lldb-vscode/JSONUtils.cpp
lldb/tools/lldb-vscode/LLDBUtils.cpp
lldb/unittests/Process/Linux/CMakeLists.txt
lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
Removed:
lldb/include/lldb/Target/ThreadTrace.h
lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp
lldb/source/Plugins/Process/Linux/ProcessorTrace.h
lldb/source/Target/ThreadTrace.cpp
lldb/source/Utility/TraceOptions.cpp
lldb/unittests/Process/Linux/ProcessorTraceTest.cpp
################################################################################
diff --git a/lldb/docs/lldb-gdb-remote.txt b/lldb/docs/lldb-gdb-remote.txt
index 7d333c8c8e6e8..fb92c5665be6e 100644
--- a/lldb/docs/lldb-gdb-remote.txt
+++ b/lldb/docs/lldb-gdb-remote.txt
@@ -235,22 +235,23 @@ send packet: QListThreadsInStopReply
read packet: OK
//----------------------------------------------------------------------
-// jLLDBTraceSupportedType
+// jLLDBTraceSupported
//
// BRIEF
// Get the processor tracing type supported by the gdb-server for the current
// inferior. Responses might be
diff erent depending on the architecture and
// capabilities of the underlying OS.
//
-// The return packet is a JSON object with the following schema
+// OUTPUT SCHEMA
+// {
+// "name": <string>,
+// Tracing technology name, e.g. intel-pt, arm-coresight.
+// "description": <string>,
+// Description for this technology.
+// }
//
-// {
-// "name": <tracing technology name, e.g. intel-pt, arm-coresight>
-// "description": <description string for this technology>
-// }
-//
-// If no tracing technology is supported for the inferior, or no process is
-// running, then an error should be returned.
+// If no tracing technology is supported for the inferior, or no process is
+// running, then an error message is returned.
//
// NOTE
// This packet is used by Trace plug-ins (see lldb_private::Trace.h) to
@@ -258,176 +259,217 @@ read packet: OK
// of the tracing technology returned by this packet.
//----------------------------------------------------------------------
-send packet: jLLDBTraceSupportedType
-read packet: {"name": <name>, "description", <description>}/E<error code>;AAAAAAAAA
+send packet: jLLDBTraceSupported
+read packet: {"name":<name>, "description":<description>}/E<error code>;AAAAAAAAA
//----------------------------------------------------------------------
-// jTraceStart:
-//
-// This packet is deprecated.
+// jLLDBTraceStart
//
// BRIEF
-// Packet for starting trace of type lldb::TraceType. The following
-// parameters should be appended to the packet formatted as a JSON
-// dictionary, where the schematic for the JSON dictionary in terms of
-// the recognized Keys is given below in the table.
-// Different tracing types could require
diff erent custom parameters.
-// Such custom tracing parameters if needed should be collectively
-// specified in a JSON dictionary and the dictionary can be appended
-// to this packet (as Value corresponding to "params"). Since sending
-// JSON data over gdb-remote protocol has certain limitations, binary
-// escaping convention should be used.
+// Start tracing a process or its threads using a provided tracing technology.
+// The input and output are specified as JSON objects. In case of success, an OK
+// response is returned, or an error otherwise.
+//
+// PROCESS TRACING
+// This traces existing and future threads of the current process. An error is
+// returned if the process is already being traced.
//
-// Following is the list of parameters -
+// THREAD TRACING
+// This traces specific threads.
//
-// Key Value (Integer) (O)Optional/
-// (except params which should be a (M)Mandatory
-// JSON dictionary)
-// ========== ====================================================
+// INPUT SCHEMA
+// {
+// "type": <string>,
+// Tracing technology name, e.g. intel-pt, arm-coresight.
+//
+// /* thread tracing only */
+// "tids": [<decimal integer>],
+// Individual threads to trace.
+//
+// ... other parameters specific to the provided tracing type
+// }
//
-// type The type of trace to start (see M
-// lldb-enumerations for TraceType)
+// NOTES
+// - If "tids" is not provided, then the operation is "process tracing",
+// otherwise it's "thread tracing".
+// - Each tracing technology can have
diff erent levels of support for "thread
+// tracing" and "process tracing".
//
-// buffersize The size of the buffer to allocate M
-// for trace gathering.
+// INTEL-PT
+// intel-pt supports both "thread tracing" and "process tracing".
//
-// threadid The id of the thread to start tracing O
-// on.
+// "Process tracing" is implemented by tracing each thread individually, but
+// managed by the same "process trace" instance.
+// Each actual thread trace, either from "process tracing" or "thread tracing",
+// is stored in an in-memory circular buffer, which keeps the most recent data.
//
-// metabuffersize The size of buffer to hold meta data O
-// used for decoding the trace data.
+// Additional params in the input schema:
+// {
+// "threadBufferSize": <decimal integer>,
+// Trace buffer size per thread in bytes. It must be a power of 2
+// greater than or equal to 4096 (2^12) bytes.
//
-// params Any parameters that are specific to O
-// certain trace technologies should be
-// collectively specified as a JSON
-// dictionary
-// ========== ====================================================
+// /* 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".
//
-// Each tracing instance is identified by a trace id which is returned
-// as the reply to this packet. In case the tracing failed to begin an
-// error code along with a hex encoded ASCII message is returned
-// instead.
+// Whenever a thread is attempted to be traced due to "process tracing"
+// and the limit would be reached, the process is stopped with a
+// "tracing" reason along with a meaningful description, so that the
+// user can retrace the process if needed.
+// }
+//
+// Notes:
+// - Modifying the parameters of an existing trace is not supported. The user
+// needs to stop the trace and start a new one.
+// - If "process tracing" is attempted and there are individual threads
+// already being traced with "thread tracing", these traces are left
+// unaffected and the threads not traced twice.
+// - If "thread tracing" is attempted on a thread already being traced with
+// either "thread tracing" or "process tracing", it fails.
//----------------------------------------------------------------------
-send packet: jTraceStart:{"type":<type>,"buffersize":<buffersize>}]
-read packet: <trace id>/E<error code>;AAAAAAAAA
+Process tracing:
+send packet: jLLDBTraceStart:{"type":<type>,...other params}]
+read packet: OK/E<error code>;AAAAAAAAA
+
+Thread tracing:
+send packet: jLLDBTraceStart:{"type":<type>,"tids":<tids>,...other params}]
+read packet: OK/E<error code>;AAAAAAAAA
//----------------------------------------------------------------------
-// jTraceStop:
-//
-// This packet is deprecated.
+// jLLDBTraceStop
//
// BRIEF
-// Stop tracing instance with trace id <trace id>, of course trace
-// needs to be started before. The following parameters should be
-// formatted as a JSON dictionary to the packet. Since sending
-// JSON data over gdb-remote protocol has certain limitations, binary
-// escaping convention should be used.
+// Stop tracing a process or its threads using a provided tracing technology.
+// The input and output are specified as JSON objects. In case of success, an OK
+// response is returned, or an error otherwise.
+//
+// PROCESS TRACE STOPPING
+// Stopping a process trace doesn't stop the active traces initiated with
+// "thread tracing".
//
-// Following is the list of parameters -
+// THREAD TRACE STOPPING
+// This is a best effort request, which tries to stop as many traces as
+// possible.
//
-// Key Value (Integer) (O)Optional/
-// (M)Mandatory
-// ========== ====================================================
+// INPUT SCHEMA
+// The schema for the input is
//
-// traceid The trace id of the tracing instance M
+// {
+// "type": <string>
+// Tracing technology name, e.g. intel-pt, arm-coresight.
+//
+// /* thread trace stopping only */
+// "tids": [<decimal integer>]
+// Individual thread traces to stop.
+// }
//
-// threadid The id of the thread to stop tracing O
-// on. Since <trace id> could map to
-// multiple trace instances (in case it
-// maps to the complete process), the
-// threadid of a particular thread could
-// be appended as "threadid:<thread id>;"
-// to stop tracing on that thread.
-// ========== ====================================================
+// NOTES
+// - If "tids" is not provided, then the operation is "process trace stopping".
//
-// An OK response is sent in case of success else an error code along
-// with a hex encoded ASCII message is returned.
+// INTEL PT
+// Stopping a specific thread trace started with "process tracing" is allowed.
//----------------------------------------------------------------------
-send packet: jTraceStop:{"traceid":<trace id>}]
-read packet: <OK response>/E<error code>;AAAAAAAAA
+Process trace stopping:
+send packet: jLLDBTraceStop:{"type":<type>}]
+read packet: OK/E<error code>;AAAAAAAAA
+
+Thread trace stopping:
+send packet: jLLDBTraceStop:{"type":<type>,"tids":<tids>}]
+read packet: OK/E<error code>;AAAAAAAAA
//----------------------------------------------------------------------
-// jTraceBufferRead:
-//
-// This packet is deprecated.
+// jLLDBTraceGetState
//
// BRIEF
-// Packet for reading the trace for tracing instance <trace id>, i.e the
-// id obtained from StartTrace API. The following parameters should be
-// formatted as a JSON dictionary to the packet. Since sending
-// JSON data over gdb-remote protocol has certain limitations, binary
-// escaping convention should be used.
-//
-// Following is the list of parameters -
+// Get the current state of the process and its threads being traced by
+// a given trace technology. The response is a JSON object with custom
+// information depending on the trace technology. In case of errors, an
+// error message is returned.
//
-// Key Value (Integer) (O)Optional/
-// (M)Mandatory
-// ========== ====================================================
-// traceid The trace id of the tracing instance M
+// INPUT SCHEMA
+// {
+// "type": <string>
+// Tracing technology name, e.g. intel-pt, arm-coresight.
+// }
//
-// offset The offset to start reading the data M
-// from.
+// OUTPUT SCHEMA
+// {
+// "tracedThreads": [{
+// "tid": <decimal integer>,
+// "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 thread data.
+// },
+// ]
+// }],
+// "processBinaryData": [
+// {
+// "kind": <string>,
+// Identifier for some binary data related to this process to
+// fetch with the jLLDBTraceGetBinaryData packet.
+// "size": <decimal integer>,
+// Size in bytes of this thread data.
+// },
+// }]
+// }
//
-// buffersize The size of the data intended to read. M
+// NOTES
+// - "traceThreads" includes all thread traced by both "process tracing" and
+// "thread tracing".
//
-// threadid The id of the thread to retrieve data O
-// from.
-// ========== ====================================================
+// INTEL PT
//
-// The trace data is sent as raw binary data if the read was successful
-// else an error code along with a hex encoded ASCII message is sent.
+// Binary data kinds:
+// - threadTraceBuffer: trace buffer for a thread.
+// - cpuInfo: contents of the /proc/cpuinfo file.
//----------------------------------------------------------------------
-send packet: jTraceBufferRead:{"traceid":<trace id>,"offset":<byteoffset>,"buffersize":<byte_count>}]
-read packet: <binary trace data>/E<error code>;AAAAAAAAA
+send packet: jLLDBTraceGetState:{"type":<type>}]
+read packet: {...object}/E<error code>;AAAAAAAAA
//----------------------------------------------------------------------
-// jTraceMetaRead:
-//
-// This packet is deprecated.
+// jLLDBTraceGetBinaryData
//
// BRIEF
-// Similar Packet as above except it reads meta data.
-//----------------------------------------------------------------------
-
-/----------------------------------------------------------------------
-// jTraceConfigRead:
+// Get binary data given a trace technology and a data identifier.
+// The input is specified as a JSON object and the response has the same format
+// as the "binary memory read" (aka "x") packet. In case of failures, an error
+// message is returned.
//
-// This packet is deprecated.
+// SCHEMA
+// The schema for the input is
//
-// BRIEF
-// Request the trace configuration for the tracing instance with id
-// <trace id>.
-//
-// Following is the list of parameters -
-//
-// Key Value (Integer) (O)Optional/
-// (M)Mandatory
-// ========== ====================================================
-// traceid The trace id of the tracing instance M
-//
-// threadid The id of the thread to obtain trace O
-// configuration from. Since <trace id>
-// could map to multiple trace instances
-// (in case it maps to the complete
-// process), the threadid of a particular
-// thread could be appended as
-// "threadid:<thread id>;" to obtain the
-// trace configuration of that thread.
-// ========== ====================================================
-//
-// In the response packet the trace configuration is sent as text,
-// formatted as a JSON dictionary. Since sending JSON data over
-// gdb-remote protocol has certain limitations, binary escaping
-// convention is used.
-// In case the trace instance with the <trace id> was not found, an
-// error code along with a hex encoded ASCII message is returned.
-//----------------------------------------------------------------------
-
-send packet: jTraceConfigRead:{"traceid":<trace id>}
-read packet: {"conf1":<conf1>,"conf2":<conf2>,"params":{"paramName":paramValue}]}];/E<error code>;AAAAAAAAA
+// {
+// "type": <string>,
+// Tracing technology name, e.g. intel-pt, arm-coresight.
+// "kind": <string>,
+// Identifier for the data.
+// "tid"?: <Optional decimal>,
+// Tid in decimal if the data belongs to a thread.
+// "offset": <decimal>,
+// Offset of the data in bytes.
+// "size": <decimal>,
+// Number of bytes in to read starting from the offset.
+// }
+//
+// INTEL PT
+//
+// Binary data kinds:
+// - threadTraceBuffer: trace buffer for a thread.
+// - cpuInfo: contents of the /proc/cpuinfo file.
+//----------------------------------------------------------------------
+
+send packet: jLLDBTraceGetBinaryData:{"type":<type>,"kind":<query>,"tid":<tid>,"offset":<offset>,"size":<size>}]
+read packet: <binary data>/E<error code>;AAAAAAAAA
//----------------------------------------------------------------------
// "qRegisterInfo<hex-reg-id>"
diff --git a/lldb/include/lldb/Core/PluginManager.h b/lldb/include/lldb/Core/PluginManager.h
index 0ac8308d17588..4fc2ffe095ce5 100644
--- a/lldb/include/lldb/Core/PluginManager.h
+++ b/lldb/include/lldb/Core/PluginManager.h
@@ -331,18 +331,20 @@ class PluginManager {
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);
- static bool UnregisterPlugin(TraceCreateInstance create_callback);
+ static bool
+ UnregisterPlugin(TraceCreateInstanceForSessionFile create_callback);
- static TraceCreateInstance GetTraceCreateCallback(ConstString plugin_name);
+ static TraceCreateInstanceForSessionFile
+ GetTraceCreateCallback(ConstString plugin_name);
- static lldb::CommandObjectSP
- GetTraceStartCommand(llvm::StringRef plugin_name,
- CommandInterpreter &interpreter);
+ static TraceCreateInstanceForLiveProcess
+ GetTraceCreateCallbackForLiveProcess(ConstString plugin_name);
/// Get the JSON schema for a trace session file corresponding to the given
/// plugin.
diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
index 5be9cb657382a..36b22d72b1962 100644
--- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -16,7 +16,7 @@
#include "lldb/Host/MainLoop.h"
#include "lldb/Utility/ArchSpec.h"
#include "lldb/Utility/Status.h"
-#include "lldb/Utility/TraceOptions.h"
+#include "lldb/Utility/TraceGDBRemotePackets.h"
#include "lldb/Utility/UnimplementedError.h"
#include "lldb/lldb-private-forward.h"
#include "lldb/lldb-types.h"
@@ -306,94 +306,55 @@ class NativeProcessProtocol {
MainLoop &mainloop) const = 0;
};
- /// StartTracing API for starting a tracing instance with the
- /// TraceOptions on a specific thread or process.
+ /// Start tracing a process or its threads.
///
- /// \param[in] config
- /// The configuration to use when starting tracing.
+ /// \param[in] json_params
+ /// JSON object with the information of what and how to trace.
+ /// In the case of gdb-remote, this object should conform to the
+ /// jLLDBTraceStart packet.
///
- /// \param[out] error
- /// Status indicates what went wrong.
+ /// This object should have a string entry called "type", which is the
+ /// tracing technology name.
///
- /// \return
- /// The API returns a user_id which can be used to get trace
- /// data, trace configuration or stopping the trace instance.
- /// The user_id is a key to identify and operate with a tracing
- /// instance. It may refer to the complete process or a single
- /// thread.
- virtual lldb::user_id_t StartTrace(const TraceOptions &config,
- Status &error) {
- error.SetErrorString("Not implemented");
- return LLDB_INVALID_UID;
- }
-
- /// StopTracing API as the name suggests stops a tracing instance.
- ///
- /// \param[in] traceid
- /// The user id of the trace intended to be stopped. Now a
- /// user_id may map to multiple threads in which case this API
- /// could be used to stop the tracing for a specific thread by
- /// supplying its thread id.
- ///
- /// \param[in] thread
- /// Thread is needed when the complete process is being traced
- /// and the user wishes to stop tracing on a particular thread.
+ /// \param[in] type
+ /// Tracing technology type, as described in the \a json_params.
///
/// \return
- /// Status indicating what went wrong.
- virtual Status StopTrace(lldb::user_id_t traceid,
- lldb::tid_t thread = LLDB_INVALID_THREAD_ID) {
- return Status("Not implemented");
+ /// \a llvm::Error::success if the operation was successful, or an
+ /// \a llvm::Error otherwise.
+ virtual llvm::Error TraceStart(llvm::StringRef json_params,
+ llvm::StringRef type) {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Unsupported tracing type '%s'",
+ type.data());
}
- /// This API provides the trace data collected in the form of raw
- /// data.
- ///
- /// \param[in] traceid thread
- /// The traceid and thread provide the context for the trace
- /// instance.
- ///
- /// \param[in] buffer
- /// The buffer provides the destination buffer where the trace
- /// data would be read to. The buffer should be truncated to the
- /// filled length by this function.
- ///
- /// \param[in] offset
- /// There is possibility to read partially the trace data from
- /// a specified offset where in such cases the buffer provided
- /// may be smaller than the internal trace collection container.
- ///
- /// \return
- /// The size of the data actually read.
- virtual Status GetData(lldb::user_id_t traceid, lldb::tid_t thread,
- llvm::MutableArrayRef<uint8_t> &buffer,
- size_t offset = 0) {
- return Status("Not implemented");
+ /// \copydoc Process::TraceStop(const TraceStopRequest &)
+ virtual llvm::Error TraceStop(const TraceStopRequest &request) {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Unsupported tracing type '%s'",
+ request.type.data());
}
- /// Similar API as above except it aims to provide any extra data
- /// useful for decoding the actual trace data.
- virtual Status GetMetaData(lldb::user_id_t traceid, lldb::tid_t thread,
- llvm::MutableArrayRef<uint8_t> &buffer,
- size_t offset = 0) {
- return Status("Not implemented");
+ /// \copydoc Process::TraceGetState(llvm::StringRef type)
+ virtual llvm::Expected<llvm::json::Value>
+ TraceGetState(llvm::StringRef type) {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Unsupported tracing type '%s'",
+ type.data());
}
- /// API to query the TraceOptions for a given user id
- ///
- /// \param[in] traceid
- /// The user id of the tracing instance.
- ///
- /// \param[out] config
- /// The configuration being used for tracing.
- ///
- /// \return A status indicating what went wrong.
- virtual Status GetTraceConfig(lldb::user_id_t traceid, TraceOptions &config) {
- return Status("Not implemented");
+ /// \copydoc Process::TraceGetBinaryData(const TraceGetBinaryDataRequest &)
+ virtual llvm::Expected<std::vector<uint8_t>>
+ TraceGetBinaryData(const TraceGetBinaryDataRequest &request) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Unsupported data kind '%s' for the '%s' tracing technology",
+ request.kind.c_str(), request.type.c_str());
}
- /// \copydoc Process::GetSupportedTraceType()
- virtual llvm::Expected<TraceTypeInfo> GetSupportedTraceType() {
+ /// \copydoc Process::TraceSupported()
+ virtual llvm::Expected<TraceSupportedResponse> TraceSupported() {
return llvm::make_error<UnimplementedError>();
}
diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h
index fbdb5069b39fc..dbe61d892c499 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -46,7 +46,7 @@
#include "lldb/Utility/ProcessInfo.h"
#include "lldb/Utility/Status.h"
#include "lldb/Utility/StructuredData.h"
-#include "lldb/Utility/TraceOptions.h"
+#include "lldb/Utility/TraceGDBRemotePackets.h"
#include "lldb/Utility/UnimplementedError.h"
#include "lldb/Utility/UserIDResolver.h"
#include "lldb/lldb-private.h"
@@ -2455,6 +2455,8 @@ void PruneThreadPlans();
lldb::StructuredDataPluginSP
GetStructuredDataPlugin(ConstString type_name) const;
+ /// Deprecated
+ ///
/// Starts tracing with the configuration provided in options. To enable
/// tracing on the complete process the thread_id in the options should be
/// set to LLDB_INVALID_THREAD_ID. The API returns a user_id which is needed
@@ -2469,6 +2471,8 @@ void PruneThreadPlans();
return LLDB_INVALID_UID;
}
+ /// Deprecated
+ ///
/// Stops the tracing instance leading to deletion of the trace data. The
/// tracing instance is identified by the user_id which is obtained when
/// tracing was started from the StartTrace. In case tracing of the complete
@@ -2479,6 +2483,8 @@ void PruneThreadPlans();
return Status("Not implemented");
}
+ /// Deprecated
+ ///
/// Provides the trace data as raw bytes. A buffer needs to be supplied to
/// copy the trace data. The exact behavior of this API may vary across
/// trace technology, as some may support partial reading of the trace data
@@ -2490,6 +2496,8 @@ void PruneThreadPlans();
return Status("Not implemented");
}
+ /// Deprecated
+ ///
/// Similar API as above except for obtaining meta data
virtual Status GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id,
llvm::MutableArrayRef<uint8_t> &buffer,
@@ -2497,17 +2505,8 @@ void PruneThreadPlans();
return Status("Not implemented");
}
- /// API to obtain the trace configuration used by a trace instance.
- /// Configurations that may be specific to some trace technology should be
- /// stored in the custom parameters. The options are transported to the
- /// server, which shall interpret accordingly. The thread_id can be
- /// specified in the options to obtain the configuration used by a specific
- /// thread. The thread_id specified should also match the uid otherwise an
- /// error will be returned.
- virtual Status GetTraceConfig(lldb::user_id_t uid, TraceOptions &options) {
- return Status("Not implemented");
- }
-
+protected:
+ friend class Trace;
/// Get the processor tracing type supported for this process.
/// Responses might be
diff erent depending on the architecture and
/// capabilities of the underlying OS.
@@ -2515,14 +2514,64 @@ void PruneThreadPlans();
/// \return
/// The supported trace type or an \a llvm::Error if tracing is
/// not supported for the inferior.
- virtual llvm::Expected<TraceTypeInfo> GetSupportedTraceType();
+ virtual llvm::Expected<TraceSupportedResponse> TraceSupported();
+
+ /// Start tracing a process or its threads.
+ ///
+ /// \param[in] request
+ /// JSON object with the information necessary to start tracing. In the
+ /// case of gdb-remote processes, this JSON object should conform to the
+ /// jLLDBTraceStart packet.
+ ///
+ /// \return
+ /// \a llvm::Error::success if the operation was successful, or
+ /// \a llvm::Error otherwise.
+ virtual llvm::Error TraceStart(const llvm::json::Value &request) {
+ return llvm::make_error<UnimplementedError>();
+ }
+
+ /// Stop tracing a live process or its threads.
+ ///
+ /// \param[in] request
+ /// The information determining which threads or process to stop tracing.
+ ///
+ /// \return
+ /// \a llvm::Error::success if the operation was successful, or
+ /// \a llvm::Error otherwise.
+ virtual llvm::Error TraceStop(const TraceStopRequest &request) {
+ return llvm::make_error<UnimplementedError>();
+ }
+
+ /// Get the current tracing state of the process and its threads.
+ ///
+ /// \param[in] type
+ /// Tracing technology type to consider.
+ ///
+ /// \return
+ /// A JSON object string with custom data depending on the trace
+ /// technology, or an \a llvm::Error in case of errors.
+ virtual llvm::Expected<std::string> TraceGetState(llvm::StringRef type) {
+ return llvm::make_error<UnimplementedError>();
+ }
+
+ /// Get binary data given a trace technology and a data identifier.
+ ///
+ /// \param[in] request
+ /// Object with the params of the requested data.
+ ///
+ /// \return
+ /// A vector of bytes with the requested data, or an \a llvm::Error in
+ /// case of failures.
+ virtual llvm::Expected<std::vector<uint8_t>>
+ TraceGetBinaryData(const TraceGetBinaryDataRequest &request) {
+ return llvm::make_error<UnimplementedError>();
+ }
// This calls a function of the form "void * (*)(void)".
bool CallVoidArgVoidPtrReturn(const Address *address,
lldb::addr_t &returned_func,
bool trap_exceptions = false);
-protected:
/// Update the thread list following process plug-in's specific logic.
///
/// This method should only be invoked by \a UpdateThreadList.
diff --git a/lldb/include/lldb/Target/ProcessTrace.h b/lldb/include/lldb/Target/ProcessTrace.h
index 55faba1576d00..7b9d6b13dd6f8 100644
--- a/lldb/include/lldb/Target/ProcessTrace.h
+++ b/lldb/include/lldb/Target/ProcessTrace.h
@@ -15,6 +15,8 @@
namespace lldb_private {
+/// Class that represents a defunct process loaded on memory via the "trace
+/// load" command.
class ProcessTrace : public PostMortemProcess {
public:
static void Initialize();
@@ -54,8 +56,6 @@ class ProcessTrace : public PostMortemProcess {
return error;
}
- bool IsAlive() override;
-
bool WarnBeforeDetach() const override { return false; }
size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size,
diff --git a/lldb/include/lldb/Target/StopInfo.h b/lldb/include/lldb/Target/StopInfo.h
index 4378d2d637993..2039b3a6fcf11 100644
--- a/lldb/include/lldb/Target/StopInfo.h
+++ b/lldb/include/lldb/Target/StopInfo.h
@@ -129,6 +129,9 @@ class StopInfo {
static lldb::StopInfoSP CreateStopReasonWithExec(Thread &thread);
+ static lldb::StopInfoSP
+ CreateStopReasonProcessorTrace(Thread &thread, const char *description);
+
static lldb::ValueObjectSP
GetReturnValueObject(lldb::StopInfoSP &stop_info_sp);
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 737efaf308829..0c2131a60b4bb 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -1126,7 +1126,12 @@ class Target : public std::enable_shared_from_this<Target>,
///
/// \return
/// The trace object. It might be undefined.
- const lldb::TraceSP &GetTrace();
+ lldb::TraceSP &GetTrace();
+
+ /// Similar to \a GetTrace, but this also tries to create a \a Trace object
+ /// if not available using the default supported tracing technology for
+ /// this process.
+ llvm::Expected<lldb::TraceSP &> GetTraceOrCreate();
// Since expressions results can persist beyond the lifetime of a process,
// and the const expression results are available after a process is gone, we
diff --git a/lldb/include/lldb/Target/ThreadTrace.h b/lldb/include/lldb/Target/ThreadPostMortemTrace.h
similarity index 77%
rename from lldb/include/lldb/Target/ThreadTrace.h
rename to lldb/include/lldb/Target/ThreadPostMortemTrace.h
index fee09a742a4e5..9cfe754ae0e4e 100644
--- a/lldb/include/lldb/Target/ThreadTrace.h
+++ b/lldb/include/lldb/Target/ThreadPostMortemTrace.h
@@ -1,4 +1,4 @@
-//===-- ThreadTrace.h -------------------------------------------*- C++ -*-===//
+//===-- ThreadPostMortemTrace.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.
@@ -6,21 +6,21 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLDB_TARGET_THREADTRACE_H
-#define LLDB_TARGET_THREADTRACE_H
+#ifndef LLDB_TARGET_THREADPOSTMORTEMTRACE_H
+#define LLDB_TARGET_THREADPOSTMORTEMTRACE_H
#include "lldb/Target/Thread.h"
namespace lldb_private {
-/// \class ThreadTrace ThreadTrace.h
+/// \class ThreadPostMortemTrace ThreadPostMortemTrace.h
///
/// Thread implementation used for representing threads gotten from trace
/// session files, which are similar to threads from core files.
///
/// See \a TraceSessionFileParser for more information regarding trace session
/// files.
-class ThreadTrace : public Thread {
+class ThreadPostMortemTrace : public Thread {
public:
/// \param[in] process
/// The process who owns this thread.
@@ -31,7 +31,8 @@ class ThreadTrace : public Thread {
/// \param[in] trace_file
/// The file that contains the list of instructions that were traced when
/// this thread was being executed.
- ThreadTrace(Process &process, lldb::tid_t tid, const FileSpec &trace_file)
+ ThreadPostMortemTrace(Process &process, lldb::tid_t tid,
+ const FileSpec &trace_file)
: Thread(process, tid), m_trace_file(trace_file) {}
void RefreshStateAfterStop() override;
@@ -54,8 +55,6 @@ class ThreadTrace : public Thread {
FileSpec m_trace_file;
};
-typedef std::shared_ptr<ThreadTrace> ThreadTraceSP;
-
} // namespace lldb_private
-#endif // LLDB_TARGET_THREADTRACE_H
+#endif // LLDB_TARGET_THREADPOSTMORTEMTRACE_H
diff --git a/lldb/include/lldb/Target/Trace.h b/lldb/include/lldb/Target/Trace.h
index 3b127916a9171..403b1f868a594 100644
--- a/lldb/include/lldb/Target/Trace.h
+++ b/lldb/include/lldb/Target/Trace.h
@@ -9,10 +9,14 @@
#ifndef LLDB_TARGET_TRACE_H
#define LLDB_TARGET_TRACE_H
+#include <unordered_map>
+
#include "llvm/Support/JSON.h"
#include "lldb/Core/PluginInterface.h"
+#include "lldb/Target/Thread.h"
#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/TraceGDBRemotePackets.h"
#include "lldb/Utility/UnimplementedError.h"
#include "lldb/lldb-private.h"
@@ -36,7 +40,7 @@ namespace lldb_private {
///
/// In order to support live tracing, the name of the plug-in should match the
/// name of the tracing type returned by the gdb-remote packet
-/// \a jLLDBTraceSupportedType.
+/// \a jLLDBTraceSupported.
class Trace : public PluginInterface,
public std::enable_shared_from_this<Trace> {
public:
@@ -94,8 +98,24 @@ class Trace : public PluginInterface,
/// The path to the directory that contains the session file. It's used to
/// resolved relative paths in the session file.
static llvm::Expected<lldb::TraceSP>
- FindPlugin(Debugger &debugger, const llvm::json::Value &trace_session_file,
- llvm::StringRef session_file_dir);
+ FindPluginForPostMortemProcess(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>
+ FindPluginForLiveProcess(llvm::StringRef plugin_name, Process &process);
/// Get the schema of a Trace plug-in given its name.
///
@@ -104,6 +124,14 @@ class Trace : public PluginInterface,
static llvm::Expected<llvm::StringRef>
FindPluginSchema(llvm::StringRef plugin_name);
+ /// Get the command handle for the "process trace start" command.
+ virtual lldb::CommandObjectSP
+ GetProcessTraceStartCommand(CommandInterpreter &interpreter) = 0;
+
+ /// Get the command handle for the "thread trace start" command.
+ virtual lldb::CommandObjectSP
+ GetThreadTraceStartCommand(CommandInterpreter &interpreter) = 0;
+
/// \return
/// The JSON schema of this Trace plug-in.
virtual llvm::StringRef GetSchema() = 0;
@@ -118,7 +146,7 @@ class Trace : public PluginInterface,
///
/// \return
/// The current position of the thread's trace or \b 0 if empty.
- virtual size_t GetCursorPosition(const Thread &thread) = 0;
+ virtual size_t GetCursorPosition(Thread &thread) = 0;
/// Dump \a count instructions of the given thread's trace ending at the
/// given \a end_position position.
@@ -172,30 +200,130 @@ class Trace : public PluginInterface,
/// The callback to execute on each instruction. If it returns \b false,
/// the iteration stops.
virtual void TraverseInstructions(
- const Thread &thread, size_t position, TraceDirection direction,
+ Thread &thread, size_t position, TraceDirection direction,
std::function<bool(size_t index, llvm::Expected<lldb::addr_t> load_addr)>
callback) = 0;
- /// Stop tracing a live thread
+ /// Get the number of available instructions in the trace of the given thread.
///
/// \param[in] thread
- /// The thread object to stop tracing.
+ /// The thread whose trace will be inspected.
///
/// \return
- /// An \a llvm::Error if stopping tracing failed, or \b
- /// llvm::Error::success() otherwise.
- virtual llvm::Error StopTracingThread(const Thread &thread) {
- return llvm::make_error<UnimplementedError>();
- }
+ /// The total number of instructions in the trace.
+ virtual size_t GetInstructionCount(Thread &thread) = 0;
- /// Get the number of available instructions in the trace of the given thread.
+ /// Check if a thread is currently traced by this object.
///
/// \param[in] thread
- /// The thread whose trace will be inspected.
+ /// The thread in question.
///
/// \return
- /// The total number of instructions in the trace.
- virtual size_t GetInstructionCount(const Thread &thread) = 0;
+ /// \b true if the thread is traced by this instance, \b false otherwise.
+ virtual bool IsTraced(const Thread &thread) = 0;
+
+ /// Stop tracing live threads.
+ ///
+ /// \param[in] tids
+ /// The threads to stop tracing on.
+ ///
+ /// \return
+ /// \a llvm::Error::success if the operation was successful, or
+ /// \a llvm::Error otherwise.
+ llvm::Error StopThreads(const std::vector<lldb::tid_t> &tids);
+
+ /// Stop tracing a live process.
+ ///
+ /// \param[in] request
+ /// The information determining which threads or process to stop tracing.
+ ///
+ /// \return
+ /// \a llvm::Error::success if the operation was successful, or
+ /// \a llvm::Error otherwise.
+ llvm::Error StopProcess();
+
+ /// Get the trace file of the given post mortem thread.
+ llvm::Expected<const FileSpec &> GetPostMortemTraceFile(lldb::tid_t tid);
+
+protected:
+ /// Get binary data of a live thread given a data identifier.
+ ///
+ /// \param[in] tid
+ /// The thread whose data is requested.
+ ///
+ /// \param[in] kind
+ /// The kind of data requested.
+ ///
+ /// \return
+ /// A vector of bytes with the requested data, or an \a llvm::Error in
+ /// case of failures.
+ llvm::Expected<std::vector<uint8_t>>
+ GetLiveThreadBinaryData(lldb::tid_t tid, llvm::StringRef kind);
+
+ /// Get binary data of the current process given a data identifier.
+ ///
+ /// \param[in] kind
+ /// The kind of data requested.
+ ///
+ /// \return
+ /// A vector of bytes with the requested data, or an \a llvm::Error in
+ /// case of failures.
+ llvm::Expected<std::vector<uint8_t>>
+ GetLiveProcessBinaryData(llvm::StringRef kind);
+
+ /// Get the size of the data returned by \a GetLiveThreadBinaryData
+ llvm::Optional<size_t> GetLiveThreadBinaryDataSize(lldb::tid_t tid,
+ llvm::StringRef kind);
+
+ /// Get the size of the data returned by \a GetLiveProcessBinaryData
+ llvm::Optional<size_t> GetLiveProcessBinaryDataSize(llvm::StringRef kind);
+ /// Constructor for post mortem processes
+ Trace() = default;
+
+ /// Constructor for a live process
+ Trace(Process &live_process) : m_live_process(&live_process) {}
+
+ /// Start tracing a live process or its threads.
+ ///
+ /// \param[in] request
+ /// JSON object with the information necessary to start tracing. In the
+ /// case of gdb-remote processes, this JSON object should conform to the
+ /// jLLDBTraceStart packet.
+ ///
+ /// \return
+ /// \a llvm::Error::success if the operation was successful, or
+ /// \a llvm::Error otherwise.
+ llvm::Error Start(const llvm::json::Value &request);
+
+ /// Get the current tracing state of a live process and its threads.
+ ///
+ /// \return
+ /// A JSON object string with custom data depending on the trace
+ /// technology, or an \a llvm::Error in case of errors.
+ llvm::Expected<std::string> GetLiveProcessState();
+
+ /// Method to be overriden by the plug-in to refresh its own state.
+ ///
+ /// This is invoked by RefreshLiveProcessState when a new state is found.
+ ///
+ /// \param[in] state
+ /// The jLLDBTraceGetState response.
+ virtual void
+ DoRefreshLiveProcessState(llvm::Expected<TraceGetStateResponse> state) = 0;
+
+ /// Method to be invoked by the plug-in to refresh the live process state.
+ ///
+ /// The result is cached through the same process stop.
+ void RefreshLiveProcessState();
+
+ /// Process traced by this object if doing live tracing. Otherwise it's null.
+ int64_t m_stop_id = -1;
+ Process *m_live_process = nullptr;
+ /// tid -> data kind -> size
+ std::map<lldb::tid_t, std::unordered_map<std::string, size_t>>
+ m_live_thread_data;
+ /// data kind -> size
+ std::unordered_map<std::string, size_t> m_live_process_data;
};
} // namespace lldb_private
diff --git a/lldb/include/lldb/Target/TraceSessionFileParser.h b/lldb/include/lldb/Target/TraceSessionFileParser.h
index 52cc27c1a485a..68abc7d006d34 100644
--- a/lldb/include/lldb/Target/TraceSessionFileParser.h
+++ b/lldb/include/lldb/Target/TraceSessionFileParser.h
@@ -11,7 +11,7 @@
#include "llvm/Support/JSON.h"
-#include "lldb/Target/ThreadTrace.h"
+#include "lldb/Target/ThreadPostMortemTrace.h"
namespace lldb_private {
@@ -68,7 +68,7 @@ class TraceSessionFileParser {
/// Helper struct holding the objects created when parsing a process
struct ParsedProcess {
lldb::TargetSP target_sp;
- std::vector<ThreadTraceSP> threads;
+ std::vector<lldb::ThreadPostMortemTraceSP> threads;
};
TraceSessionFileParser(Debugger &debugger, llvm::StringRef session_file_dir,
@@ -103,8 +103,8 @@ class TraceSessionFileParser {
/// modifies the given file_spec.
void NormalizePath(lldb_private::FileSpec &file_spec);
- ThreadTraceSP ParseThread(lldb::ProcessSP &process_sp,
- const JSONThread &thread);
+ lldb::ThreadPostMortemTraceSP ParseThread(lldb::ProcessSP &process_sp,
+ const JSONThread &thread);
llvm::Expected<ParsedProcess> ParseProcess(const JSONProcess &process);
diff --git a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
index 4ef3cea1aeb3a..da73708b91d11 100644
--- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -162,13 +162,11 @@ class StringExtractorGDBRemote : public StringExtractor {
eServerPacketType__m,
eServerPacketType_notify, // '%' notification
- eServerPacketType_jTraceStart, // deprecated
- eServerPacketType_jTraceBufferRead, // deprecated
- eServerPacketType_jTraceMetaRead, // deprecated
- eServerPacketType_jTraceStop, // deprecated
- eServerPacketType_jTraceConfigRead, // deprecated
-
- eServerPacketType_jLLDBTraceSupportedType,
+ eServerPacketType_jLLDBTraceSupported,
+ eServerPacketType_jLLDBTraceStart,
+ eServerPacketType_jLLDBTraceStop,
+ eServerPacketType_jLLDBTraceGetState,
+ eServerPacketType_jLLDBTraceGetBinaryData,
};
ServerPacketType GetServerPacketType() const;
diff --git a/lldb/include/lldb/Utility/TraceGDBRemotePackets.h b/lldb/include/lldb/Utility/TraceGDBRemotePackets.h
new file mode 100644
index 0000000000000..66fa5e2f8b516
--- /dev/null
+++ b/lldb/include/lldb/Utility/TraceGDBRemotePackets.h
@@ -0,0 +1,151 @@
+//===-- TraceGDBRemotePackets.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 LLDB_UTILITY_TRACEGDBREMOTEPACKETS_H
+#define LLDB_UTILITY_TRACEGDBREMOTEPACKETS_H
+
+#include "llvm/Support/JSON.h"
+
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-enumerations.h"
+
+/// See docs/lldb-gdb-remote.txt for more information.
+namespace lldb_private {
+
+/// jLLDBTraceSupported gdb-remote packet
+/// \{
+struct TraceSupportedResponse {
+ /// The name of the technology, e.g. intel-pt or arm-coresight.
+ ///
+ /// In order for a Trace plug-in (see \a lldb_private::Trace.h) to support the
+ /// trace technology given by this struct, it should match its name with this
+ /// field.
+ std::string name;
+ /// The description for the technology.
+ std::string description;
+};
+
+bool fromJSON(const llvm::json::Value &value, TraceSupportedResponse &info,
+ llvm::json::Path path);
+
+llvm::json::Value toJSON(const TraceSupportedResponse &packet);
+/// \}
+
+/// jLLDBTraceStart gdb-remote packet
+/// \{
+struct TraceStartRequest {
+ /// Tracing technology name, e.g. intel-pt, arm-coresight.
+ std::string type;
+ /// If \a llvm::None, then this starts tracing the whole process. Otherwise,
+ /// only tracing for the specified threads is enabled.
+ llvm::Optional<std::vector<int64_t>> tids;
+
+ bool IsProcessTracing() const;
+};
+
+bool fromJSON(const llvm::json::Value &value, TraceStartRequest &packet,
+ llvm::json::Path path);
+
+llvm::json::Value toJSON(const TraceStartRequest &packet);
+/// \}
+
+/// jLLDBTraceStop gdb-remote packet
+/// \{
+struct TraceStopRequest {
+ TraceStopRequest() {}
+
+ TraceStopRequest(llvm::StringRef type, const std::vector<lldb::tid_t> &tids);
+
+ TraceStopRequest(llvm::StringRef type) : type(type){};
+
+ bool IsProcessTracing() const;
+
+ /// Tracing technology name, e.g. intel-pt, arm-coresight.
+ std::string type;
+ /// If \a llvm::None, then this stops tracing the whole process. Otherwise,
+ /// only tracing for the specified threads is stopped.
+ llvm::Optional<std::vector<int64_t>> tids;
+};
+
+bool fromJSON(const llvm::json::Value &value, TraceStopRequest &packet,
+ llvm::json::Path path);
+
+llvm::json::Value toJSON(const TraceStopRequest &packet);
+///}
+
+/// jLLDBTraceGetState gdb-remote packet
+/// \{
+struct TraceGetStateRequest {
+ /// Tracing technology name, e.g. intel-pt, arm-coresight.
+ std::string type;
+};
+
+bool fromJSON(const llvm::json::Value &value, TraceGetStateRequest &packet,
+ llvm::json::Path path);
+
+llvm::json::Value toJSON(const TraceGetStateRequest &packet);
+
+struct TraceBinaryData {
+ /// Identifier of data to fetch with jLLDBTraceGetBinaryData.
+ std::string kind;
+ /// Size in bytes for this data.
+ int64_t size;
+};
+
+bool fromJSON(const llvm::json::Value &value, TraceBinaryData &packet,
+ llvm::json::Path path);
+
+llvm::json::Value toJSON(const TraceBinaryData &packet);
+
+struct TraceThreadState {
+ int64_t tid;
+ /// List of binary data objects for this thread.
+ std::vector<TraceBinaryData> binaryData;
+};
+
+bool fromJSON(const llvm::json::Value &value, TraceThreadState &packet,
+ llvm::json::Path path);
+
+llvm::json::Value toJSON(const TraceThreadState &packet);
+
+struct TraceGetStateResponse {
+ std::vector<TraceThreadState> tracedThreads;
+ std::vector<TraceBinaryData> processBinaryData;
+};
+
+bool fromJSON(const llvm::json::Value &value, TraceGetStateResponse &packet,
+ llvm::json::Path path);
+
+llvm::json::Value toJSON(const TraceGetStateResponse &packet);
+/// \}
+
+/// jLLDBTraceGetBinaryData gdb-remote packet
+/// \{
+struct TraceGetBinaryDataRequest {
+ /// Tracing technology name, e.g. intel-pt, arm-coresight.
+ std::string type;
+ /// Identifier for the data.
+ std::string kind;
+ /// Optional tid if the data is related to a thread.
+ llvm::Optional<int64_t> tid;
+ /// Offset in bytes from where to start reading the data.
+ int64_t offset;
+ /// Number of bytes to read.
+ int64_t size;
+};
+
+bool fromJSON(const llvm::json::Value &value,
+ lldb_private::TraceGetBinaryDataRequest &packet,
+ llvm::json::Path path);
+
+llvm::json::Value toJSON(const lldb_private::TraceGetBinaryDataRequest &packet);
+/// \}
+
+} // namespace lldb_private
+
+#endif // LLDB_UTILITY_TRACEGDBREMOTEPACKETS_H
diff --git a/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h b/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
new file mode 100644
index 0000000000000..95f4eeb27d3fa
--- /dev/null
+++ b/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
@@ -0,0 +1,38 @@
+//===-- TraceIntelPTGDBRemotePackets.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 LLDB_UTILITY_TRACEINTELPTGDBREMOTEPACKETS_H
+#define LLDB_UTILITY_TRACEINTELPTGDBREMOTEPACKETS_H
+
+#include "lldb/Utility/TraceGDBRemotePackets.h"
+
+/// See docs/lldb-gdb-remote.txt for more information.
+namespace lldb_private {
+
+/// jLLDBTraceStart gdb-remote packet
+/// \{
+struct TraceIntelPTStartRequest : TraceStartRequest {
+ /// Size in bytes to use for each thread's trace buffer.
+ int64_t threadBufferSize;
+ /// Required when doing "process tracing".
+ ///
+ /// Limit in bytes on all the thread traces started by this "process trace"
+ /// instance. When a thread is about to be traced and the limit would be hit,
+ /// then a "tracing" stop event is triggered.
+ llvm::Optional<int64_t> processBufferSizeLimit;
+};
+
+bool fromJSON(const llvm::json::Value &value, TraceIntelPTStartRequest &packet,
+ llvm::json::Path path);
+
+llvm::json::Value toJSON(const TraceIntelPTStartRequest &packet);
+/// \}
+
+} // namespace lldb_private
+
+#endif // LLDB_UTILITY_TRACEINTELPTGDBREMOTEPACKETS_H
diff --git a/lldb/include/lldb/Utility/TraceOptions.h b/lldb/include/lldb/Utility/TraceOptions.h
index c9a8d12ce1251..677ba0eabea36 100644
--- a/lldb/include/lldb/Utility/TraceOptions.h
+++ b/lldb/include/lldb/Utility/TraceOptions.h
@@ -16,18 +16,7 @@
namespace lldb_private {
-/// This struct represents a tracing technology.
-struct TraceTypeInfo {
- /// The name of the technology, e.g. intel-pt or arm-coresight.
- ///
- /// In order for a Trace plug-in (see \a lldb_private::Trace.h) to support the
- /// trace technology given by this struct, it should match its name with this
- /// field.
- std::string name;
- /// A description for the technology.
- std::string description;
-};
-
+/// Deprecated
class TraceOptions {
public:
TraceOptions() : m_trace_params(new StructuredData::Dictionary()) {}
@@ -68,14 +57,7 @@ class TraceOptions {
/// the lldb-server.
StructuredData::DictionarySP m_trace_params;
};
-}
-
-namespace llvm {
-namespace json {
-
-bool fromJSON(const Value &value, lldb_private::TraceTypeInfo &info, Path path);
-} // namespace json
-} // namespace llvm
+} // namespace lldb_private
#endif // LLDB_UTILITY_TRACEOPTIONS_H
diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h
index 8283b14cdee5b..dcd022b38eb9b 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -247,7 +247,8 @@ enum StopReason {
eStopReasonExec, ///< Program was re-exec'ed
eStopReasonPlanComplete,
eStopReasonThreadExiting,
- eStopReasonInstrumentation
+ eStopReasonInstrumentation,
+ eStopReasonProcessorTrace,
};
/// Command Return Status Types.
diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h
index 3cf5754862776..a3cba953ea1ae 100644
--- a/lldb/include/lldb/lldb-forward.h
+++ b/lldb/include/lldb/lldb-forward.h
@@ -227,7 +227,7 @@ class ThreadPlanStepRange;
class ThreadPlanStepThrough;
class ThreadPlanTracer;
class ThreadSpec;
-class ThreadTrace;
+class ThreadPostMortemTrace;
class Trace;
class TraceSessionFileParser;
class TraceOptions;
@@ -437,6 +437,8 @@ typedef std::shared_ptr<lldb_private::Thread> ThreadSP;
typedef std::weak_ptr<lldb_private::Thread> ThreadWP;
typedef std::shared_ptr<lldb_private::ThreadCollection> ThreadCollectionSP;
typedef std::shared_ptr<lldb_private::ThreadPlan> ThreadPlanSP;
+typedef std::shared_ptr<lldb_private::ThreadPostMortemTrace>
+ ThreadPostMortemTraceSP;
typedef std::weak_ptr<lldb_private::ThreadPlan> ThreadPlanWP;
typedef std::shared_ptr<lldb_private::ThreadPlanTracer> ThreadPlanTracerSP;
typedef std::shared_ptr<lldb_private::Trace> TraceSP;
diff --git a/lldb/include/lldb/lldb-private-interfaces.h b/lldb/include/lldb/lldb-private-interfaces.h
index df33f8af0e142..3086a01942666 100644
--- a/lldb/include/lldb/lldb-private-interfaces.h
+++ b/lldb/include/lldb/lldb-private-interfaces.h
@@ -111,12 +111,11 @@ typedef lldb::REPLSP (*REPLCreateInstance)(Status &error,
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 lldb::CommandObjectSP (*TraceGetStartCommand)(
- CommandInterpreter &interpreter);
-
+typedef llvm::Expected<lldb::TraceSP> (*TraceCreateInstanceForLiveProcess)(
+ Process &process);
} // namespace lldb_private
#endif // #if defined(__cplusplus)
diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp
index 0d50aceee5e41..4a1b53d4a5927 100644
--- a/lldb/source/API/SBThread.cpp
+++ b/lldb/source/API/SBThread.cpp
@@ -172,6 +172,7 @@ size_t SBThread::GetStopReasonDataCount() {
case eStopReasonPlanComplete:
case eStopReasonThreadExiting:
case eStopReasonInstrumentation:
+ case eStopReasonProcessorTrace:
// There is no data for these stop reasons.
return 0;
@@ -223,6 +224,7 @@ uint64_t SBThread::GetStopReasonDataAtIndex(uint32_t idx) {
case eStopReasonPlanComplete:
case eStopReasonThreadExiting:
case eStopReasonInstrumentation:
+ case eStopReasonProcessorTrace:
// There is no data for these stop reasons.
return 0;
diff --git a/lldb/source/API/SBTrace.cpp b/lldb/source/API/SBTrace.cpp
index 3fdabaa29ac21..242a282f033e0 100644
--- a/lldb/source/API/SBTrace.cpp
+++ b/lldb/source/API/SBTrace.cpp
@@ -77,18 +77,7 @@ void SBTrace::StopTrace(SBError &error, lldb::tid_t thread_id) {
}
void SBTrace::GetTraceConfig(SBTraceOptions &options, SBError &error) {
- LLDB_RECORD_METHOD(void, SBTrace, GetTraceConfig,
- (lldb::SBTraceOptions &, lldb::SBError &), options, error);
-
- ProcessSP process_sp(GetSP());
- error.Clear();
-
- if (!process_sp) {
- error.SetErrorString("invalid process");
- } else {
- error.SetError(process_sp->GetTraceConfig(GetTraceUID(),
- *(options.m_traceoptions_sp)));
- }
+ error.SetErrorString("deprecated");
}
lldb::user_id_t SBTrace::GetTraceUID() {
diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp
index 91ae4c64ab0e7..3e45b71cef085 100644
--- a/lldb/source/Commands/CommandObjectProcess.cpp
+++ b/lldb/source/Commands/CommandObjectProcess.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "CommandObjectProcess.h"
+#include "CommandObjectTrace.h"
#include "CommandOptionsProcessLaunch.h"
#include "lldb/Breakpoint/Breakpoint.h"
#include "lldb/Breakpoint/BreakpointLocation.h"
@@ -1577,6 +1578,71 @@ class CommandObjectProcessHandle : public CommandObjectParsed {
CommandOptions m_options;
};
+// Next are the subcommands of CommandObjectMultiwordProcessTrace
+
+// CommandObjectProcessTraceStart
+class CommandObjectProcessTraceStart : public CommandObjectTraceProxy {
+public:
+ CommandObjectProcessTraceStart(CommandInterpreter &interpreter)
+ : CommandObjectTraceProxy(
+ /*live_debug_session_only*/ true, interpreter,
+ "process trace start",
+ "Start tracing this process with the corresponding trace "
+ "plug-in.",
+ "process trace start [<trace-options>]") {}
+
+protected:
+ lldb::CommandObjectSP GetDelegateCommand(Trace &trace) override {
+ return trace.GetProcessTraceStartCommand(m_interpreter);
+ }
+};
+
+// CommandObjectProcessTraceStop
+class CommandObjectProcessTraceStop : public CommandObjectParsed {
+public:
+ CommandObjectProcessTraceStop(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process trace stop",
+ "Stop tracing this process. This does not affect "
+ "traces started with the "
+ "\"thread trace start\" command.",
+ "process trace stop",
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched |
+ eCommandProcessMustBePaused |
+ eCommandProcessMustBeTraced) {}
+
+ ~CommandObjectProcessTraceStop() override = default;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override {
+ ProcessSP process_sp = m_exe_ctx.GetProcessSP();
+
+ TraceSP trace_sp = process_sp->GetTarget().GetTrace();
+
+ if (llvm::Error err = trace_sp->StopProcess())
+ result.SetError(toString(std::move(err)));
+ else
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ return result.Succeeded();
+ }
+};
+
+// CommandObjectMultiwordProcessTrace
+class CommandObjectMultiwordProcessTrace : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordProcessTrace(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "trace", "Commands for tracing the current process.",
+ "process trace <subcommand> [<subcommand objects>]") {
+ LoadSubCommand("start", CommandObjectSP(new CommandObjectProcessTraceStart(
+ interpreter)));
+ LoadSubCommand("stop", CommandObjectSP(
+ new CommandObjectProcessTraceStop(interpreter)));
+ }
+
+ ~CommandObjectMultiwordProcessTrace() override = default;
+};
+
// CommandObjectMultiwordProcess
CommandObjectMultiwordProcess::CommandObjectMultiwordProcess(
@@ -1613,6 +1679,9 @@ CommandObjectMultiwordProcess::CommandObjectMultiwordProcess(
CommandObjectSP(new CommandObjectProcessPlugin(interpreter)));
LoadSubCommand("save-core", CommandObjectSP(new CommandObjectProcessSaveCore(
interpreter)));
+ LoadSubCommand(
+ "trace",
+ CommandObjectSP(new CommandObjectMultiwordProcessTrace(interpreter)));
}
CommandObjectMultiwordProcess::~CommandObjectMultiwordProcess() = default;
diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp
index f4ce5cc599cbc..a88fda9fa3d4f 100644
--- a/lldb/source/Commands/CommandObjectThread.cpp
+++ b/lldb/source/Commands/CommandObjectThread.cpp
@@ -11,6 +11,7 @@
#include <sstream>
#include "CommandObjectThreadUtil.h"
+#include "CommandObjectTrace.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/ValueObject.h"
#include "lldb/Host/OptionParser.h"
@@ -1978,75 +1979,34 @@ class CommandObjectMultiwordThreadPlan : public CommandObjectMultiword {
// CommandObjectTraceStart
-/// This class works by delegating the logic to the actual trace plug-in that
-/// can support the current process.
-class CommandObjectTraceStart : public CommandObjectProxy {
+class CommandObjectTraceStart : public CommandObjectTraceProxy {
public:
CommandObjectTraceStart(CommandInterpreter &interpreter)
- : CommandObjectProxy(interpreter, "thread trace start",
- "Start tracing threads with the corresponding trace "
- "plug-in for the current process.",
- "thread trace start [<trace-options>]") {}
+ : CommandObjectTraceProxy(
+ /*live_debug_session_only*/ true, interpreter, "thread trace start",
+ "Start tracing threads with the corresponding trace "
+ "plug-in for the current process.",
+ "thread trace start [<trace-options>]") {}
protected:
- llvm::Expected<CommandObjectSP> DoGetProxyCommandObject() {
- ProcessSP process_sp = m_interpreter.GetExecutionContext().GetProcessSP();
-
- if (!process_sp)
- return llvm::createStringError(llvm::inconvertibleErrorCode(),
- "Process not available.");
- if (!process_sp->IsAlive())
- return llvm::createStringError(llvm::inconvertibleErrorCode(),
- "Process must be launched.");
-
- llvm::Expected<TraceTypeInfo> trace_type =
- process_sp->GetSupportedTraceType();
-
- if (!trace_type)
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(), "Tracing is not supported. %s",
- llvm::toString(trace_type.takeError()).c_str());
-
- CommandObjectSP delegate_sp =
- PluginManager::GetTraceStartCommand(trace_type->name, m_interpreter);
- if (!delegate_sp)
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
- "No trace plug-in matches the specified type: \"%s\"",
- trace_type->name.c_str());
- return delegate_sp;
+ lldb::CommandObjectSP GetDelegateCommand(Trace &trace) override {
+ return trace.GetThreadTraceStartCommand(m_interpreter);
}
-
- CommandObject *GetProxyCommandObject() override {
- if (llvm::Expected<CommandObjectSP> delegate = DoGetProxyCommandObject()) {
- m_delegate_sp = *delegate;
- m_delegate_error.clear();
- return m_delegate_sp.get();
- } else {
- m_delegate_sp.reset();
- m_delegate_error = llvm::toString(delegate.takeError());
- return nullptr;
- }
- }
-
-private:
- llvm::StringRef GetUnsupportedError() override { return m_delegate_error; }
-
- CommandObjectSP m_delegate_sp;
- std::string m_delegate_error;
};
// CommandObjectTraceStop
-class CommandObjectTraceStop : public CommandObjectIterateOverThreads {
+class CommandObjectTraceStop : public CommandObjectMultipleThreads {
public:
CommandObjectTraceStop(CommandInterpreter &interpreter)
- : CommandObjectIterateOverThreads(
+ : CommandObjectMultipleThreads(
interpreter, "thread trace stop",
- "Stop tracing threads. "
+ "Stop tracing threads, including the ones traced with the "
+ "\"process trace start\" command."
"Defaults to the current thread. Thread indices can be "
- "specified as arguments.\n Use the thread-index \"all\" to trace "
- "all threads.",
+ "specified as arguments.\n Use the thread-index \"all\" to stop "
+ "tracing "
+ "for all existing threads.",
"thread trace stop [<thread-index> <thread-index> ...]",
eCommandRequiresProcess | eCommandTryTargetAPILock |
eCommandProcessMustBeLaunched | eCommandProcessMustBePaused |
@@ -2054,20 +2014,18 @@ class CommandObjectTraceStop : public CommandObjectIterateOverThreads {
~CommandObjectTraceStop() override = default;
- bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override {
- const Thread &thread =
- *m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid);
- Trace &trace = *m_exe_ctx.GetTargetSP()->GetTrace();
+ bool DoExecuteOnThreads(Args &command, CommandReturnObject &result,
+ const std::vector<lldb::tid_t> &tids) override {
+ ProcessSP process_sp = m_exe_ctx.GetProcessSP();
- if (llvm::Error err = trace.StopTracingThread(thread)) {
- result.AppendErrorWithFormat("Failed stopping thread %" PRIu64 ": %s\n",
- tid, toString(std::move(err)).c_str());
- result.SetStatus(eReturnStatusFailed);
- }
+ TraceSP trace_sp = process_sp->GetTarget().GetTrace();
- // We don't return false on errors to try to stop as many threads as
- // possible.
- return true;
+ if (llvm::Error err = trace_sp->StopThreads(tids))
+ result.SetError(toString(std::move(err)));
+ else
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ return result.Succeeded();
}
};
diff --git a/lldb/source/Commands/CommandObjectThreadUtil.cpp b/lldb/source/Commands/CommandObjectThreadUtil.cpp
index b93698c7be60b..b395f2d38b45a 100644
--- a/lldb/source/Commands/CommandObjectThreadUtil.cpp
+++ b/lldb/source/Commands/CommandObjectThreadUtil.cpp
@@ -156,3 +156,47 @@ bool CommandObjectIterateOverThreads::BucketThread(
}
return true;
}
+
+bool CommandObjectMultipleThreads::DoExecute(Args &command,
+ CommandReturnObject &result) {
+ Process &process = m_exe_ctx.GetProcessRef();
+
+ std::vector<lldb::tid_t> tids;
+ const size_t num_args = command.GetArgumentCount();
+
+ std::lock_guard<std::recursive_mutex> guard(
+ process.GetThreadList().GetMutex());
+
+ if (num_args > 0 && ::strcmp(command.GetArgumentAtIndex(0), "all") == 0) {
+ for (ThreadSP thread_sp : process.Threads())
+ tids.push_back(thread_sp->GetID());
+ } else {
+ if (num_args == 0) {
+ Thread &thread = m_exe_ctx.GetThreadRef();
+ tids.push_back(thread.GetID());
+ }
+
+ for (size_t i = 0; i < num_args; i++) {
+ uint32_t thread_idx;
+ if (!llvm::to_integer(command.GetArgumentAtIndex(i), thread_idx)) {
+ result.AppendErrorWithFormat("invalid thread specification: \"%s\"\n",
+ command.GetArgumentAtIndex(i));
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ ThreadSP thread = process.GetThreadList().FindThreadByIndexID(thread_idx);
+
+ if (!thread) {
+ result.AppendErrorWithFormat("no thread with index: \"%s\"\n",
+ command.GetArgumentAtIndex(i));
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ tids.push_back(thread->GetID());
+ }
+ }
+
+ return DoExecuteOnThreads(command, result, tids);
+}
diff --git a/lldb/source/Commands/CommandObjectThreadUtil.h b/lldb/source/Commands/CommandObjectThreadUtil.h
index 7122982d89433..18f65813c6398 100644
--- a/lldb/source/Commands/CommandObjectThreadUtil.h
+++ b/lldb/source/Commands/CommandObjectThreadUtil.h
@@ -76,6 +76,26 @@ class CommandObjectIterateOverThreads : public CommandObjectParsed {
bool m_add_return = true;
};
+/// Class similar to \a CommandObjectIterateOverThreads, but which performs
+/// an action on multiple threads at once instead of iterating over each thread.
+class CommandObjectMultipleThreads : public CommandObjectParsed {
+public:
+ using CommandObjectParsed::CommandObjectParsed;
+
+ bool DoExecute(Args &command, CommandReturnObject &result) override;
+
+protected:
+ /// Method that handles the command after the main arguments have been parsed.
+ ///
+ /// \param[in] tids
+ /// The thread ids passed as arguments.
+ ///
+ /// \return
+ /// A boolean result similar to the one expected from \a DoExecute.
+ virtual bool DoExecuteOnThreads(Args &command, CommandReturnObject &result,
+ const std::vector<lldb::tid_t> &tids) = 0;
+};
+
} // namespace lldb_private
#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTTHREADUTIL_H
diff --git a/lldb/source/Commands/CommandObjectTrace.cpp b/lldb/source/Commands/CommandObjectTrace.cpp
index 9d87e8cc4017b..b151a4dc80559 100644
--- a/lldb/source/Commands/CommandObjectTrace.cpp
+++ b/lldb/source/Commands/CommandObjectTrace.cpp
@@ -23,6 +23,7 @@
#include "lldb/Interpreter/OptionValueLanguage.h"
#include "lldb/Interpreter/OptionValueString.h"
#include "lldb/Interpreter/Options.h"
+#include "lldb/Target/Process.h"
#include "lldb/Target/Trace.h"
using namespace lldb;
@@ -113,8 +114,9 @@ class CommandObjectTraceLoad : public CommandObjectParsed {
return end_with_failure(session_file.takeError());
if (Expected<lldb::TraceSP> traceOrErr =
- Trace::FindPlugin(GetDebugger(), *session_file,
- json_file.GetDirectory().AsCString())) {
+ Trace::FindPluginForPostMortemProcess(
+ GetDebugger(), *session_file,
+ json_file.GetDirectory().AsCString())) {
lldb::TraceSP trace_sp = traceOrErr.get();
if (m_options.m_verbose && trace_sp)
result.AppendMessageWithFormat("loading trace with plugin %s\n",
@@ -303,3 +305,33 @@ CommandObjectTrace::CommandObjectTrace(CommandInterpreter &interpreter)
}
CommandObjectTrace::~CommandObjectTrace() = default;
+
+Expected<CommandObjectSP> CommandObjectTraceProxy::DoGetProxyCommandObject() {
+ ProcessSP process_sp = m_interpreter.GetExecutionContext().GetProcessSP();
+
+ if (!process_sp)
+ return createStringError(inconvertibleErrorCode(),
+ "Process not available.");
+ if (m_live_debug_session_only && !process_sp->IsLiveDebugSession())
+ return createStringError(inconvertibleErrorCode(),
+ "Process must be alive.");
+
+ if (Expected<TraceSP &> trace_sp = process_sp->GetTarget().GetTraceOrCreate())
+ return GetDelegateCommand(**trace_sp);
+ else
+ return createStringError(inconvertibleErrorCode(),
+ "Tracing is not supported. %s",
+ toString(trace_sp.takeError()).c_str());
+}
+
+CommandObject *CommandObjectTraceProxy::GetProxyCommandObject() {
+ if (Expected<CommandObjectSP> delegate = DoGetProxyCommandObject()) {
+ m_delegate_sp = *delegate;
+ m_delegate_error.clear();
+ return m_delegate_sp.get();
+ } else {
+ m_delegate_sp.reset();
+ m_delegate_error = toString(delegate.takeError());
+ return nullptr;
+ }
+}
diff --git a/lldb/source/Commands/CommandObjectTrace.h b/lldb/source/Commands/CommandObjectTrace.h
index 2dca0e26b2434..b96a3094cefc8 100644
--- a/lldb/source/Commands/CommandObjectTrace.h
+++ b/lldb/source/Commands/CommandObjectTrace.h
@@ -9,7 +9,7 @@
#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTTRACE_H
#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTTRACE_H
-#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "CommandObjectThreadUtil.h"
namespace lldb_private {
@@ -20,6 +20,32 @@ class CommandObjectTrace : public CommandObjectMultiword {
~CommandObjectTrace() override;
};
+/// This class works by delegating the logic to the actual trace plug-in that
+/// can support the current process.
+class CommandObjectTraceProxy : public CommandObjectProxy {
+public:
+ CommandObjectTraceProxy(bool live_debug_session_only,
+ CommandInterpreter &interpreter, const char *name,
+ const char *help = nullptr,
+ const char *syntax = nullptr, uint32_t flags = 0)
+ : CommandObjectProxy(interpreter, name, help, syntax, flags),
+ m_live_debug_session_only(live_debug_session_only) {}
+
+protected:
+ virtual lldb::CommandObjectSP GetDelegateCommand(Trace &trace) = 0;
+
+ llvm::Expected<lldb::CommandObjectSP> DoGetProxyCommandObject();
+
+ CommandObject *GetProxyCommandObject() override;
+
+private:
+ llvm::StringRef GetUnsupportedError() override { return m_delegate_error; }
+
+ bool m_live_debug_session_only;
+ lldb::CommandObjectSP m_delegate_sp;
+ std::string m_delegate_error;
+};
+
} // namespace lldb_private
#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTTRACE_H
diff --git a/lldb/source/Core/PluginManager.cpp b/lldb/source/Core/PluginManager.cpp
index 97e1e8d14039e..a826e123459f9 100644
--- a/lldb/source/Core/PluginManager.cpp
+++ b/lldb/source/Core/PluginManager.cpp
@@ -1008,16 +1008,20 @@ PluginManager::GetSymbolVendorCreateCallbackAtIndex(uint32_t idx) {
#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)
+ : PluginInstance<TraceCreateInstanceForSessionFile>(
+ name, std::move(description), create_callback_for_session_file),
+ schema(schema),
+ 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,37 +1031,40 @@ static TraceInstances &GetTracePluginInstances() {
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) {
return GetTracePluginInstances().RegisterPlugin(
- name, description, create_callback, schema, get_start_command);
+ name, description, create_callback_for_session_file,
+ create_callback_for_live_process, schema);
}
-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);
}
-llvm::StringRef PluginManager::GetTraceSchema(ConstString plugin_name) {
+TraceCreateInstanceForLiveProcess
+PluginManager::GetTraceCreateCallbackForLiveProcess(ConstString plugin_name) {
for (const TraceInstance &instance : GetTracePluginInstances().GetInstances())
if (instance.name == plugin_name)
- return instance.schema;
- return llvm::StringRef();
+ return instance.create_callback_for_live_process;
+ return nullptr;
}
-CommandObjectSP
-PluginManager::GetTraceStartCommand(llvm::StringRef plugin_name,
- CommandInterpreter &interpreter) {
+llvm::StringRef PluginManager::GetTraceSchema(ConstString plugin_name) {
for (const TraceInstance &instance : GetTracePluginInstances().GetInstances())
- if (instance.name.GetStringRef() == plugin_name)
- return instance.get_start_command(interpreter);
- return CommandObjectSP();
+ if (instance.name == plugin_name)
+ return instance.schema;
+ return llvm::StringRef();
}
llvm::StringRef PluginManager::GetTraceSchema(size_t index) {
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index eeef14bf36c27..a7aea8daf624e 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -2244,7 +2244,9 @@ bool CommandInterpreter::DidProcessStopAbnormally() const {
return false;
const StopReason reason = stop_info->GetStopReason();
- if (reason == eStopReasonException || reason == eStopReasonInstrumentation)
+ if (reason == eStopReasonException ||
+ reason == eStopReasonInstrumentation ||
+ reason == eStopReasonProcessorTrace)
return true;
if (reason == eStopReasonSignal) {
diff --git a/lldb/source/Plugins/Process/Linux/CMakeLists.txt b/lldb/source/Plugins/Process/Linux/CMakeLists.txt
index a68ff4ffd822a..c4edc57a8a2d8 100644
--- a/lldb/source/Plugins/Process/Linux/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/Linux/CMakeLists.txt
@@ -1,4 +1,5 @@
add_lldb_library(lldbPluginProcessLinux
+ IntelPTManager.cpp
NativeProcessLinux.cpp
NativeRegisterContextLinux.cpp
NativeRegisterContextLinux_arm.cpp
@@ -7,7 +8,6 @@ add_lldb_library(lldbPluginProcessLinux
NativeRegisterContextLinux_s390x.cpp
NativeRegisterContextLinux_x86_64.cpp
NativeThreadLinux.cpp
- ProcessorTrace.cpp
SingleStepCheck.cpp
LINK_LIBS
diff --git a/lldb/source/Plugins/Process/Linux/IntelPTManager.cpp b/lldb/source/Plugins/Process/Linux/IntelPTManager.cpp
new file mode 100644
index 0000000000000..14b08a6aa93c0
--- /dev/null
+++ b/lldb/source/Plugins/Process/Linux/IntelPTManager.cpp
@@ -0,0 +1,554 @@
+//===-- IntelPTManager.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 <algorithm>
+#include <fstream>
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MathExtras.h"
+
+#include "IntelPTManager.h"
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#include "lldb/Host/linux/Support.h"
+#include "lldb/Utility/StreamString.h"
+
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace process_linux;
+using namespace llvm;
+
+const char *kOSEventIntelPTTypeFile =
+ "/sys/bus/event_source/devices/intel_pt/type";
+
+/// Return the Linux perf event type for Intel PT.
+static Expected<uint32_t> GetOSEventType() {
+ auto intel_pt_type_text =
+ llvm::MemoryBuffer::getFileAsStream(kOSEventIntelPTTypeFile);
+
+ if (!intel_pt_type_text)
+ return createStringError(inconvertibleErrorCode(),
+ "Can't open the file '%s'",
+ kOSEventIntelPTTypeFile);
+
+ uint32_t intel_pt_type = 0;
+ StringRef buffer = intel_pt_type_text.get()->getBuffer();
+ if (buffer.trim().getAsInteger(10, intel_pt_type))
+ return createStringError(
+ inconvertibleErrorCode(),
+ "The file '%s' has a invalid value. It should be an unsigned int.",
+ kOSEventIntelPTTypeFile);
+ return intel_pt_type;
+}
+
+size_t IntelPTThreadTrace::GetTraceBufferSize() const {
+ return m_mmap_meta->aux_size;
+}
+
+Error IntelPTThreadTrace::StartTrace(lldb::pid_t pid, lldb::tid_t tid,
+ uint64_t buffer_size) {
+#ifndef PERF_ATTR_SIZE_VER5
+ llvm_unreachable("Intel PT Linux perf event not supported");
+#else
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+
+ m_tid = tid;
+ LLDB_LOG(log, "called thread id {0}", tid);
+ uint64_t page_size = getpagesize();
+
+ if (__builtin_popcount(buffer_size) != 1 || buffer_size < 4096) {
+ return createStringError(
+ inconvertibleErrorCode(),
+ "The trace buffer size must be a power of 2 greater than or equal to "
+ "4096 (2^12) bytes. It was %" PRIu64 ".",
+ buffer_size);
+ }
+ uint64_t numpages = static_cast<uint64_t>(
+ llvm::PowerOf2Floor((buffer_size + page_size - 1) / page_size));
+ numpages = std::max<uint64_t>(1, numpages);
+ buffer_size = page_size * numpages;
+
+ perf_event_attr attr;
+ memset(&attr, 0, sizeof(attr));
+ attr.size = sizeof(attr);
+ attr.exclude_kernel = 1;
+ attr.sample_type = PERF_SAMPLE_TIME;
+ attr.sample_id_all = 1;
+ attr.exclude_hv = 1;
+ attr.exclude_idle = 1;
+ attr.mmap = 1;
+ attr.config = 0;
+
+ Expected<uint32_t> intel_pt_type = GetOSEventType();
+
+ 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, "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);
+ 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, (buffer_size + page_size), PROT_WRITE, MAP_SHARED, fd, 0);
+
+ if (base == MAP_FAILED) {
+ LLDB_LOG(log, "mmap base error {0}", errno);
+ 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(buffer_size + page_size));
+
+ m_mmap_meta->aux_offset = m_mmap_meta->data_offset + m_mmap_meta->data_size;
+ m_mmap_meta->aux_size = buffer_size;
+
+ errno = 0;
+ 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);
+ 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(buffer_size));
+ return Error::success();
+#endif
+}
+
+llvm::MutableArrayRef<uint8_t> IntelPTThreadTrace::GetDataBuffer() const {
+#ifndef PERF_ATTR_SIZE_VER5
+ llvm_unreachable("Intel PT Linux perf event not supported");
+#else
+ return MutableArrayRef<uint8_t>(
+ (reinterpret_cast<uint8_t *>(m_mmap_meta.get()) +
+ m_mmap_meta->data_offset),
+ m_mmap_meta->data_size);
+#endif
+}
+
+llvm::MutableArrayRef<uint8_t> IntelPTThreadTrace::GetAuxBuffer() const {
+#ifndef PERF_ATTR_SIZE_VER5
+ llvm_unreachable("Intel PT Linux perf event not supported");
+#else
+ return MutableArrayRef<uint8_t>(m_mmap_aux.get(), m_mmap_meta->aux_size);
+#endif
+}
+
+Expected<ArrayRef<uint8_t>> IntelPTThreadTrace::GetCPUInfo() {
+ static llvm::Optional<std::vector<uint8_t>> cpu_info;
+ if (!cpu_info) {
+ auto buffer_or_error = getProcFile("cpuinfo");
+ if (!buffer_or_error)
+ return Status(buffer_or_error.getError()).ToError();
+ MemoryBuffer &buffer = **buffer_or_error;
+ cpu_info = std::vector<uint8_t>(
+ reinterpret_cast<const uint8_t *>(buffer.getBufferStart()),
+ reinterpret_cast<const uint8_t *>(buffer.getBufferEnd()));
+ }
+ return *cpu_info;
+}
+
+llvm::Expected<IntelPTThreadTraceUP>
+IntelPTThreadTrace::Create(lldb::pid_t pid, lldb::tid_t tid,
+ size_t buffer_size) {
+ IntelPTThreadTraceUP thread_trace_up(new IntelPTThreadTrace());
+
+ if (llvm::Error err = thread_trace_up->StartTrace(pid, tid, buffer_size))
+ return std::move(err);
+
+ return std::move(thread_trace_up);
+}
+
+Expected<std::vector<uint8_t>>
+IntelPTThreadTrace::GetIntelPTBuffer(size_t offset, size_t size) const {
+ std::vector<uint8_t> data(size, 0);
+ MutableArrayRef<uint8_t> buffer_ref(data);
+ Status error = ReadPerfTraceAux(buffer_ref, 0);
+ if (error.Fail())
+ return error.ToError();
+ return data;
+}
+
+Status
+IntelPTThreadTrace::ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset) const {
+#ifndef PERF_ATTR_SIZE_VER5
+ llvm_unreachable("perf event not supported");
+#else
+ // Disable the perf event to force a flush out of the CPU's internal buffer.
+ // Besides, we can guarantee that the CPU won't override any data as we are
+ // reading the buffer.
+ //
+ // The Intel documentation says:
+ //
+ // Packets are first buffered internally and then written out asynchronously.
+ // To collect packet output for postprocessing, a collector needs first to
+ // ensure that all packet data has been flushed from internal buffers.
+ // Software can ensure this by stopping packet generation by clearing
+ // IA32_RTIT_CTL.TraceEn (see “Disabling Packet Generation” in
+ // Section 35.2.7.2).
+ //
+ // This is achieved by the PERF_EVENT_IOC_DISABLE ioctl request, as mentioned
+ // in the man page of perf_event_open.
+ ioctl(*m_fd, PERF_EVENT_IOC_DISABLE);
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+ Status error;
+ uint64_t head = m_mmap_meta->aux_head;
+
+ LLDB_LOG(log, "Aux size -{0} , Head - {1}", m_mmap_meta->aux_size, head);
+
+ /**
+ * When configured as ring buffer, the aux buffer keeps wrapping around
+ * the buffer and its not possible to detect how many times the buffer
+ * wrapped. Initially the buffer is filled with zeros,as shown below
+ * so in order to get complete buffer we first copy firstpartsize, followed
+ * by any left over part from beginning to aux_head
+ *
+ * aux_offset [d,d,d,d,d,d,d,d,0,0,0,0,0,0,0,0,0,0,0] aux_size
+ * aux_head->||<- firstpartsize ->|
+ *
+ * */
+
+ ReadCyclicBuffer(buffer, GetAuxBuffer(), static_cast<size_t>(head), offset);
+ LLDB_LOG(log, "ReadCyclic BUffer Done");
+
+ // Reenable tracing now we have read the buffer
+ ioctl(*m_fd, PERF_EVENT_IOC_ENABLE);
+ return error;
+#endif
+}
+
+Status
+IntelPTThreadTrace::ReadPerfTraceData(llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset) const {
+#ifndef PERF_ATTR_SIZE_VER5
+ llvm_unreachable("perf event not supported");
+#else
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+ uint64_t bytes_remaining = buffer.size();
+ Status error;
+
+ uint64_t head = m_mmap_meta->data_head;
+
+ /*
+ * The data buffer and aux buffer have
diff erent implementations
+ * with respect to their definition of head pointer. In the case
+ * of Aux data buffer the head always wraps around the aux buffer
+ * and we don't need to care about it, whereas the data_head keeps
+ * increasing and needs to be wrapped by modulus operator
+ */
+
+ LLDB_LOG(log, "bytes_remaining - {0}", bytes_remaining);
+
+ auto data_buffer = GetDataBuffer();
+
+ if (head > data_buffer.size()) {
+ head = head % data_buffer.size();
+ LLDB_LOG(log, "Data size -{0} Head - {1}", m_mmap_meta->data_size, head);
+
+ ReadCyclicBuffer(buffer, data_buffer, static_cast<size_t>(head), offset);
+ bytes_remaining -= buffer.size();
+ } else {
+ LLDB_LOG(log, "Head - {0}", head);
+ if (offset >= head) {
+ LLDB_LOG(log, "Invalid Offset ");
+ error.SetErrorString("invalid offset");
+ buffer = buffer.slice(buffer.size());
+ return error;
+ }
+
+ auto data = data_buffer.slice(offset, (head - offset));
+ auto remaining = std::copy(data.begin(), data.end(), buffer.begin());
+ bytes_remaining -= (remaining - buffer.begin());
+ }
+ buffer = buffer.drop_back(bytes_remaining);
+ return error;
+#endif
+}
+
+void IntelPTThreadTrace::ReadCyclicBuffer(llvm::MutableArrayRef<uint8_t> &dst,
+ llvm::MutableArrayRef<uint8_t> src,
+ size_t src_cyc_index, size_t offset) {
+
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
+
+ if (dst.empty() || src.empty()) {
+ dst = dst.drop_back(dst.size());
+ return;
+ }
+
+ if (dst.data() == nullptr || src.data() == nullptr) {
+ dst = dst.drop_back(dst.size());
+ return;
+ }
+
+ if (src_cyc_index > src.size()) {
+ dst = dst.drop_back(dst.size());
+ return;
+ }
+
+ if (offset >= src.size()) {
+ LLDB_LOG(log, "Too Big offset ");
+ dst = dst.drop_back(dst.size());
+ return;
+ }
+
+ llvm::SmallVector<MutableArrayRef<uint8_t>, 2> parts = {
+ src.slice(src_cyc_index), src.take_front(src_cyc_index)};
+
+ if (offset > parts[0].size()) {
+ parts[1] = parts[1].slice(offset - parts[0].size());
+ parts[0] = parts[0].drop_back(parts[0].size());
+ } else if (offset == parts[0].size()) {
+ parts[0] = parts[0].drop_back(parts[0].size());
+ } else {
+ parts[0] = parts[0].slice(offset);
+ }
+ auto next = dst.begin();
+ auto bytes_left = dst.size();
+ for (auto part : parts) {
+ size_t chunk_size = std::min(part.size(), bytes_left);
+ next = std::copy_n(part.begin(), chunk_size, next);
+ bytes_left -= chunk_size;
+ }
+ dst = dst.drop_back(bytes_left);
+}
+
+TraceThreadState IntelPTThreadTrace::GetState() const {
+ return {static_cast<int64_t>(m_tid),
+ {TraceBinaryData{"threadTraceBuffer",
+ static_cast<int64_t>(GetTraceBufferSize())}}};
+}
+
+/// IntelPTThreadTraceCollection
+
+bool IntelPTThreadTraceCollection::TracesThread(lldb::tid_t tid) const {
+ return m_thread_traces.count(tid);
+}
+
+Error IntelPTThreadTraceCollection::TraceStop(lldb::tid_t tid) {
+ auto it = m_thread_traces.find(tid);
+ if (it == m_thread_traces.end())
+ return createStringError(inconvertibleErrorCode(),
+ "Thread %" PRIu64 " not currently traced", tid);
+ m_total_buffer_size -= it->second->GetTraceBufferSize();
+ m_thread_traces.erase(tid);
+ return Error::success();
+}
+
+Error IntelPTThreadTraceCollection::TraceStart(
+ lldb::tid_t tid, const TraceIntelPTStartRequest &request) {
+ if (TracesThread(tid))
+ return createStringError(inconvertibleErrorCode(),
+ "Thread %" PRIu64 " already traced", tid);
+
+ Expected<IntelPTThreadTraceUP> trace_up =
+ IntelPTThreadTrace::Create(m_pid, tid, request.threadBufferSize);
+ if (!trace_up)
+ return trace_up.takeError();
+
+ m_total_buffer_size += (*trace_up)->GetTraceBufferSize();
+ m_thread_traces.try_emplace(tid, std::move(*trace_up));
+ return Error::success();
+}
+
+size_t IntelPTThreadTraceCollection::GetTotalBufferSize() const {
+ return m_total_buffer_size;
+}
+
+std::vector<TraceThreadState>
+IntelPTThreadTraceCollection::GetThreadStates() const {
+ std::vector<TraceThreadState> states;
+ for (const auto &it : m_thread_traces)
+ states.push_back(it.second->GetState());
+ return states;
+}
+
+Expected<const IntelPTThreadTrace &>
+IntelPTThreadTraceCollection::GetTracedThread(lldb::tid_t tid) const {
+ auto it = m_thread_traces.find(tid);
+ if (it == m_thread_traces.end())
+ return createStringError(inconvertibleErrorCode(),
+ "Thread %" PRIu64 " not currently traced", tid);
+ return *it->second.get();
+}
+
+void IntelPTThreadTraceCollection::Clear() {
+ m_thread_traces.clear();
+ m_total_buffer_size = 0;
+}
+
+/// IntelPTProcessTrace
+
+bool IntelPTProcessTrace::TracesThread(lldb::tid_t tid) const {
+ return m_thread_traces.TracesThread(tid);
+}
+
+Error IntelPTProcessTrace::TraceStop(lldb::tid_t tid) {
+ return m_thread_traces.TraceStop(tid);
+}
+
+Error IntelPTProcessTrace::TraceStart(lldb::tid_t tid) {
+ if (m_thread_traces.GetTotalBufferSize() + m_tracing_params.threadBufferSize >
+ static_cast<size_t>(*m_tracing_params.processBufferSizeLimit))
+ return createStringError(
+ inconvertibleErrorCode(),
+ "Thread %" PRIu64 " can't be traced as the process trace size limit "
+ "has been reached. Consider retracing with a higher "
+ "limit.",
+ tid);
+
+ return m_thread_traces.TraceStart(tid, m_tracing_params);
+}
+
+const IntelPTThreadTraceCollection &
+IntelPTProcessTrace::GetThreadTraces() const {
+ return m_thread_traces;
+}
+
+/// IntelPTManager
+
+Error IntelPTManager::TraceStop(lldb::tid_t tid) {
+ if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid))
+ return m_process_trace->TraceStop(tid);
+ return m_thread_traces.TraceStop(tid);
+}
+
+Error IntelPTManager::TraceStop(const TraceStopRequest &request) {
+ if (request.IsProcessTracing()) {
+ if (!IsProcessTracingEnabled()) {
+ return createStringError(inconvertibleErrorCode(),
+ "Process not currently traced");
+ }
+ ClearProcessTracing();
+ return Error::success();
+ } else {
+ Error error = Error::success();
+ for (int64_t tid : *request.tids)
+ error = joinErrors(std::move(error),
+ TraceStop(static_cast<lldb::tid_t>(tid)));
+ return error;
+ }
+}
+
+Error IntelPTManager::TraceStart(
+ const TraceIntelPTStartRequest &request,
+ const std::vector<lldb::tid_t> &process_threads) {
+ if (request.IsProcessTracing()) {
+ if (IsProcessTracingEnabled()) {
+ return createStringError(
+ inconvertibleErrorCode(),
+ "Process currently traced. Stop process tracing first");
+ }
+ m_process_trace = IntelPTProcessTrace(m_pid, request);
+
+ Error error = Error::success();
+ for (lldb::tid_t tid : process_threads)
+ error = joinErrors(std::move(error), m_process_trace->TraceStart(tid));
+ return error;
+ } else {
+ Error error = Error::success();
+ for (int64_t tid : *request.tids)
+ error = joinErrors(std::move(error),
+ m_thread_traces.TraceStart(tid, request));
+ return error;
+ }
+}
+
+Error IntelPTManager::OnThreadCreated(lldb::tid_t tid) {
+ if (!IsProcessTracingEnabled())
+ return Error::success();
+ return m_process_trace->TraceStart(tid);
+}
+
+Error IntelPTManager::OnThreadDestroyed(lldb::tid_t tid) {
+ if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid))
+ return m_process_trace->TraceStop(tid);
+ else if (m_thread_traces.TracesThread(tid))
+ return m_thread_traces.TraceStop(tid);
+ return Error::success();
+}
+
+Expected<json::Value> IntelPTManager::GetState() const {
+ Expected<ArrayRef<uint8_t>> cpu_info = IntelPTThreadTrace::GetCPUInfo();
+ if (!cpu_info)
+ return cpu_info.takeError();
+
+ TraceGetStateResponse state;
+ state.processBinaryData.push_back(
+ {"cpuInfo", static_cast<int64_t>(cpu_info->size())});
+
+ std::vector<TraceThreadState> thread_states =
+ m_thread_traces.GetThreadStates();
+ state.tracedThreads.insert(state.tracedThreads.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());
+ }
+ return toJSON(state);
+}
+
+Expected<const IntelPTThreadTrace &>
+IntelPTManager::GetTracedThread(lldb::tid_t tid) const {
+ if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid))
+ return m_process_trace->GetThreadTraces().GetTracedThread(tid);
+ return m_thread_traces.GetTracedThread(tid);
+}
+
+Expected<std::vector<uint8_t>>
+IntelPTManager::GetBinaryData(const TraceGetBinaryDataRequest &request) const {
+ if (request.kind == "threadTraceBuffer") {
+ if (Expected<const IntelPTThreadTrace &> trace =
+ GetTracedThread(*request.tid))
+ return trace->GetIntelPTBuffer(request.offset, request.size);
+ else
+ return trace.takeError();
+ } else if (request.kind == "cpuInfo") {
+ return IntelPTThreadTrace::GetCPUInfo();
+ }
+ return createStringError(inconvertibleErrorCode(),
+ "Unsuported trace binary data kind: %s",
+ request.kind.c_str());
+}
+
+void IntelPTManager::ClearProcessTracing() { m_process_trace = None; }
+
+bool IntelPTManager::IsSupported() { return (bool)GetOSEventType(); }
+
+bool IntelPTManager::IsProcessTracingEnabled() const {
+ return (bool)m_process_trace;
+}
+
+void IntelPTManager::Clear() {
+ ClearProcessTracing();
+ m_thread_traces.Clear();
+}
diff --git a/lldb/source/Plugins/Process/Linux/IntelPTManager.h b/lldb/source/Plugins/Process/Linux/IntelPTManager.h
new file mode 100644
index 0000000000000..807798a811db6
--- /dev/null
+++ b/lldb/source/Plugins/Process/Linux/IntelPTManager.h
@@ -0,0 +1,254 @@
+//===-- IntelPTManager.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_IntelPTManager_H_
+#define liblldb_IntelPTManager_H_
+
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
+#include "lldb/lldb-types.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+
+#include <linux/perf_event.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+namespace lldb_private {
+
+namespace process_linux {
+
+/// This class keeps track of one tracing instance of
+/// Intel(R) Processor Trace on Linux OS at thread level.
+///
+/// The kernel interface for us is the perf_event_open.
+class IntelPTThreadTrace;
+typedef std::unique_ptr<IntelPTThreadTrace> IntelPTThreadTraceUP;
+
+class IntelPTThreadTrace {
+
+ class munmap_delete {
+ size_t m_length;
+
+ public:
+ munmap_delete(size_t length) : m_length(length) {}
+ void operator()(void *ptr) {
+ if (m_length)
+ munmap(ptr, m_length);
+ }
+ };
+
+ class file_close {
+
+ public:
+ file_close() = default;
+ void operator()(int *ptr) {
+ if (ptr == nullptr)
+ return;
+ if (*ptr == -1)
+ return;
+ close(*ptr);
+ std::default_delete<int>()(ptr);
+ }
+ };
+
+ std::unique_ptr<perf_event_mmap_page, munmap_delete> m_mmap_meta;
+ std::unique_ptr<uint8_t, munmap_delete> m_mmap_aux;
+ std::unique_ptr<int, file_close> m_fd;
+ lldb::tid_t m_tid;
+
+ /// Start tracing a thread
+ ///
+ /// \param[in] pid
+ /// The pid of the process whose thread will be traced.
+ ///
+ /// \param[in] buffer_size
+ /// Size of the thread buffer in bytes.
+ ///
+ /// \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,
+ uint64_t buffer_size);
+
+ llvm::MutableArrayRef<uint8_t> GetAuxBuffer() const;
+ llvm::MutableArrayRef<uint8_t> GetDataBuffer() const;
+
+ IntelPTThreadTrace()
+ : m_mmap_meta(nullptr, munmap_delete(0)),
+ m_mmap_aux(nullptr, munmap_delete(0)), m_fd(nullptr, file_close()) {}
+
+public:
+ /// Get the content of /proc/cpuinfo that can be later used to decode traces.
+ static llvm::Expected<llvm::ArrayRef<uint8_t>> GetCPUInfo();
+
+ /// Start tracing a thread.
+ ///
+ /// See \a StartTrace.
+ ///
+ /// \return
+ /// A \a IntelPTThreadTrace instance if tracing was successful, or
+ /// an \a llvm::Error otherwise.
+ static llvm::Expected<IntelPTThreadTraceUP>
+ Create(lldb::pid_t pid, lldb::tid_t tid, size_t buffer_size);
+
+ /// Read the trace buffer of the currently traced thread.
+ ///
+ /// \param[in] offset
+ /// Offset of the data to read.
+ ///
+ /// \param[in] size
+ /// Number of bytes to read.
+ ///
+ /// \return
+ /// A vector with the requested binary data. The vector will have the
+ /// size of the requested \a size. Non-available positions will be
+ /// filled with zeroes.
+ llvm::Expected<std::vector<uint8_t>> GetIntelPTBuffer(size_t offset,
+ size_t size) const;
+
+ Status ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset = 0) const;
+
+ Status ReadPerfTraceData(llvm::MutableArrayRef<uint8_t> &buffer,
+ size_t offset = 0) const;
+
+ /// Get the size in bytes of the aux section of the thread or process traced
+ /// by this object.
+ size_t GetTraceBufferSize() const;
+
+ /// Read data from a cyclic buffer
+ ///
+ /// \param[in] [out] buf
+ /// Destination buffer, the buffer will be truncated to written size.
+ ///
+ /// \param[in] src
+ /// Source buffer which must be a cyclic buffer.
+ ///
+ /// \param[in] src_cyc_index
+ /// The index pointer (start of the valid data in the cyclic
+ /// buffer).
+ ///
+ /// \param[in] offset
+ /// The offset to begin reading the data in the cyclic buffer.
+ static void ReadCyclicBuffer(llvm::MutableArrayRef<uint8_t> &dst,
+ llvm::MutableArrayRef<uint8_t> src,
+ size_t src_cyc_index, size_t offset);
+
+ /// Return the thread-specific part of the jLLDBTraceGetState packet.
+ TraceThreadState GetState() const;
+};
+
+/// Manages a list of thread traces.
+class IntelPTThreadTraceCollection {
+public:
+ IntelPTThreadTraceCollection(lldb::pid_t pid) : m_pid(pid) {}
+
+ /// Dispose of all traces
+ void Clear();
+
+ bool TracesThread(lldb::tid_t tid) const;
+
+ size_t GetTotalBufferSize() const;
+
+ std::vector<TraceThreadState> GetThreadStates() const;
+
+ llvm::Expected<const IntelPTThreadTrace &>
+ GetTracedThread(lldb::tid_t tid) const;
+
+ llvm::Error TraceStart(lldb::tid_t tid,
+ const TraceIntelPTStartRequest &request);
+
+ llvm::Error TraceStop(lldb::tid_t tid);
+
+private:
+ lldb::pid_t m_pid;
+ llvm::DenseMap<lldb::tid_t, IntelPTThreadTraceUP> m_thread_traces;
+ /// Total actual thread buffer size in bytes
+ size_t m_total_buffer_size = 0;
+};
+
+/// Manages a "process trace" instance.
+class IntelPTProcessTrace {
+public:
+ IntelPTProcessTrace(lldb::pid_t pid, const TraceIntelPTStartRequest &request)
+ : m_thread_traces(pid), m_tracing_params(request) {}
+
+ bool TracesThread(lldb::tid_t tid) const;
+
+ const IntelPTThreadTraceCollection &GetThreadTraces() const;
+
+ llvm::Error TraceStart(lldb::tid_t tid);
+
+ llvm::Error TraceStop(lldb::tid_t tid);
+
+private:
+ IntelPTThreadTraceCollection m_thread_traces;
+ /// Params used to trace threads when the user started "process tracing".
+ TraceIntelPTStartRequest m_tracing_params;
+};
+
+/// Main class that manages intel-pt process and thread tracing.
+class IntelPTManager {
+public:
+ IntelPTManager(lldb::pid_t pid) : m_pid(pid), m_thread_traces(pid) {}
+
+ static bool IsSupported();
+
+ /// If "process tracing" is enabled, then trace the given thread.
+ llvm::Error OnThreadCreated(lldb::tid_t tid);
+
+ /// Stops tracing a tracing upon a destroy event.
+ llvm::Error OnThreadDestroyed(lldb::tid_t tid);
+
+ /// Implementation of the jLLDBTraceStop packet
+ 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);
+
+ /// Implementation of the jLLDBTraceGetState packet
+ llvm::Expected<llvm::json::Value> GetState() const;
+
+ /// Implementation of the jLLDBTraceGetBinaryData packet
+ llvm::Expected<std::vector<uint8_t>>
+ GetBinaryData(const TraceGetBinaryDataRequest &request) const;
+
+ /// Dispose of all traces
+ void Clear();
+
+private:
+ llvm::Error TraceStop(lldb::tid_t tid);
+
+ /// Start tracing a specific thread.
+ llvm::Error TraceStart(lldb::tid_t tid,
+ const TraceIntelPTStartRequest &request);
+
+ llvm::Expected<const IntelPTThreadTrace &>
+ GetTracedThread(lldb::tid_t tid) const;
+
+ bool IsProcessTracingEnabled() const;
+
+ void ClearProcessTracing();
+
+ lldb::pid_t m_pid;
+ /// 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;
+};
+
+} // namespace process_linux
+} // namespace lldb_private
+
+#endif // liblldb_IntelPTManager_H_
diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
index aed0d32f23640..5a876ce5befec 100644
--- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -286,7 +286,8 @@ NativeProcessLinux::NativeProcessLinux(::pid_t pid, int terminal_fd,
NativeDelegate &delegate,
const ArchSpec &arch, MainLoop &mainloop,
llvm::ArrayRef<::pid_t> tids)
- : NativeProcessELF(pid, terminal_fd, delegate), m_arch(arch) {
+ : NativeProcessELF(pid, terminal_fd, delegate), m_arch(arch),
+ m_intel_pt_manager(pid) {
if (m_terminal_fd != -1) {
Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK);
assert(status.Success());
@@ -298,8 +299,7 @@ NativeProcessLinux::NativeProcessLinux(::pid_t pid, int terminal_fd,
assert(m_sigchld_handle && status.Success());
for (const auto &tid : tids) {
- NativeThreadLinux &thread = AddThread(tid);
- thread.SetStoppedBySignal(SIGSTOP);
+ NativeThreadLinux &thread = AddThread(tid, /*resume*/ false);
ThreadWasCreated(thread);
}
@@ -444,10 +444,7 @@ void NativeProcessLinux::MonitorCallback(lldb::pid_t pid, bool exited,
LLDB_LOG(log, "tid {0}, si_code: {1}, si_pid: {2}", pid, info.si_code,
info.si_pid);
- NativeThreadLinux &thread = AddThread(pid);
-
- // Resume the newly created thread.
- ResumeThread(thread, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER);
+ NativeThreadLinux &thread = AddThread(pid, /*resume*/ true);
ThreadWasCreated(thread);
return;
}
@@ -550,9 +547,8 @@ void NativeProcessLinux::WaitForNewThread(::pid_t tid) {
}
LLDB_LOG(log, "pid = {0}: tracking new thread tid {1}", GetID(), tid);
- NativeThreadLinux &new_thread = AddThread(tid);
+ NativeThreadLinux &new_thread = AddThread(tid, /*resume*/ true);
- ResumeThread(new_thread, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER);
ThreadWasCreated(new_thread);
}
@@ -954,8 +950,7 @@ Status NativeProcessLinux::Detach() {
e; // Save the error, but still attempt to detach from other threads.
}
- m_processor_trace_monitor.clear();
- m_pt_proces_trace_id = LLDB_INVALID_UID;
+ m_intel_pt_manager.Clear();
return error;
}
@@ -1511,12 +1506,33 @@ bool NativeProcessLinux::StopTrackingThread(lldb::tid_t thread_id) {
}
if (found)
- StopTracingForThread(thread_id);
+ NotifyTracersOfThreadDestroyed(thread_id);
+
SignalIfAllThreadsStopped();
return found;
}
-NativeThreadLinux &NativeProcessLinux::AddThread(lldb::tid_t thread_id) {
+Status NativeProcessLinux::NotifyTracersOfNewThread(lldb::tid_t tid) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+ Status error(m_intel_pt_manager.OnThreadCreated(tid));
+ if (error.Fail())
+ LLDB_LOG(log, "Failed to trace a new thread with intel-pt, tid = {0}. {1}",
+ tid, error.AsCString());
+ return error;
+}
+
+Status NativeProcessLinux::NotifyTracersOfThreadDestroyed(lldb::tid_t tid) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
+ Status error(m_intel_pt_manager.OnThreadDestroyed(tid));
+ if (error.Fail())
+ LLDB_LOG(log,
+ "Failed to stop a destroyed thread with intel-pt, tid = {0}. {1}",
+ tid, error.AsCString());
+ return error;
+}
+
+NativeThreadLinux &NativeProcessLinux::AddThread(lldb::tid_t thread_id,
+ bool resume) {
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
LLDB_LOG(log, "pid {0} adding thread with tid {1}", GetID(), thread_id);
@@ -1528,22 +1544,19 @@ NativeThreadLinux &NativeProcessLinux::AddThread(lldb::tid_t thread_id) {
SetCurrentThreadID(thread_id);
m_threads.push_back(std::make_unique<NativeThreadLinux>(*this, thread_id));
+ NativeThreadLinux &thread =
+ static_cast<NativeThreadLinux &>(*m_threads.back());
+
+ Status tracing_error = NotifyTracersOfNewThread(thread.GetID());
+ if (tracing_error.Fail()) {
+ thread.SetStoppedByProcessorTrace(tracing_error.AsCString());
+ StopRunningThreads(thread.GetID());
+ } else if (resume)
+ ResumeThread(thread, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER);
+ else
+ thread.SetStoppedBySignal(SIGSTOP);
- if (m_pt_proces_trace_id != LLDB_INVALID_UID) {
- auto traceMonitor = ProcessorTraceMonitor::Create(
- GetID(), thread_id, m_pt_process_trace_config, true);
- if (traceMonitor) {
- m_pt_traced_thread_group.insert(thread_id);
- m_processor_trace_monitor.insert(
- std::make_pair(thread_id, std::move(*traceMonitor)));
- } else {
- LLDB_LOG(log, "failed to start trace on thread {0}", thread_id);
- Status error(traceMonitor.takeError());
- LLDB_LOG(log, "error {0}", error);
- }
- }
-
- return static_cast<NativeThreadLinux &>(*m_threads.back());
+ return thread;
}
Status NativeProcessLinux::GetLoadedModuleFileSpec(const char *module_path,
@@ -1761,263 +1774,43 @@ Status NativeProcessLinux::PtraceWrapper(int req, lldb::pid_t pid, void *addr,
return error;
}
-llvm::Expected<ProcessorTraceMonitor &>
-NativeProcessLinux::LookupProcessorTraceInstance(lldb::user_id_t traceid,
- lldb::tid_t thread) {
- Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
- if (thread == LLDB_INVALID_THREAD_ID && traceid == m_pt_proces_trace_id) {
- LLDB_LOG(log, "thread not specified: {0}", traceid);
- return Status("tracing not active thread not specified").ToError();
- }
-
- for (auto& iter : m_processor_trace_monitor) {
- if (traceid == iter.second->GetTraceID() &&
- (thread == iter.first || thread == LLDB_INVALID_THREAD_ID))
- return *(iter.second);
- }
-
- LLDB_LOG(log, "traceid not being traced: {0}", traceid);
- return Status("tracing not active for this thread").ToError();
+llvm::Expected<TraceSupportedResponse> NativeProcessLinux::TraceSupported() {
+ if (IntelPTManager::IsSupported())
+ return TraceSupportedResponse{"intel-pt", "Intel Processor Trace"};
+ return NativeProcessProtocol::TraceSupported();
}
-Status NativeProcessLinux::GetMetaData(lldb::user_id_t traceid,
- lldb::tid_t thread,
- llvm::MutableArrayRef<uint8_t> &buffer,
- size_t offset) {
- TraceOptions trace_options;
- Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
- Status error;
-
- LLDB_LOG(log, "traceid {0}", traceid);
-
- auto perf_monitor = LookupProcessorTraceInstance(traceid, thread);
- if (!perf_monitor) {
- LLDB_LOG(log, "traceid not being traced: {0}", traceid);
- buffer = buffer.slice(buffer.size());
- error = perf_monitor.takeError();
- return error;
- }
- return (*perf_monitor).ReadPerfTraceData(buffer, offset);
-}
-
-Status NativeProcessLinux::GetData(lldb::user_id_t traceid, lldb::tid_t thread,
- llvm::MutableArrayRef<uint8_t> &buffer,
- size_t offset) {
- Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
- Status error;
-
- LLDB_LOG(log, "traceid {0}", traceid);
-
- auto perf_monitor = LookupProcessorTraceInstance(traceid, thread);
- if (!perf_monitor) {
- LLDB_LOG(log, "traceid not being traced: {0}", traceid);
- buffer = buffer.slice(buffer.size());
- error = perf_monitor.takeError();
- return error;
- }
- return (*perf_monitor).ReadPerfTraceAux(buffer, offset);
-}
-
-Status NativeProcessLinux::GetTraceConfig(lldb::user_id_t traceid,
- TraceOptions &config) {
- Status error;
- if (config.getThreadID() == LLDB_INVALID_THREAD_ID &&
- m_pt_proces_trace_id == traceid) {
- if (m_pt_proces_trace_id == LLDB_INVALID_UID) {
- error.SetErrorString("tracing not active for this process");
- return error;
- }
- config = m_pt_process_trace_config;
- } else {
- auto perf_monitor =
- LookupProcessorTraceInstance(traceid, config.getThreadID());
- if (!perf_monitor) {
- error = perf_monitor.takeError();
- return error;
- }
- error = (*perf_monitor).GetTraceConfig(config);
- }
- return error;
-}
-
-llvm::Expected<TraceTypeInfo> NativeProcessLinux::GetSupportedTraceType() {
- if (ProcessorTraceMonitor::IsSupported())
- return TraceTypeInfo{"intel-pt", "Intel Processor Trace"};
- return NativeProcessProtocol::GetSupportedTraceType();
-}
-
-lldb::user_id_t
-NativeProcessLinux::StartTraceGroup(const TraceOptions &config,
- Status &error) {
-
- Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
- if (config.getType() != TraceType::eTraceTypeProcessorTrace)
- return LLDB_INVALID_UID;
-
- if (m_pt_proces_trace_id != LLDB_INVALID_UID) {
- error.SetErrorString("tracing already active on this process");
- return m_pt_proces_trace_id;
- }
-
- for (const auto &thread_sp : m_threads) {
- if (auto traceInstance = ProcessorTraceMonitor::Create(
- GetID(), thread_sp->GetID(), config, true)) {
- m_pt_traced_thread_group.insert(thread_sp->GetID());
- m_processor_trace_monitor.insert(
- std::make_pair(thread_sp->GetID(), std::move(*traceInstance)));
- }
- }
-
- m_pt_process_trace_config = config;
- error = ProcessorTraceMonitor::GetCPUType(m_pt_process_trace_config);
-
- // Trace on Complete process will have traceid of 0
- m_pt_proces_trace_id = 0;
-
- LLDB_LOG(log, "Process Trace ID {0}", m_pt_proces_trace_id);
- return m_pt_proces_trace_id;
-}
-
-lldb::user_id_t NativeProcessLinux::StartTrace(const TraceOptions &config,
- Status &error) {
- if (config.getType() != TraceType::eTraceTypeProcessorTrace)
- return NativeProcessProtocol::StartTrace(config, error);
-
- Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
-
- lldb::tid_t threadid = config.getThreadID();
-
- if (threadid == LLDB_INVALID_THREAD_ID)
- return StartTraceGroup(config, error);
-
- auto thread_sp = GetThreadByID(threadid);
- if (!thread_sp) {
- // Thread not tracked by lldb so don't trace.
- error.SetErrorString("invalid thread id");
- return LLDB_INVALID_UID;
- }
-
- const auto &iter = m_processor_trace_monitor.find(threadid);
- if (iter != m_processor_trace_monitor.end()) {
- LLDB_LOG(log, "Thread already being traced");
- error.SetErrorString("tracing already active on this thread");
- return LLDB_INVALID_UID;
- }
-
- auto traceMonitor =
- ProcessorTraceMonitor::Create(GetID(), threadid, config, false);
- if (!traceMonitor) {
- error = traceMonitor.takeError();
- LLDB_LOG(log, "error {0}", error);
- return LLDB_INVALID_UID;
- }
- lldb::user_id_t ret_trace_id = (*traceMonitor)->GetTraceID();
- m_processor_trace_monitor.insert(
- std::make_pair(threadid, std::move(*traceMonitor)));
- return ret_trace_id;
-}
-
-Status NativeProcessLinux::StopTracingForThread(lldb::tid_t thread) {
- Status error;
- Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
- LLDB_LOG(log, "Thread {0}", thread);
-
- const auto& iter = m_processor_trace_monitor.find(thread);
- if (iter == m_processor_trace_monitor.end()) {
- error.SetErrorString("tracing not active for this thread");
- return error;
- }
-
- if (iter->second->GetTraceID() == m_pt_proces_trace_id) {
- // traceid maps to the whole process so we have to erase it from the thread
- // group.
- LLDB_LOG(log, "traceid maps to process");
- m_pt_traced_thread_group.erase(thread);
+Error NativeProcessLinux::TraceStart(StringRef json_request, StringRef type) {
+ if (type == "intel-pt") {
+ 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_manager.TraceStart(*request, process_threads);
+ } else
+ return request.takeError();
}
- m_processor_trace_monitor.erase(iter);
- return error;
+ return NativeProcessProtocol::TraceStart(json_request, type);
}
-Status NativeProcessLinux::StopTrace(lldb::user_id_t traceid,
- lldb::tid_t thread) {
- Status error;
-
- TraceOptions trace_options;
- trace_options.setThreadID(thread);
- error = NativeProcessLinux::GetTraceConfig(traceid, trace_options);
-
- if (error.Fail())
- return error;
-
- switch (trace_options.getType()) {
- case lldb::TraceType::eTraceTypeProcessorTrace:
- if (traceid == m_pt_proces_trace_id &&
- thread == LLDB_INVALID_THREAD_ID)
- StopProcessorTracingOnProcess();
- else
- error = StopProcessorTracingOnThread(traceid, thread);
- break;
- default:
- error.SetErrorString("trace not supported");
- break;
- }
-
- return error;
+Error NativeProcessLinux::TraceStop(const TraceStopRequest &request) {
+ if (request.type == "intel-pt")
+ return m_intel_pt_manager.TraceStop(request);
+ return NativeProcessProtocol::TraceStop(request);
}
-void NativeProcessLinux::StopProcessorTracingOnProcess() {
- for (auto thread_id_iter : m_pt_traced_thread_group)
- m_processor_trace_monitor.erase(thread_id_iter);
- m_pt_traced_thread_group.clear();
- m_pt_proces_trace_id = LLDB_INVALID_UID;
+Expected<json::Value> NativeProcessLinux::TraceGetState(StringRef type) {
+ if (type == "intel-pt")
+ return m_intel_pt_manager.GetState();
+ return NativeProcessProtocol::TraceGetState(type);
}
-Status NativeProcessLinux::StopProcessorTracingOnThread(lldb::user_id_t traceid,
- lldb::tid_t thread) {
- Status error;
- Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
-
- if (thread == LLDB_INVALID_THREAD_ID) {
- for (auto& iter : m_processor_trace_monitor) {
- if (iter.second->GetTraceID() == traceid) {
- // Stopping a trace instance for an individual thread hence there will
- // only be one traceid that can match.
- m_processor_trace_monitor.erase(iter.first);
- return error;
- }
- LLDB_LOG(log, "Trace ID {0}", iter.second->GetTraceID());
- }
-
- LLDB_LOG(log, "Invalid TraceID");
- error.SetErrorString("invalid trace id");
- return error;
- }
-
- // thread is specified so we can use find function on the map.
- const auto& iter = m_processor_trace_monitor.find(thread);
- if (iter == m_processor_trace_monitor.end()) {
- // thread not found in our map.
- LLDB_LOG(log, "thread not being traced");
- error.SetErrorString("tracing not active for this thread");
- return error;
- }
- if (iter->second->GetTraceID() != traceid) {
- // traceid did not match so it has to be invalid.
- LLDB_LOG(log, "Invalid TraceID");
- error.SetErrorString("invalid trace id");
- return error;
- }
-
- LLDB_LOG(log, "UID - {0} , Thread -{1}", traceid, thread);
-
- if (traceid == m_pt_proces_trace_id) {
- // traceid maps to the whole process so we have to erase it from the thread
- // group.
- LLDB_LOG(log, "traceid maps to process");
- m_pt_traced_thread_group.erase(thread);
- }
- m_processor_trace_monitor.erase(iter);
-
- return error;
+Expected<std::vector<uint8_t>> NativeProcessLinux::TraceGetBinaryData(
+ const TraceGetBinaryDataRequest &request) {
+ if (request.type == "intel-pt")
+ return m_intel_pt_manager.GetBinaryData(request);
+ return NativeProcessProtocol::TraceGetBinaryData(request);
}
diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
index 077c62d362342..77905a193c2b7 100644
--- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
+++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
@@ -20,10 +20,10 @@
#include "lldb/Utility/FileSpec.h"
#include "lldb/lldb-types.h"
+#include "IntelPTManager.h"
#include "NativeThreadLinux.h"
#include "Plugins/Process/POSIX/NativeProcessELF.h"
#include "Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h"
-#include "ProcessorTrace.h"
namespace lldb_private {
class Status;
@@ -103,23 +103,22 @@ class NativeProcessLinux : public NativeProcessELF,
return getProcFile(GetID(), "auxv");
}
- lldb::user_id_t StartTrace(const TraceOptions &config,
- Status &error) override;
+ /// Tracing
+ /// These methods implement the jLLDBTrace packets
+ /// \{
+ llvm::Error TraceStart(llvm::StringRef json_request,
+ llvm::StringRef type) override;
- Status StopTrace(lldb::user_id_t traceid,
- lldb::tid_t thread) override;
+ llvm::Error TraceStop(const TraceStopRequest &request) override;
- Status GetData(lldb::user_id_t traceid, lldb::tid_t thread,
- llvm::MutableArrayRef<uint8_t> &buffer,
- size_t offset = 0) override;
+ llvm::Expected<llvm::json::Value>
+ TraceGetState(llvm::StringRef type) override;
- Status GetMetaData(lldb::user_id_t traceid, lldb::tid_t thread,
- llvm::MutableArrayRef<uint8_t> &buffer,
- size_t offset = 0) override;
+ llvm::Expected<std::vector<uint8_t>>
+ TraceGetBinaryData(const TraceGetBinaryDataRequest &request) override;
- Status GetTraceConfig(lldb::user_id_t traceid, TraceOptions &config) override;
-
- virtual llvm::Expected<TraceTypeInfo> GetSupportedTraceType() override;
+ llvm::Expected<TraceSupportedResponse> TraceSupported() override;
+ /// }
// Interface used by NativeRegisterContext-derived classes.
static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr = nullptr,
@@ -175,7 +174,28 @@ class NativeProcessLinux : public NativeProcessELF,
bool StopTrackingThread(lldb::tid_t thread_id);
- NativeThreadLinux &AddThread(lldb::tid_t thread_id);
+ /// Create a new thread.
+ ///
+ /// If process tracing is enabled and the thread can't be traced, then the
+ /// thread is left stopped with a \a eStopReasonProcessorTrace status, and
+ /// then the process is stopped.
+ ///
+ /// \param[in] resume
+ /// If a tracing error didn't happen, then resume the thread after
+ /// creation if \b true, or leave it stopped with SIGSTOP if \b false.
+ NativeThreadLinux &AddThread(lldb::tid_t thread_id, bool resume);
+
+ /// Start tracing a new thread if process tracing is enabled.
+ ///
+ /// Trace mechanisms should modify this method to provide automatic tracing
+ /// for new threads.
+ Status NotifyTracersOfNewThread(lldb::tid_t tid);
+
+ /// Stop tracing threads upon a destroy event.
+ ///
+ /// Trace mechanisms should modify this method to provide automatic trace
+ /// stopping for threads being destroyed.
+ Status NotifyTracersOfThreadDestroyed(lldb::tid_t tid);
/// Writes a siginfo_t structure corresponding to the given thread ID to the
/// memory region pointed to by \p siginfo.
@@ -212,42 +232,8 @@ class NativeProcessLinux : public NativeProcessELF,
Status PopulateMemoryRegionCache();
- lldb::user_id_t StartTraceGroup(const TraceOptions &config,
- Status &error);
-
- // This function is intended to be used to stop tracing
- // on a thread that exited.
- Status StopTracingForThread(lldb::tid_t thread);
-
- // The below function as the name suggests, looks up a ProcessorTrace
- // instance from the m_processor_trace_monitor map. In the case of
- // process tracing where the traceid passed would map to the complete
- // process, it is mandatory to provide a threadid to obtain a trace
- // instance (since ProcessorTrace is tied to a thread). In the other
- // scenario that an individual thread is being traced, just the traceid
- // is sufficient to obtain the actual ProcessorTrace instance.
- llvm::Expected<ProcessorTraceMonitor &>
- LookupProcessorTraceInstance(lldb::user_id_t traceid, lldb::tid_t thread);
-
- // Stops tracing on individual threads being traced. Not intended
- // to be used to stop tracing on complete process.
- Status StopProcessorTracingOnThread(lldb::user_id_t traceid,
- lldb::tid_t thread);
-
- // Intended to stop tracing on complete process.
- // Should not be used for stopping trace on
- // individual threads.
- void StopProcessorTracingOnProcess();
-
- llvm::DenseMap<lldb::tid_t, ProcessorTraceMonitorUP>
- m_processor_trace_monitor;
-
- // Set for tracking threads being traced under
- // same process user id.
- llvm::DenseSet<lldb::tid_t> m_pt_traced_thread_group;
-
- lldb::user_id_t m_pt_proces_trace_id = LLDB_INVALID_UID;
- TraceOptions m_pt_process_trace_config;
+ /// Manages Intel PT process and thread traces.
+ IntelPTManager m_intel_pt_manager;
};
} // namespace process_linux
diff --git a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
index 5aec98bdeca6a..fcc96e274d224 100644
--- a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
@@ -77,6 +77,9 @@ void LogThreadStopInfo(Log &log, const ThreadStopInfo &stop_info,
case eStopReasonInstrumentation:
log.Printf("%s: %s instrumentation", __FUNCTION__, header);
return;
+ case eStopReasonProcessorTrace:
+ log.Printf("%s: %s processor trace", __FUNCTION__, header);
+ return;
default:
log.Printf("%s: %s invalid stop reason %" PRIu32, __FUNCTION__, header,
static_cast<uint32_t>(stop_info.reason));
@@ -398,6 +401,15 @@ void NativeThreadLinux::SetStoppedWithNoReason() {
m_stop_info.details.signal.signo = 0;
}
+void NativeThreadLinux::SetStoppedByProcessorTrace(
+ llvm::StringRef description) {
+ SetStopped();
+
+ m_stop_info.reason = StopReason::eStopReasonProcessorTrace;
+ m_stop_info.details.signal.signo = 0;
+ m_stop_description = description.str();
+}
+
void NativeThreadLinux::SetExited() {
const StateType new_state = StateType::eStateExited;
MaybeLogStateChange(new_state);
diff --git a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
index 5cf473e89052a..fe42270abed5c 100644
--- a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
+++ b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
@@ -14,6 +14,8 @@
#include "lldb/Host/common/NativeThreadProtocol.h"
#include "lldb/lldb-private-forward.h"
+#include "llvm/ADT/StringRef.h"
+
#include <csignal>
#include <map>
#include <memory>
@@ -85,6 +87,8 @@ class NativeThreadLinux : public NativeThreadProtocol {
void SetStoppedWithNoReason();
+ void SetStoppedByProcessorTrace(llvm::StringRef description);
+
void SetExited();
Status RequestStop();
diff --git a/lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp b/lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp
deleted file mode 100644
index 1a8aa36b3edd9..0000000000000
--- a/lldb/source/Plugins/Process/Linux/ProcessorTrace.cpp
+++ /dev/null
@@ -1,428 +0,0 @@
-//===-- ProcessorTrace.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 <algorithm>
-#include <fstream>
-
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/MathExtras.h"
-
-#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
-#include "ProcessorTrace.h"
-#include "lldb/Host/linux/Support.h"
-
-#include <sys/ioctl.h>
-#include <sys/syscall.h>
-
-using namespace lldb;
-using namespace lldb_private;
-using namespace process_linux;
-using namespace llvm;
-
-lldb::user_id_t ProcessorTraceMonitor::m_trace_num = 1;
-const char *kOSEventIntelPTTypeFile =
- "/sys/bus/event_source/devices/intel_pt/type";
-
-Status ProcessorTraceMonitor::GetTraceConfig(TraceOptions &config) const {
-#ifndef PERF_ATTR_SIZE_VER5
- llvm_unreachable("perf event not supported");
-#else
- Status error;
-
- config.setType(lldb::TraceType::eTraceTypeProcessorTrace);
- config.setMetaDataBufferSize(m_mmap_meta->data_size);
-
- config.setTraceBufferSize(m_mmap_meta->aux_size);
-
- error = GetCPUType(config);
-
- return error;
-#endif
-}
-
-Expected<uint32_t> ProcessorTraceMonitor::GetOSEventType() {
- auto intel_pt_type_text =
- llvm::MemoryBuffer::getFileAsStream(kOSEventIntelPTTypeFile);
-
- if (!intel_pt_type_text)
- return createStringError(inconvertibleErrorCode(),
- "Can't open the file '%s'",
- kOSEventIntelPTTypeFile);
-
- uint32_t intel_pt_type = 0;
- StringRef buffer = intel_pt_type_text.get()->getBuffer();
- if (buffer.trim().getAsInteger(10, intel_pt_type))
- return createStringError(
- inconvertibleErrorCode(),
- "The file '%s' has a invalid value. It should be an unsigned int.",
- kOSEventIntelPTTypeFile);
- return intel_pt_type;
-}
-
-bool ProcessorTraceMonitor::IsSupported() { return (bool)GetOSEventType(); }
-
-Status ProcessorTraceMonitor::StartTrace(lldb::pid_t pid, lldb::tid_t tid,
- const TraceOptions &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 numpages = static_cast<uint64_t>(
- llvm::PowerOf2Floor((bufsize + 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;
-
- perf_event_attr attr;
- memset(&attr, 0, sizeof(attr));
- attr.size = sizeof(attr);
- attr.exclude_kernel = 1;
- attr.sample_type = PERF_SAMPLE_TIME;
- attr.sample_id_all = 1;
- attr.exclude_hv = 1;
- attr.exclude_idle = 1;
- attr.mmap = 1;
-
- Expected<uint32_t> intel_pt_type = GetOSEventType();
-
- if (!intel_pt_type) {
- error = intel_pt_type.takeError();
- return error;
- }
-
- 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;
- }
-
- 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;
- }
-
- 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);
-
- if (base == MAP_FAILED) {
- LLDB_LOG(log, "mmap base error {0}", errno);
- error.SetErrorString("Meta buffer allocation failed");
- return error;
- }
-
- m_mmap_meta = std::unique_ptr<perf_event_mmap_page, munmap_delete>(
- reinterpret_cast<perf_event_mmap_page *>(base),
- munmap_delete(metabufsize + page_size));
-
- m_mmap_meta->aux_offset = m_mmap_meta->data_offset + m_mmap_meta->data_size;
- m_mmap_meta->aux_size = bufsize;
-
- errno = 0;
- auto mmap_aux = mmap(nullptr, bufsize, 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;
- }
- m_mmap_aux = std::unique_ptr<uint8_t, munmap_delete>(
- reinterpret_cast<uint8_t *>(mmap_aux), munmap_delete(bufsize));
- return error;
-#endif
-}
-
-llvm::MutableArrayRef<uint8_t> ProcessorTraceMonitor::GetDataBuffer() {
-#ifndef PERF_ATTR_SIZE_VER5
- llvm_unreachable("perf event not supported");
-#else
- return MutableArrayRef<uint8_t>(
- (reinterpret_cast<uint8_t *>(m_mmap_meta.get()) +
- m_mmap_meta->data_offset),
- m_mmap_meta->data_size);
-#endif
-}
-
-llvm::MutableArrayRef<uint8_t> ProcessorTraceMonitor::GetAuxBuffer() {
-#ifndef PERF_ATTR_SIZE_VER5
- llvm_unreachable("perf event not supported");
-#else
- return MutableArrayRef<uint8_t>(m_mmap_aux.get(), m_mmap_meta->aux_size);
-#endif
-}
-
-Status ProcessorTraceMonitor::GetCPUType(TraceOptions &config) {
-
- Status error;
- uint64_t cpu_family = -1;
- uint64_t model = -1;
- uint64_t stepping = -1;
- std::string vendor_id;
-
- Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
-
- auto BufferOrError = getProcFile("cpuinfo");
- if (!BufferOrError)
- return BufferOrError.getError();
-
- LLDB_LOG(log, "GetCPUType Function");
-
- StringRef Rest = BufferOrError.get()->getBuffer();
- while (!Rest.empty()) {
- StringRef Line;
- std::tie(Line, Rest) = Rest.split('\n');
-
- SmallVector<StringRef, 2> columns;
- Line.split(columns, StringRef(":"), -1, false);
-
- if (columns.size() < 2)
- continue; // continue searching
-
- columns[1] = columns[1].trim(" ");
- if (columns[0].contains("cpu family") &&
- columns[1].getAsInteger(10, cpu_family))
- continue;
-
- else if (columns[0].contains("model") && columns[1].getAsInteger(10, model))
- continue;
-
- else if (columns[0].contains("stepping") &&
- columns[1].getAsInteger(10, stepping))
- continue;
-
- else if (columns[0].contains("vendor_id")) {
- vendor_id = columns[1].str();
- if (!vendor_id.empty())
- continue;
- }
- 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);
-
- 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 error; // we are done
- }
- }
-
- error.SetErrorString("cpu info not found");
- return error;
-}
-
-llvm::Expected<ProcessorTraceMonitorUP>
-ProcessorTraceMonitor::Create(lldb::pid_t pid, lldb::tid_t tid,
- const TraceOptions &config,
- bool useProcessSettings) {
-
- Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
-
- Status error;
- if (tid == LLDB_INVALID_THREAD_ID) {
- error.SetErrorString("thread not specified");
- return error.ToError();
- }
-
- ProcessorTraceMonitorUP pt_monitor_up(new ProcessorTraceMonitor);
-
- error = pt_monitor_up->StartTrace(pid, tid, config);
- if (error.Fail())
- return error.ToError();
-
- pt_monitor_up->SetThreadID(tid);
-
- if (useProcessSettings) {
- pt_monitor_up->SetTraceID(0);
- } else {
- pt_monitor_up->SetTraceID(m_trace_num++);
- LLDB_LOG(log, "Trace ID {0}", m_trace_num);
- }
- return std::move(pt_monitor_up);
-}
-
-Status
-ProcessorTraceMonitor::ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer,
- size_t offset) {
-#ifndef PERF_ATTR_SIZE_VER5
- llvm_unreachable("perf event not supported");
-#else
- // Disable the perf event to force a flush out of the CPU's internal buffer.
- // Besides, we can guarantee that the CPU won't override any data as we are
- // reading the buffer.
- //
- // The Intel documentation says:
- //
- // Packets are first buffered internally and then written out asynchronously.
- // To collect packet output for postprocessing, a collector needs first to
- // ensure that all packet data has been flushed from internal buffers.
- // Software can ensure this by stopping packet generation by clearing
- // IA32_RTIT_CTL.TraceEn (see “Disabling Packet Generation” in
- // Section 35.2.7.2).
- //
- // This is achieved by the PERF_EVENT_IOC_DISABLE ioctl request, as mentioned
- // in the man page of perf_event_open.
- ioctl(*m_fd, PERF_EVENT_IOC_DISABLE);
-
- Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
- Status error;
- uint64_t head = m_mmap_meta->aux_head;
-
- LLDB_LOG(log, "Aux size -{0} , Head - {1}", m_mmap_meta->aux_size, head);
-
- /**
- * When configured as ring buffer, the aux buffer keeps wrapping around
- * the buffer and its not possible to detect how many times the buffer
- * wrapped. Initially the buffer is filled with zeros,as shown below
- * so in order to get complete buffer we first copy firstpartsize, followed
- * by any left over part from beginning to aux_head
- *
- * aux_offset [d,d,d,d,d,d,d,d,0,0,0,0,0,0,0,0,0,0,0] aux_size
- * aux_head->||<- firstpartsize ->|
- *
- * */
-
- ReadCyclicBuffer(buffer, GetAuxBuffer(), static_cast<size_t>(head), offset);
- LLDB_LOG(log, "ReadCyclic BUffer Done");
-
- // Reenable tracing now we have read the buffer
- ioctl(*m_fd, PERF_EVENT_IOC_ENABLE);
- return error;
-#endif
-}
-
-Status
-ProcessorTraceMonitor::ReadPerfTraceData(llvm::MutableArrayRef<uint8_t> &buffer,
- size_t offset) {
-#ifndef PERF_ATTR_SIZE_VER5
- llvm_unreachable("perf event not supported");
-#else
- Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
- uint64_t bytes_remaining = buffer.size();
- Status error;
-
- uint64_t head = m_mmap_meta->data_head;
-
- /*
- * The data buffer and aux buffer have
diff erent implementations
- * with respect to their definition of head pointer. In the case
- * of Aux data buffer the head always wraps around the aux buffer
- * and we don't need to care about it, whereas the data_head keeps
- * increasing and needs to be wrapped by modulus operator
- */
-
- LLDB_LOG(log, "bytes_remaining - {0}", bytes_remaining);
-
- auto data_buffer = GetDataBuffer();
-
- if (head > data_buffer.size()) {
- head = head % data_buffer.size();
- LLDB_LOG(log, "Data size -{0} Head - {1}", m_mmap_meta->data_size, head);
-
- ReadCyclicBuffer(buffer, data_buffer, static_cast<size_t>(head), offset);
- bytes_remaining -= buffer.size();
- } else {
- LLDB_LOG(log, "Head - {0}", head);
- if (offset >= head) {
- LLDB_LOG(log, "Invalid Offset ");
- error.SetErrorString("invalid offset");
- buffer = buffer.slice(buffer.size());
- return error;
- }
-
- auto data = data_buffer.slice(offset, (head - offset));
- auto remaining = std::copy(data.begin(), data.end(), buffer.begin());
- bytes_remaining -= (remaining - buffer.begin());
- }
- buffer = buffer.drop_back(bytes_remaining);
- return error;
-#endif
-}
-
-void ProcessorTraceMonitor::ReadCyclicBuffer(
- llvm::MutableArrayRef<uint8_t> &dst, llvm::MutableArrayRef<uint8_t> src,
- size_t src_cyc_index, size_t offset) {
-
- Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE));
-
- if (dst.empty() || src.empty()) {
- dst = dst.drop_back(dst.size());
- return;
- }
-
- if (dst.data() == nullptr || src.data() == nullptr) {
- dst = dst.drop_back(dst.size());
- return;
- }
-
- if (src_cyc_index > src.size()) {
- dst = dst.drop_back(dst.size());
- return;
- }
-
- if (offset >= src.size()) {
- LLDB_LOG(log, "Too Big offset ");
- dst = dst.drop_back(dst.size());
- return;
- }
-
- llvm::SmallVector<MutableArrayRef<uint8_t>, 2> parts = {
- src.slice(src_cyc_index), src.take_front(src_cyc_index)};
-
- if (offset > parts[0].size()) {
- parts[1] = parts[1].slice(offset - parts[0].size());
- parts[0] = parts[0].drop_back(parts[0].size());
- } else if (offset == parts[0].size()) {
- parts[0] = parts[0].drop_back(parts[0].size());
- } else {
- parts[0] = parts[0].slice(offset);
- }
- auto next = dst.begin();
- auto bytes_left = dst.size();
- for (auto part : parts) {
- size_t chunk_size = std::min(part.size(), bytes_left);
- next = std::copy_n(part.begin(), chunk_size, next);
- bytes_left -= chunk_size;
- }
- dst = dst.drop_back(bytes_left);
-}
diff --git a/lldb/source/Plugins/Process/Linux/ProcessorTrace.h b/lldb/source/Plugins/Process/Linux/ProcessorTrace.h
deleted file mode 100644
index 29f98cc5be658..0000000000000
--- a/lldb/source/Plugins/Process/Linux/ProcessorTrace.h
+++ /dev/null
@@ -1,140 +0,0 @@
-//===-- ProcessorTrace.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_ProcessorTrace_H_
-#define liblldb_ProcessorTrace_H_
-
-#include "lldb/Utility/Status.h"
-#include "lldb/Utility/TraceOptions.h"
-#include "lldb/lldb-types.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/DenseSet.h"
-
-#include <linux/perf_event.h>
-#include <sys/mman.h>
-#include <unistd.h>
-
-namespace lldb_private {
-
-namespace process_linux {
-
-// This class keeps track of one tracing instance of
-// Intel(R) Processor Trace on Linux OS. There is a map keeping track
-// of
diff erent tracing instances on each thread, which enables trace
-// gathering on a per thread level.
-//
-// The tracing instance is linked with a trace id. The trace id acts like
-// a key to the tracing instance and trace manipulations could be
-// performed using the trace id.
-//
-// The trace id could map to trace instances for a group of threads
-// (spanning to all the threads in the process) or a single thread.
-// The kernel interface for us is the perf_event_open.
-
-class ProcessorTraceMonitor;
-typedef std::unique_ptr<ProcessorTraceMonitor> ProcessorTraceMonitorUP;
-
-class ProcessorTraceMonitor {
-
- class munmap_delete {
- size_t m_length;
-
- public:
- munmap_delete(size_t length) : m_length(length) {}
- void operator()(void *ptr) {
- if (m_length)
- munmap(ptr, m_length);
- }
- };
-
- class file_close {
-
- public:
- file_close() = default;
- void operator()(int *ptr) {
- if (ptr == nullptr)
- return;
- if (*ptr == -1)
- return;
- close(*ptr);
- std::default_delete<int>()(ptr);
- }
- };
-
- std::unique_ptr<perf_event_mmap_page, munmap_delete> m_mmap_meta;
- std::unique_ptr<uint8_t, munmap_delete> m_mmap_aux;
- std::unique_ptr<int, file_close> m_fd;
-
- // perf_event_mmap_page *m_mmap_base;
- lldb::user_id_t m_traceid;
- lldb::tid_t m_thread_id;
-
- // Counter to track trace instances.
- static lldb::user_id_t m_trace_num;
-
- void SetTraceID(lldb::user_id_t traceid) { m_traceid = traceid; }
-
- Status StartTrace(lldb::pid_t pid, lldb::tid_t tid,
- const TraceOptions &config);
-
- llvm::MutableArrayRef<uint8_t> GetAuxBuffer();
- llvm::MutableArrayRef<uint8_t> GetDataBuffer();
-
- ProcessorTraceMonitor()
- : m_mmap_meta(nullptr, munmap_delete(0)),
- m_mmap_aux(nullptr, munmap_delete(0)), m_fd(nullptr, file_close()),
- m_traceid(LLDB_INVALID_UID), m_thread_id(LLDB_INVALID_THREAD_ID){};
-
- void SetThreadID(lldb::tid_t tid) { m_thread_id = tid; }
-
-public:
- static llvm::Expected<uint32_t> GetOSEventType();
-
- static bool IsSupported();
-
- static Status GetCPUType(TraceOptions &config);
-
- static llvm::Expected<ProcessorTraceMonitorUP>
- Create(lldb::pid_t pid, lldb::tid_t tid, const TraceOptions &config,
- bool useProcessSettings);
-
- Status ReadPerfTraceAux(llvm::MutableArrayRef<uint8_t> &buffer,
- size_t offset = 0);
-
- Status ReadPerfTraceData(llvm::MutableArrayRef<uint8_t> &buffer,
- size_t offset = 0);
-
- ~ProcessorTraceMonitor() = default;
-
- lldb::tid_t GetThreadID() const { return m_thread_id; }
-
- lldb::user_id_t GetTraceID() const { return m_traceid; }
-
- Status GetTraceConfig(TraceOptions &config) const;
-
- /// Read data from a cyclic buffer
- ///
- /// \param[in] [out] buf
- /// Destination buffer, the buffer will be truncated to written size.
- ///
- /// \param[in] src
- /// Source buffer which must be a cyclic buffer.
- ///
- /// \param[in] src_cyc_index
- /// The index pointer (start of the valid data in the cyclic
- /// buffer).
- ///
- /// \param[in] offset
- /// The offset to begin reading the data in the cyclic buffer.
- static void ReadCyclicBuffer(llvm::MutableArrayRef<uint8_t> &dst,
- llvm::MutableArrayRef<uint8_t> src,
- size_t src_cyc_index, size_t offset);
-};
-} // namespace process_linux
-} // namespace lldb_private
-#endif
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index 9218f8a63c01e..22e05c04b3a08 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -3372,110 +3372,78 @@ bool GDBRemoteCommunicationClient::SyncThreadState(lldb::tid_t tid) {
response.IsOKResponse();
}
-lldb::user_id_t
-GDBRemoteCommunicationClient::SendStartTracePacket(const TraceOptions &options,
- Status &error) {
+llvm::Expected<TraceSupportedResponse>
+GDBRemoteCommunicationClient::SendTraceSupported() {
Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
- lldb::user_id_t ret_uid = LLDB_INVALID_UID;
StreamGDBRemote escaped_packet;
- escaped_packet.PutCString("jTraceStart:");
-
- StructuredData::Dictionary json_packet;
- json_packet.AddIntegerItem("type", options.getType());
- json_packet.AddIntegerItem("buffersize", options.getTraceBufferSize());
- json_packet.AddIntegerItem("metabuffersize", options.getMetaDataBufferSize());
-
- if (options.getThreadID() != LLDB_INVALID_THREAD_ID)
- json_packet.AddIntegerItem("threadid", options.getThreadID());
-
- StructuredData::DictionarySP custom_params = options.getTraceParams();
- if (custom_params)
- json_packet.AddItem("params", custom_params);
-
- StreamString json_string;
- json_packet.Dump(json_string, false);
- escaped_packet.PutEscapedBytes(json_string.GetData(), json_string.GetSize());
+ escaped_packet.PutCString("jLLDBTraceSupported");
StringExtractorGDBRemote response;
if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response,
true) ==
GDBRemoteCommunication::PacketResult::Success) {
- if (!response.IsNormalResponse()) {
- error = response.GetStatus();
- LLDB_LOG(log, "Target does not support Tracing , error {0}", error);
- } else {
- ret_uid = response.GetHexMaxU64(false, LLDB_INVALID_UID);
- }
- } else {
- LLDB_LOG(log, "failed to send packet");
- error.SetErrorStringWithFormat("failed to send packet: '%s'",
- escaped_packet.GetData());
+ if (response.IsErrorResponse())
+ return response.GetStatus().ToError();
+ if (response.IsUnsupportedResponse())
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "jLLDBTraceSupported is unsupported");
+
+ return llvm::json::parse<TraceSupportedResponse>(response.Peek(),
+ "TraceSupportedResponse");
}
- return ret_uid;
+ LLDB_LOG(log, "failed to send packet: jLLDBTraceSupported");
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "failed to send packet: jLLDBTraceSupported");
}
-Status
-GDBRemoteCommunicationClient::SendStopTracePacket(lldb::user_id_t uid,
- lldb::tid_t thread_id) {
+llvm::Error
+GDBRemoteCommunicationClient::SendTraceStop(const TraceStopRequest &request) {
Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
- StringExtractorGDBRemote response;
- Status error;
- StructuredData::Dictionary json_packet;
StreamGDBRemote escaped_packet;
- StreamString json_string;
- escaped_packet.PutCString("jTraceStop:");
-
- json_packet.AddIntegerItem("traceid", uid);
+ escaped_packet.PutCString("jLLDBTraceStop:");
- if (thread_id != LLDB_INVALID_THREAD_ID)
- json_packet.AddIntegerItem("threadid", thread_id);
+ std::string json_string;
+ llvm::raw_string_ostream os(json_string);
+ os << toJSON(request);
+ os.flush();
- json_packet.Dump(json_string, false);
-
- escaped_packet.PutEscapedBytes(json_string.GetData(), json_string.GetSize());
+ 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()) {
- error = response.GetStatus();
- LLDB_LOG(log, "stop tracing failed");
- }
- } else {
- LLDB_LOG(log, "failed to send packet");
- error.SetErrorStringWithFormat(
- "failed to send packet: '%s' with error '%d'", escaped_packet.GetData(),
- response.GetError());
+ if (response.IsErrorResponse())
+ return response.GetStatus().ToError();
+ if (response.IsUnsupportedResponse())
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "jLLDBTraceStop is unsupported");
+ if (response.IsOKResponse())
+ return llvm::Error::success();
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Invalid jLLDBTraceStart response");
}
- return error;
+ LLDB_LOG(log, "failed to send packet: jLLDBTraceStop");
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "failed to send packet: jLLDBTraceStop '%s'",
+ escaped_packet.GetData());
}
-Status GDBRemoteCommunicationClient::SendGetDataPacket(
- lldb::user_id_t uid, lldb::tid_t thread_id,
- llvm::MutableArrayRef<uint8_t> &buffer, size_t offset) {
+llvm::Error
+GDBRemoteCommunicationClient::SendTraceStart(const llvm::json::Value ¶ms) {
+ Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
StreamGDBRemote escaped_packet;
- escaped_packet.PutCString("jTraceBufferRead:");
- return SendGetTraceDataPacket(escaped_packet, uid, thread_id, buffer, offset);
-}
+ escaped_packet.PutCString("jLLDBTraceStart:");
-Status GDBRemoteCommunicationClient::SendGetMetaDataPacket(
- lldb::user_id_t uid, lldb::tid_t thread_id,
- llvm::MutableArrayRef<uint8_t> &buffer, size_t offset) {
+ std::string json_string;
+ llvm::raw_string_ostream os(json_string);
+ os << params;
+ os.flush();
- StreamGDBRemote escaped_packet;
- escaped_packet.PutCString("jTraceMetaRead:");
- return SendGetTraceDataPacket(escaped_packet, uid, thread_id, buffer, offset);
-}
-
-llvm::Expected<TraceTypeInfo>
-GDBRemoteCommunicationClient::SendGetSupportedTraceType() {
- Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
-
- StreamGDBRemote escaped_packet;
- escaped_packet.PutCString("jLLDBTraceSupportedType");
+ escaped_packet.PutEscapedBytes(json_string.c_str(), json_string.size());
StringExtractorGDBRemote response;
if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response,
@@ -3485,127 +3453,84 @@ GDBRemoteCommunicationClient::SendGetSupportedTraceType() {
return response.GetStatus().ToError();
if (response.IsUnsupportedResponse())
return llvm::createStringError(llvm::inconvertibleErrorCode(),
- "jLLDBTraceSupportedType is unsupported");
-
- if (llvm::Expected<TraceTypeInfo> type =
- llvm::json::parse<TraceTypeInfo>(response.Peek()))
- return *type;
- else
- return type.takeError();
+ "jLLDBTraceStart is unsupported");
+ if (response.IsOKResponse())
+ return llvm::Error::success();
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Invalid jLLDBTraceStart response");
}
- LLDB_LOG(log, "failed to send packet: jLLDBTraceSupportedType");
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
- "failed to send packet: jLLDBTraceSupportedType");
+ LLDB_LOG(log, "failed to send packet: jLLDBTraceStart");
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "failed to send packet: jLLDBTraceStart '%s'",
+ escaped_packet.GetData());
}
-Status
-GDBRemoteCommunicationClient::SendGetTraceConfigPacket(lldb::user_id_t uid,
- TraceOptions &options) {
+llvm::Expected<std::string>
+GDBRemoteCommunicationClient::SendTraceGetState(llvm::StringRef type) {
Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
- StringExtractorGDBRemote response;
- Status error;
- StreamString json_string;
StreamGDBRemote escaped_packet;
- escaped_packet.PutCString("jTraceConfigRead:");
+ escaped_packet.PutCString("jLLDBTraceGetState:");
- StructuredData::Dictionary json_packet;
- json_packet.AddIntegerItem("traceid", uid);
+ std::string json_string;
+ llvm::raw_string_ostream os(json_string);
+ os << toJSON(TraceGetStateRequest{type.str()});
+ os.flush();
- if (options.getThreadID() != LLDB_INVALID_THREAD_ID)
- json_packet.AddIntegerItem("threadid", options.getThreadID());
-
- json_packet.Dump(json_string, false);
- escaped_packet.PutEscapedBytes(json_string.GetData(), json_string.GetSize());
+ 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()) {
- uint64_t type = std::numeric_limits<uint64_t>::max();
- uint64_t buffersize = std::numeric_limits<uint64_t>::max();
- uint64_t metabuffersize = std::numeric_limits<uint64_t>::max();
-
- auto json_object = StructuredData::ParseJSON(response.Peek());
-
- if (!json_object ||
- json_object->GetType() != lldb::eStructuredDataTypeDictionary) {
- error.SetErrorString("Invalid Configuration obtained");
- return error;
- }
-
- auto json_dict = json_object->GetAsDictionary();
-
- json_dict->GetValueForKeyAsInteger<uint64_t>("metabuffersize",
- metabuffersize);
- options.setMetaDataBufferSize(metabuffersize);
-
- json_dict->GetValueForKeyAsInteger<uint64_t>("buffersize", buffersize);
- options.setTraceBufferSize(buffersize);
-
- json_dict->GetValueForKeyAsInteger<uint64_t>("type", type);
- options.setType(static_cast<lldb::TraceType>(type));
-
- StructuredData::ObjectSP custom_params_sp =
- json_dict->GetValueForKey("params");
- if (custom_params_sp) {
- if (custom_params_sp->GetType() !=
- lldb::eStructuredDataTypeDictionary) {
- error.SetErrorString("Invalid Configuration obtained");
- return error;
- } else
- options.setTraceParams(
- std::static_pointer_cast<StructuredData::Dictionary>(
- custom_params_sp));
- }
- } else {
- error = response.GetStatus();
- }
- } else {
- LLDB_LOG(log, "failed to send packet");
- error.SetErrorStringWithFormat("failed to send packet: '%s'",
- escaped_packet.GetData());
+ if (response.IsErrorResponse())
+ return response.GetStatus().ToError();
+ if (response.IsUnsupportedResponse())
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "jLLDBTraceGetState is unsupported");
+ return std::string(response.Peek());
}
- return error;
+
+ LLDB_LOG(log, "failed to send packet: jLLDBTraceGetState");
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "failed to send packet: jLLDBTraceGetState '%s'",
+ escaped_packet.GetData());
}
-Status GDBRemoteCommunicationClient::SendGetTraceDataPacket(
- StreamGDBRemote &packet, lldb::user_id_t uid, lldb::tid_t thread_id,
- llvm::MutableArrayRef<uint8_t> &buffer, size_t offset) {
+llvm::Expected<std::vector<uint8_t>>
+GDBRemoteCommunicationClient::SendTraceGetBinaryData(
+ const TraceGetBinaryDataRequest &request) {
Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
- Status error;
- StructuredData::Dictionary json_packet;
-
- json_packet.AddIntegerItem("traceid", uid);
- json_packet.AddIntegerItem("offset", offset);
- json_packet.AddIntegerItem("buffersize", buffer.size());
+ StreamGDBRemote escaped_packet;
+ escaped_packet.PutCString("jLLDBTraceGetBinaryData:");
- if (thread_id != LLDB_INVALID_THREAD_ID)
- json_packet.AddIntegerItem("threadid", thread_id);
+ std::string json_string;
+ llvm::raw_string_ostream os(json_string);
+ os << toJSON(request);
+ os.flush();
- StreamString json_string;
- json_packet.Dump(json_string, false);
+ escaped_packet.PutEscapedBytes(json_string.c_str(), json_string.size());
- packet.PutEscapedBytes(json_string.GetData(), json_string.GetSize());
StringExtractorGDBRemote response;
- if (SendPacketAndWaitForResponse(packet.GetString(), response, true) ==
+ if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response,
+ true) ==
GDBRemoteCommunication::PacketResult::Success) {
- if (response.IsNormalResponse()) {
- size_t filled_size = response.GetHexBytesAvail(buffer);
- buffer = llvm::MutableArrayRef<uint8_t>(buffer.data(), filled_size);
- } else {
- error = response.GetStatus();
- buffer = buffer.slice(buffer.size());
- }
- } else {
- LLDB_LOG(log, "failed to send packet");
- error.SetErrorStringWithFormat("failed to send packet: '%s'",
- packet.GetData());
- buffer = buffer.slice(buffer.size());
+ if (response.IsErrorResponse())
+ return response.GetStatus().ToError();
+ if (response.IsUnsupportedResponse())
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "jLLDBTraceGetBinaryData is unsupported");
+ std::string data;
+ response.GetEscapedBinaryData(data);
+ return std::vector<uint8_t>(data.begin(), data.end());
}
- return error;
+ LLDB_LOG(log, "failed to send packet: jLLDBTraceGetBinaryData");
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "failed to send packet: jLLDBTraceGetBinaryData '%s'",
+ escaped_packet.GetData());
}
llvm::Optional<QOffsets> GDBRemoteCommunicationClient::GetQOffsets() {
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index 3eab71388067a..aaa6f7890fb14 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -22,7 +22,7 @@
#include "lldb/Utility/GDBRemote.h"
#include "lldb/Utility/ProcessInfo.h"
#include "lldb/Utility/StructuredData.h"
-#include "lldb/Utility/TraceOptions.h"
+#include "lldb/Utility/TraceGDBRemotePackets.h"
#if defined(_WIN32)
#include "lldb/Host/windows/PosixApi.h"
#endif
@@ -505,22 +505,16 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
ConfigureRemoteStructuredData(ConstString type_name,
const StructuredData::ObjectSP &config_sp);
- lldb::user_id_t SendStartTracePacket(const TraceOptions &options,
- Status &error);
+ llvm::Expected<TraceSupportedResponse> SendTraceSupported();
- Status SendStopTracePacket(lldb::user_id_t uid, lldb::tid_t thread_id);
+ llvm::Error SendTraceStart(const llvm::json::Value &request);
- Status SendGetDataPacket(lldb::user_id_t uid, lldb::tid_t thread_id,
- llvm::MutableArrayRef<uint8_t> &buffer,
- size_t offset = 0);
+ llvm::Error SendTraceStop(const TraceStopRequest &request);
- Status SendGetMetaDataPacket(lldb::user_id_t uid, lldb::tid_t thread_id,
- llvm::MutableArrayRef<uint8_t> &buffer,
- size_t offset = 0);
+ llvm::Expected<std::string> SendTraceGetState(llvm::StringRef type);
- Status SendGetTraceConfigPacket(lldb::user_id_t uid, TraceOptions &options);
-
- llvm::Expected<TraceTypeInfo> SendGetSupportedTraceType();
+ llvm::Expected<std::vector<uint8_t>>
+ SendTraceGetBinaryData(const TraceGetBinaryDataRequest &request);
protected:
LazyBool m_supports_not_sending_acks;
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
index 60548efc0f33f..2996717dae6a0 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp
+++ b/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 @@ GDBRemoteCommunicationServer::SendOKResponse() {
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);
+}
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h
index a1cf70f9cd1ab..68448eae2b9f8 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h
@@ -72,6 +72,13 @@ class GDBRemoteCommunicationServer : public GDBRemoteCommunication {
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 &
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index b665610a54a5f..2f615866a2f43 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -186,23 +186,20 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
&GDBRemoteCommunicationServerLLGS::Handle_QPassSignals);
RegisterMemberFunctionHandler(
- StringExtractorGDBRemote::eServerPacketType_jTraceStart,
- &GDBRemoteCommunicationServerLLGS::Handle_jTraceStart);
+ StringExtractorGDBRemote::eServerPacketType_jLLDBTraceSupported,
+ &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceSupported);
RegisterMemberFunctionHandler(
- StringExtractorGDBRemote::eServerPacketType_jTraceBufferRead,
- &GDBRemoteCommunicationServerLLGS::Handle_jTraceRead);
+ StringExtractorGDBRemote::eServerPacketType_jLLDBTraceStart,
+ &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceStart);
RegisterMemberFunctionHandler(
- StringExtractorGDBRemote::eServerPacketType_jTraceMetaRead,
- &GDBRemoteCommunicationServerLLGS::Handle_jTraceRead);
+ StringExtractorGDBRemote::eServerPacketType_jLLDBTraceStop,
+ &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceStop);
RegisterMemberFunctionHandler(
- StringExtractorGDBRemote::eServerPacketType_jTraceStop,
- &GDBRemoteCommunicationServerLLGS::Handle_jTraceStop);
+ StringExtractorGDBRemote::eServerPacketType_jLLDBTraceGetState,
+ &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceGetState);
RegisterMemberFunctionHandler(
- StringExtractorGDBRemote::eServerPacketType_jTraceConfigRead,
- &GDBRemoteCommunicationServerLLGS::Handle_jTraceConfigRead);
- RegisterMemberFunctionHandler(
- StringExtractorGDBRemote::eServerPacketType_jLLDBTraceSupportedType,
- &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceSupportedType);
+ StringExtractorGDBRemote::eServerPacketType_jLLDBTraceGetBinaryData,
+ &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceGetBinaryData);
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_g,
&GDBRemoteCommunicationServerLLGS::Handle_g);
@@ -648,6 +645,8 @@ static const char *GetStopReasonString(StopReason stop_reason) {
return "exception";
case eStopReasonExec:
return "exec";
+ case eStopReasonProcessorTrace:
+ return "processor trace";
case eStopReasonInstrumentation:
case eStopReasonInvalid:
case eStopReasonPlanComplete:
@@ -1170,108 +1169,39 @@ void GDBRemoteCommunicationServerLLGS::SendProcessOutput() {
}
GDBRemoteCommunication::PacketResult
-GDBRemoteCommunicationServerLLGS::Handle_jTraceStart(
+GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceSupported(
StringExtractorGDBRemote &packet) {
- Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
// 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(68);
-
- if (!packet.ConsumeFront("jTraceStart:"))
- return SendIllFormedResponse(packet, "jTraceStart: Ill formed packet ");
-
- TraceOptions options;
- uint64_t type = std::numeric_limits<uint64_t>::max();
- uint64_t buffersize = std::numeric_limits<uint64_t>::max();
- lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
- uint64_t metabuffersize = std::numeric_limits<uint64_t>::max();
-
- auto json_object = StructuredData::ParseJSON(packet.Peek());
-
- if (!json_object ||
- json_object->GetType() != lldb::eStructuredDataTypeDictionary)
- return SendIllFormedResponse(packet, "jTraceStart: Ill formed packet ");
-
- auto json_dict = json_object->GetAsDictionary();
-
- json_dict->GetValueForKeyAsInteger("metabuffersize", metabuffersize);
- options.setMetaDataBufferSize(metabuffersize);
-
- json_dict->GetValueForKeyAsInteger("buffersize", buffersize);
- options.setTraceBufferSize(buffersize);
-
- json_dict->GetValueForKeyAsInteger("type", type);
- options.setType(static_cast<lldb::TraceType>(type));
-
- json_dict->GetValueForKeyAsInteger("threadid", tid);
- options.setThreadID(tid);
-
- StructuredData::ObjectSP custom_params_sp =
- json_dict->GetValueForKey("params");
- if (custom_params_sp &&
- custom_params_sp->GetType() != lldb::eStructuredDataTypeDictionary)
- return SendIllFormedResponse(packet, "jTraceStart: Ill formed packet ");
-
- options.setTraceParams(
- std::static_pointer_cast<StructuredData::Dictionary>(custom_params_sp));
-
- if (buffersize == std::numeric_limits<uint64_t>::max() ||
- type != lldb::TraceType::eTraceTypeProcessorTrace) {
- LLDB_LOG(log, "Ill formed packet buffersize = {0} type = {1}", buffersize,
- type);
- return SendIllFormedResponse(packet, "JTrace:start: Ill formed packet ");
- }
-
- Status error;
- lldb::user_id_t uid = LLDB_INVALID_UID;
- uid = m_debugged_process_up->StartTrace(options, error);
- LLDB_LOG(log, "uid is {0} , error is {1}", uid, error.GetError());
- if (error.Fail())
- return SendErrorResponse(error);
+ return SendErrorResponse(Status("Process not running."));
- StreamGDBRemote response;
- response.Printf("%" PRIx64, uid);
- return SendPacketNoLock(response.GetString());
+ return SendJSONResponse(m_debugged_process_up->TraceSupported());
}
GDBRemoteCommunication::PacketResult
-GDBRemoteCommunicationServerLLGS::Handle_jTraceStop(
+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(68);
-
- if (!packet.ConsumeFront("jTraceStop:"))
- return SendIllFormedResponse(packet, "jTraceStop: Ill formed packet ");
-
- lldb::user_id_t uid = LLDB_INVALID_UID;
- lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
-
- auto json_object = StructuredData::ParseJSON(packet.Peek());
-
- if (!json_object ||
- json_object->GetType() != lldb::eStructuredDataTypeDictionary)
- return SendIllFormedResponse(packet, "jTraceStop: Ill formed packet ");
-
- auto json_dict = json_object->GetAsDictionary();
-
- if (!json_dict->GetValueForKeyAsInteger("traceid", uid))
- return SendIllFormedResponse(packet, "jTraceStop: Ill formed packet ");
-
- json_dict->GetValueForKeyAsInteger("threadid", tid);
+ return SendErrorResponse(Status("Process not running."));
- Status error = m_debugged_process_up->StopTrace(uid, tid);
+ packet.ConsumeFront("jLLDBTraceStop:");
+ Expected<TraceStopRequest> stop_request =
+ json::parse<TraceStopRequest>(packet.Peek(), "TraceStopRequest");
+ if (!stop_request)
+ return SendErrorResponse(stop_request.takeError());
- if (error.Fail())
- return SendErrorResponse(error);
+ if (Error err = m_debugged_process_up->TraceStop(*stop_request))
+ return SendErrorResponse(std::move(err));
return SendOKResponse();
}
GDBRemoteCommunication::PacketResult
-GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceSupportedType(
+GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceStart(
StringExtractorGDBRemote &packet) {
// Fail if we don't have a current process.
@@ -1279,146 +1209,60 @@ GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceSupportedType(
(m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID))
return SendErrorResponse(Status("Process not running."));
- llvm::Expected<TraceTypeInfo> supported_trace_type =
- m_debugged_process_up->GetSupportedTraceType();
- if (!supported_trace_type)
- return SendErrorResponse(supported_trace_type.takeError());
-
- StreamGDBRemote escaped_response;
- StructuredData::Dictionary json_packet;
+ packet.ConsumeFront("jLLDBTraceStart:");
+ Expected<TraceStartRequest> request =
+ json::parse<TraceStartRequest>(packet.Peek(), "TraceStartRequest");
+ if (!request)
+ return SendErrorResponse(request.takeError());
- json_packet.AddStringItem("name", supported_trace_type->name);
- json_packet.AddStringItem("description", supported_trace_type->description);
+ if (Error err =
+ m_debugged_process_up->TraceStart(packet.Peek(), request->type))
+ return SendErrorResponse(std::move(err));
- StreamString json_string;
- json_packet.Dump(json_string, false);
- escaped_response.PutEscapedBytes(json_string.GetData(),
- json_string.GetSize());
- return SendPacketNoLock(escaped_response.GetString());
+ return SendOKResponse();
}
GDBRemoteCommunication::PacketResult
-GDBRemoteCommunicationServerLLGS::Handle_jTraceConfigRead(
+GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceGetState(
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(68);
-
- if (!packet.ConsumeFront("jTraceConfigRead:"))
- return SendIllFormedResponse(packet,
- "jTraceConfigRead: Ill formed packet ");
-
- lldb::user_id_t uid = LLDB_INVALID_UID;
- lldb::tid_t threadid = LLDB_INVALID_THREAD_ID;
-
- auto json_object = StructuredData::ParseJSON(packet.Peek());
-
- if (!json_object ||
- json_object->GetType() != lldb::eStructuredDataTypeDictionary)
- return SendIllFormedResponse(packet,
- "jTraceConfigRead: Ill formed packet ");
-
- auto json_dict = json_object->GetAsDictionary();
-
- if (!json_dict->GetValueForKeyAsInteger("traceid", uid))
- return SendIllFormedResponse(packet,
- "jTraceConfigRead: Ill formed packet ");
-
- json_dict->GetValueForKeyAsInteger("threadid", threadid);
-
- TraceOptions options;
- StreamGDBRemote response;
-
- options.setThreadID(threadid);
- Status error = m_debugged_process_up->GetTraceConfig(uid, options);
-
- if (error.Fail())
- return SendErrorResponse(error);
-
- StreamGDBRemote escaped_response;
- StructuredData::Dictionary json_packet;
-
- json_packet.AddIntegerItem("type", options.getType());
- json_packet.AddIntegerItem("buffersize", options.getTraceBufferSize());
- json_packet.AddIntegerItem("metabuffersize", options.getMetaDataBufferSize());
+ return SendErrorResponse(Status("Process not running."));
- StructuredData::DictionarySP custom_params = options.getTraceParams();
- if (custom_params)
- json_packet.AddItem("params", custom_params);
+ packet.ConsumeFront("jLLDBTraceGetState:");
+ Expected<TraceGetStateRequest> request =
+ json::parse<TraceGetStateRequest>(packet.Peek(), "TraceGetStateRequest");
+ if (!request)
+ return SendErrorResponse(request.takeError());
- StreamString json_string;
- json_packet.Dump(json_string, false);
- escaped_response.PutEscapedBytes(json_string.GetData(),
- json_string.GetSize());
- return SendPacketNoLock(escaped_response.GetString());
+ return SendJSONResponse(m_debugged_process_up->TraceGetState(request->type));
}
GDBRemoteCommunication::PacketResult
-GDBRemoteCommunicationServerLLGS::Handle_jTraceRead(
+GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceGetBinaryData(
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(68);
-
- enum PacketType { MetaData, BufferData };
- PacketType tracetype = MetaData;
-
- if (packet.ConsumeFront("jTraceBufferRead:"))
- tracetype = BufferData;
- else if (packet.ConsumeFront("jTraceMetaRead:"))
- tracetype = MetaData;
- else {
- return SendIllFormedResponse(packet, "jTrace: Ill formed packet ");
- }
-
- lldb::user_id_t uid = LLDB_INVALID_UID;
-
- uint64_t byte_count = std::numeric_limits<uint64_t>::max();
- lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
- uint64_t offset = std::numeric_limits<uint64_t>::max();
-
- auto json_object = StructuredData::ParseJSON(packet.Peek());
-
- if (!json_object ||
- json_object->GetType() != lldb::eStructuredDataTypeDictionary)
- return SendIllFormedResponse(packet, "jTrace: Ill formed packet ");
-
- auto json_dict = json_object->GetAsDictionary();
-
- if (!json_dict->GetValueForKeyAsInteger("traceid", uid) ||
- !json_dict->GetValueForKeyAsInteger("offset", offset) ||
- !json_dict->GetValueForKeyAsInteger("buffersize", byte_count))
- return SendIllFormedResponse(packet, "jTrace: Ill formed packet ");
-
- json_dict->GetValueForKeyAsInteger("threadid", tid);
-
- // Allocate the response buffer.
- std::unique_ptr<uint8_t[]> buffer (new (std::nothrow) uint8_t[byte_count]);
- if (!buffer)
- return SendErrorResponse(0x78);
-
- StreamGDBRemote response;
- Status error;
- llvm::MutableArrayRef<uint8_t> buf(buffer.get(), byte_count);
-
- if (tracetype == BufferData)
- error = m_debugged_process_up->GetData(uid, tid, buf, offset);
- else if (tracetype == MetaData)
- error = m_debugged_process_up->GetMetaData(uid, tid, buf, offset);
-
- if (error.Fail())
- return SendErrorResponse(error);
+ return SendErrorResponse(Status("Process not running."));
- for (auto i : buf)
- response.PutHex8(i);
+ packet.ConsumeFront("jLLDBTraceGetBinaryData:");
+ llvm::Expected<TraceGetBinaryDataRequest> request =
+ llvm::json::parse<TraceGetBinaryDataRequest>(packet.Peek(),
+ "TraceGetBinaryDataRequest");
+ if (!request)
+ return SendErrorResponse(Status(request.takeError()));
- StreamGDBRemote escaped_response;
- escaped_response.PutEscapedBytes(response.GetData(), response.GetSize());
- return SendPacketNoLock(escaped_response.GetString());
+ if (Expected<std::vector<uint8_t>> bytes =
+ m_debugged_process_up->TraceGetBinaryData(*request)) {
+ StreamGDBRemote response;
+ response.PutEscapedBytes(bytes->data(), bytes->size());
+ return SendPacketNoLock(response.GetString());
+ } else
+ return SendErrorResponse(bytes.takeError());
}
GDBRemoteCommunication::PacketResult
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
index a35932680d165..ef7d4471acf6c 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -167,15 +167,15 @@ class GDBRemoteCommunicationServerLLGS
PacketResult Handle_QSaveRegisterState(StringExtractorGDBRemote &packet);
- PacketResult Handle_jTraceStart(StringExtractorGDBRemote &packet);
+ PacketResult Handle_jLLDBTraceSupported(StringExtractorGDBRemote &packet);
- PacketResult Handle_jTraceRead(StringExtractorGDBRemote &packet);
+ PacketResult Handle_jLLDBTraceStart(StringExtractorGDBRemote &packet);
- PacketResult Handle_jTraceStop(StringExtractorGDBRemote &packet);
+ PacketResult Handle_jLLDBTraceStop(StringExtractorGDBRemote &packet);
- PacketResult Handle_jTraceConfigRead(StringExtractorGDBRemote &packet);
+ PacketResult Handle_jLLDBTraceGetState(StringExtractorGDBRemote &packet);
- PacketResult Handle_jLLDBTraceSupportedType(StringExtractorGDBRemote &packet);
+ PacketResult Handle_jLLDBTraceGetBinaryData(StringExtractorGDBRemote &packet);
PacketResult Handle_QRestoreRegisterState(StringExtractorGDBRemote &packet);
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 4a8eb07778862..7363c156da0bc 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -1203,34 +1203,26 @@ Status ProcessGDBRemote::DoAttachToProcessWithName(
return error;
}
-lldb::user_id_t ProcessGDBRemote::StartTrace(const TraceOptions &options,
- Status &error) {
- return m_gdb_comm.SendStartTracePacket(options, error);
+llvm::Expected<TraceSupportedResponse> ProcessGDBRemote::TraceSupported() {
+ return m_gdb_comm.SendTraceSupported();
}
-Status ProcessGDBRemote::StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id) {
- return m_gdb_comm.SendStopTracePacket(uid, thread_id);
+llvm::Error ProcessGDBRemote::TraceStop(const TraceStopRequest &request) {
+ return m_gdb_comm.SendTraceStop(request);
}
-Status ProcessGDBRemote::GetData(lldb::user_id_t uid, lldb::tid_t thread_id,
- llvm::MutableArrayRef<uint8_t> &buffer,
- size_t offset) {
- return m_gdb_comm.SendGetDataPacket(uid, thread_id, buffer, offset);
+llvm::Error ProcessGDBRemote::TraceStart(const llvm::json::Value &request) {
+ return m_gdb_comm.SendTraceStart(request);
}
-Status ProcessGDBRemote::GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id,
- llvm::MutableArrayRef<uint8_t> &buffer,
- size_t offset) {
- return m_gdb_comm.SendGetMetaDataPacket(uid, thread_id, buffer, offset);
+llvm::Expected<std::string>
+ProcessGDBRemote::TraceGetState(llvm::StringRef type) {
+ return m_gdb_comm.SendTraceGetState(type);
}
-Status ProcessGDBRemote::GetTraceConfig(lldb::user_id_t uid,
- TraceOptions &options) {
- return m_gdb_comm.SendGetTraceConfigPacket(uid, options);
-}
-
-llvm::Expected<TraceTypeInfo> ProcessGDBRemote::GetSupportedTraceType() {
- return m_gdb_comm.SendGetSupportedTraceType();
+llvm::Expected<std::vector<uint8_t>>
+ProcessGDBRemote::TraceGetBinaryData(const TraceGetBinaryDataRequest &request) {
+ return m_gdb_comm.SendTraceGetBinaryData(request);
}
void ProcessGDBRemote::DidExit() {
@@ -1906,6 +1898,9 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo(
thread_sp->SetStopInfo(
StopInfo::CreateStopReasonWithExec(*thread_sp));
handled = true;
+ } else if (reason == "processor trace") {
+ thread_sp->SetStopInfo(StopInfo::CreateStopReasonProcessorTrace(
+ *thread_sp, description.c_str()));
}
} else if (!signo) {
addr_t pc = thread_sp->GetRegisterContext()->GetPC();
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index 9c74369fd6c59..aafe1c3fc7aa8 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -165,22 +165,16 @@ class ProcessGDBRemote : public Process,
Status GetWatchpointSupportInfo(uint32_t &num) override;
- lldb::user_id_t StartTrace(const TraceOptions &options,
- Status &error) override;
+ llvm::Expected<TraceSupportedResponse> TraceSupported() override;
- Status StopTrace(lldb::user_id_t uid, lldb::tid_t thread_id) override;
+ llvm::Error TraceStop(const TraceStopRequest &request) override;
- Status GetData(lldb::user_id_t uid, lldb::tid_t thread_id,
- llvm::MutableArrayRef<uint8_t> &buffer,
- size_t offset = 0) override;
+ llvm::Error TraceStart(const llvm::json::Value &request) override;
- Status GetMetaData(lldb::user_id_t uid, lldb::tid_t thread_id,
- llvm::MutableArrayRef<uint8_t> &buffer,
- size_t offset = 0) override;
+ llvm::Expected<std::string> TraceGetState(llvm::StringRef type) override;
- llvm::Expected<TraceTypeInfo> GetSupportedTraceType() override;
-
- Status GetTraceConfig(lldb::user_id_t uid, TraceOptions &options) override;
+ llvm::Expected<std::vector<uint8_t>>
+ TraceGetBinaryData(const TraceGetBinaryDataRequest &request) override;
Status GetWatchpointSupportInfo(uint32_t &num, bool &after) override;
diff --git a/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp
index e1758dfcfc415..a21e5a2f04fc0 100644
--- a/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp
+++ b/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;
@@ -16,10 +18,12 @@ using namespace lldb_private;
using namespace lldb_private::trace_intel_pt;
using namespace llvm;
+// CommandObjectThreadTraceStartIntelPT
+
#define LLDB_OPTIONS_thread_trace_start_intel_pt
#include "TraceIntelPTCommandOptions.inc"
-Status CommandObjectTraceStartIntelPT::CommandOptions::SetOptionValue(
+Status CommandObjectThreadTraceStartIntelPT::CommandOptions::SetOptionValue(
uint32_t option_idx, llvm::StringRef option_arg,
ExecutionContext *execution_context) {
Status error;
@@ -27,23 +31,73 @@ Status CommandObjectTraceStartIntelPT::CommandOptions::SetOptionValue(
switch (short_option) {
case 's': {
- int32_t size_in_kb;
- if (option_arg.empty() || option_arg.getAsInteger(0, size_in_kb) ||
- size_in_kb < 0)
+ int64_t thread_buffer_size;
+ if (option_arg.empty() || option_arg.getAsInteger(0, thread_buffer_size) ||
+ thread_buffer_size < 0)
error.SetErrorStringWithFormat("invalid integer value for option '%s'",
option_arg.str().c_str());
else
- m_size_in_kb = size_in_kb;
+ m_thread_buffer_size = thread_buffer_size;
break;
}
- case 'c': {
- int32_t custom_config;
- if (option_arg.empty() || option_arg.getAsInteger(0, custom_config) ||
- custom_config < 0)
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+}
+
+void CommandObjectThreadTraceStartIntelPT::CommandOptions::
+ OptionParsingStarting(ExecutionContext *execution_context) {
+ m_thread_buffer_size = 4 * 1024; // 4KB
+}
+
+llvm::ArrayRef<OptionDefinition>
+CommandObjectThreadTraceStartIntelPT::CommandOptions::GetDefinitions() {
+ return llvm::makeArrayRef(g_thread_trace_start_intel_pt_options);
+}
+
+bool CommandObjectThreadTraceStartIntelPT::DoExecuteOnThreads(
+ Args &command, CommandReturnObject &result,
+ const std::vector<lldb::tid_t> &tids) {
+ if (Error err = m_trace.Start(tids, m_options.m_thread_buffer_size))
+ result.SetError(toString(std::move(err)));
+ else
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ return result.Succeeded();
+}
+
+/// CommandObjectProcessTraceStartIntelPT
+
+#define LLDB_OPTIONS_process_trace_start_intel_pt
+#include "TraceIntelPTCommandOptions.inc"
+
+Status CommandObjectProcessTraceStartIntelPT::CommandOptions::SetOptionValue(
+ uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 's': {
+ int64_t thread_buffer_size;
+ if (option_arg.empty() || option_arg.getAsInteger(0, thread_buffer_size) ||
+ thread_buffer_size < 0)
error.SetErrorStringWithFormat("invalid integer value for option '%s'",
option_arg.str().c_str());
else
- m_custom_config = custom_config;
+ m_thread_buffer_size = thread_buffer_size;
+ break;
+ }
+ case 'l': {
+ int64_t process_buffer_size_limit;
+ if (option_arg.empty() ||
+ option_arg.getAsInteger(0, process_buffer_size_limit) ||
+ process_buffer_size_limit < 0)
+ error.SetErrorStringWithFormat("invalid integer value for option '%s'",
+ option_arg.str().c_str());
+ else
+ m_process_buffer_size_limit = process_buffer_size_limit;
break;
}
default:
@@ -52,22 +106,24 @@ Status CommandObjectTraceStartIntelPT::CommandOptions::SetOptionValue(
return error;
}
-void CommandObjectTraceStartIntelPT::CommandOptions::OptionParsingStarting(
- ExecutionContext *execution_context) {
- m_size_in_kb = 4;
- m_custom_config = 0;
+void CommandObjectProcessTraceStartIntelPT::CommandOptions::
+ OptionParsingStarting(ExecutionContext *execution_context) {
+ m_thread_buffer_size = 4 * 1024; // 4KB
+ m_process_buffer_size_limit = 5 * 1024 * 1024; // 500MB
}
llvm::ArrayRef<OptionDefinition>
-CommandObjectTraceStartIntelPT::CommandOptions::GetDefinitions() {
- return llvm::makeArrayRef(g_thread_trace_start_intel_pt_options);
+CommandObjectProcessTraceStartIntelPT::CommandOptions::GetDefinitions() {
+ return llvm::makeArrayRef(g_process_trace_start_intel_pt_options);
}
-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);
+bool CommandObjectProcessTraceStartIntelPT::DoExecute(
+ Args &command, CommandReturnObject &result) {
+ if (Error err = m_trace.Start(m_options.m_thread_buffer_size,
+ m_options.m_process_buffer_size_limit))
+ result.SetError(toString(std::move(err)));
+ else
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
return result.Succeeded();
}
diff --git a/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h
index 265569c553fa3..7049e4ecbf406 100644
--- a/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h
+++ b/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h
@@ -9,21 +9,21 @@
#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_COMMANDOBJECTTRACESTARTINTELPT_H
#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_COMMANDOBJECTTRACESTARTINTELPT_H
-#include "../../../../source/Commands/CommandObjectThreadUtil.h"
+#include "../../../../source/Commands/CommandObjectTrace.h"
+#include "TraceIntelPT.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandReturnObject.h"
namespace lldb_private {
namespace trace_intel_pt {
-class CommandObjectTraceStartIntelPT : public CommandObjectIterateOverThreads {
+class CommandObjectThreadTraceStartIntelPT
+ : public CommandObjectMultipleThreads {
public:
class CommandOptions : public Options {
public:
CommandOptions() : Options() { OptionParsingStarting(nullptr); }
- ~CommandOptions() override = default;
-
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
ExecutionContext *execution_context) override;
@@ -31,31 +31,72 @@ class CommandObjectTraceStartIntelPT : public CommandObjectIterateOverThreads {
llvm::ArrayRef<OptionDefinition> GetDefinitions() override;
- size_t m_size_in_kb;
- uint32_t m_custom_config;
+ size_t m_thread_buffer_size;
};
- CommandObjectTraceStartIntelPT(CommandInterpreter &interpreter)
- : CommandObjectIterateOverThreads(
+ CommandObjectThreadTraceStartIntelPT(TraceIntelPT &trace,
+ CommandInterpreter &interpreter)
+ : CommandObjectMultipleThreads(
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 "
- "all threads.",
+ "all threads including future threads.",
"thread trace start [<thread-index> <thread-index> ...] "
"[<intel-pt-options>]",
lldb::eCommandRequiresProcess | lldb::eCommandTryTargetAPILock |
lldb::eCommandProcessMustBeLaunched |
lldb::eCommandProcessMustBePaused),
- m_options() {}
+ m_trace(trace), m_options() {}
+
+ Options *GetOptions() override { return &m_options; }
+
+protected:
+ bool DoExecuteOnThreads(Args &command, CommandReturnObject &result,
+ const std::vector<lldb::tid_t> &tids) override;
+
+ TraceIntelPT &m_trace;
+ CommandOptions m_options;
+};
+
+class CommandObjectProcessTraceStartIntelPT : public CommandObjectParsed {
+public:
+ class CommandOptions : public Options {
+ public:
+ CommandOptions() : Options() { OptionParsingStarting(nullptr); }
- ~CommandObjectTraceStartIntelPT() override = default;
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override;
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override;
+
+ size_t m_thread_buffer_size;
+ size_t m_process_buffer_size_limit;
+ };
+
+ CommandObjectProcessTraceStartIntelPT(TraceIntelPT &trace,
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "process trace start",
+ "Start tracing this process with intel-pt, including future "
+ "threads. "
+ "This is implemented by tracing each thread independently. "
+ "Threads traced with the \"thread trace start\" command are left "
+ "unaffected ant not retraced.",
+ "process trace start [<intel-pt-options>]",
+ lldb::eCommandRequiresProcess | lldb::eCommandTryTargetAPILock |
+ lldb::eCommandProcessMustBeLaunched |
+ lldb::eCommandProcessMustBePaused),
+ m_trace(trace), m_options() {}
Options *GetOptions() override { return &m_options; }
protected:
- bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override;
+ bool DoExecute(Args &command, CommandReturnObject &result) override;
+ TraceIntelPT &m_trace;
CommandOptions m_options;
};
diff --git a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
index 6b8b065640522..c65fa692e7d67 100644
--- a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
@@ -62,3 +62,8 @@ size_t DecodedThread::SetCursorPosition(size_t new_position) {
m_position = std::min(new_position, GetLastPosition());
return m_position;
}
+
+DecodedThread::DecodedThread(Error error) {
+ m_instructions.emplace_back(std::move(error));
+ m_position = GetLastPosition();
+}
diff --git a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
index 3c7e030414cb2..aabd85bcfd314 100644
--- a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
+++ b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
@@ -15,6 +15,7 @@
#include "llvm/Support/Error.h"
#include "lldb/Target/Trace.h"
+#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
#include "intel-pt.h"
@@ -111,6 +112,10 @@ class DecodedThread {
: m_instructions(std::move(instructions)), m_position(GetLastPosition()) {
}
+ /// Constructor with a single error signaling a complete failure of the
+ /// decoding process.
+ DecodedThread(llvm::Error error);
+
/// Get the instructions from the decoded trace. Some of them might indicate
/// errors (i.e. gaps) in the trace.
///
diff --git a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp b/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
index b6e8ae808632e..5d9aa0a1c743c 100644
--- a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
@@ -1,5 +1,4 @@
-//===-- IntelPTDecoder.cpp --------------------------------------*- C++ -*-===//
-//
+//===-- IntelPTDecoder.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
@@ -10,10 +9,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/Target/ThreadPostMortemTrace.h"
+#include "lldb/Utility/StringExtractor.h"
using namespace lldb;
using namespace lldb_private;
@@ -85,7 +86,7 @@ static int ProcessPTEvents(pt_insn_decoder &decoder, int errcode) {
return errcode;
}
return 0;
-};
+}
/// Decode all the instructions from a configured decoder.
/// The decoding flow is based on
@@ -158,39 +159,26 @@ static int ReadProcessMemory(uint8_t *buffer, size_t size,
return bytes_read;
}
-static std::vector<IntelPTInstruction> makeInstructionListFromError(Error err) {
- std::vector<IntelPTInstruction> instructions;
- instructions.emplace_back(std::move(err));
- 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;
+static Expected<std::vector<IntelPTInstruction>>
+DecodeInMemoryTrace(Process &process, TraceIntelPT &trace_intel_pt,
+ MutableArrayRef<uint8_t> buffer) {
+ Expected<pt_cpu> cpu_info = trace_intel_pt.GetCPUInfo();
+ if (!cpu_info)
+ return cpu_info.takeError();
pt_config config;
pt_config_init(&config);
- config.cpu = pt_cpu;
+ config.cpu = *cpu_info;
if (int errcode = pt_cpu_errata(&config.errata, &config.cpu))
- return makeInstructionListFromError(make_error<IntelPTError>(errcode));
+ return 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)
- return makeInstructionListFromError(make_error<IntelPTError>(-pte_nomem));
+ return make_error<IntelPTError>(-pte_nomem);
pt_image *image = pt_insn_get_image(decoder);
@@ -204,12 +192,62 @@ CreateDecoderAndDecode(Process &process, const pt_cpu &pt_cpu,
return instructions;
}
-const DecodedThread &ThreadTraceDecoder::Decode() {
- if (!m_decoded_thread.hasValue()) {
- m_decoded_thread = DecodedThread(
- CreateDecoderAndDecode(*m_trace_thread->GetProcess(), m_pt_cpu,
- m_trace_thread->GetTraceFile()));
- }
+static Expected<std::vector<IntelPTInstruction>>
+DecodeTraceFile(Process &process, TraceIntelPT &trace_intel_pt,
+ 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 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 DecodeInMemoryTrace(process, trace_intel_pt, trace_data);
+}
+
+static Expected<std::vector<IntelPTInstruction>>
+DecodeLiveThread(Thread &thread, TraceIntelPT &trace) {
+ Expected<std::vector<uint8_t>> buffer =
+ trace.GetLiveThreadBuffer(thread.GetID());
+ if (!buffer)
+ return buffer.takeError();
+ if (Expected<pt_cpu> cpu_info = trace.GetCPUInfo())
+ return DecodeInMemoryTrace(*thread.GetProcess(), trace,
+ MutableArrayRef<uint8_t>(*buffer));
+ else
+ return cpu_info.takeError();
+}
+
+const DecodedThread &ThreadDecoder::Decode() {
+ if (!m_decoded_thread.hasValue())
+ m_decoded_thread = DoDecode();
return *m_decoded_thread;
}
+
+PostMortemThreadDecoder::PostMortemThreadDecoder(
+ const lldb::ThreadPostMortemTraceSP &trace_thread, TraceIntelPT &trace)
+ : m_trace_thread(trace_thread), m_trace(trace) {}
+
+DecodedThread PostMortemThreadDecoder::DoDecode() {
+ if (Expected<std::vector<IntelPTInstruction>> instructions =
+ DecodeTraceFile(*m_trace_thread->GetProcess(), m_trace,
+ m_trace_thread->GetTraceFile()))
+ return DecodedThread(std::move(*instructions));
+ else
+ return DecodedThread(instructions.takeError());
+}
+
+LiveThreadDecoder::LiveThreadDecoder(Thread &thread, TraceIntelPT &trace)
+ : m_thread_sp(thread.shared_from_this()), m_trace(trace) {}
+
+DecodedThread LiveThreadDecoder::DoDecode() {
+ if (Expected<std::vector<IntelPTInstruction>> instructions =
+ DecodeLiveThread(*m_thread_sp, m_trace))
+ return DecodedThread(std::move(*instructions));
+ else
+ return DecodedThread(instructions.takeError());
+}
diff --git a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h b/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
index 2e67f9bf6da70..3768a8d80a2bf 100644
--- a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
+++ b/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
@@ -12,40 +12,75 @@
#include "intel-pt.h"
#include "DecodedThread.h"
+#include "forward-declarations.h"
#include "lldb/Target/Process.h"
#include "lldb/Utility/FileSpec.h"
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 caches the result.
+class 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.
- 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() {}
+ virtual ~ThreadDecoder() = default;
+
+ ThreadDecoder() = default;
- /// Decode the thread and store the result internally.
+ /// Decode the thread and store the result internally, to avoid
+ /// recomputations.
///
/// \return
/// A \a DecodedThread instance.
const DecodedThread &Decode();
-private:
- ThreadTraceDecoder(const ThreadTraceDecoder &other) = delete;
- ThreadTraceDecoder &operator=(const ThreadTraceDecoder &other) = delete;
+ ThreadDecoder(const ThreadDecoder &other) = delete;
+ ThreadDecoder &operator=(const ThreadDecoder &other) = delete;
+
+protected:
+ /// Decode the thread.
+ ///
+ /// \return
+ /// A \a DecodedThread instance.
+ virtual DecodedThread DoDecode() = 0;
- std::shared_ptr<ThreadTrace> m_trace_thread;
- pt_cpu m_pt_cpu;
llvm::Optional<DecodedThread> m_decoded_thread;
};
+/// Decoder implementation for \a lldb_private::ThreadPostMortemTrace, which are
+/// non-live processes that come trace session files.
+class PostMortemThreadDecoder : public ThreadDecoder {
+public:
+ /// \param[in] trace_thread
+ /// The thread whose trace file will be decoded.
+ ///
+ /// \param[in] trace
+ /// The main Trace object who owns this decoder and its data.
+ PostMortemThreadDecoder(const lldb::ThreadPostMortemTraceSP &trace_thread,
+ TraceIntelPT &trace);
+
+private:
+ DecodedThread DoDecode() override;
+
+ lldb::ThreadPostMortemTraceSP m_trace_thread;
+ TraceIntelPT &m_trace;
+};
+
+class LiveThreadDecoder : public ThreadDecoder {
+public:
+ /// \param[in] thread
+ /// The thread whose traces will be decoded.
+ ///
+ /// \param[in] trace
+ /// The main Trace object who owns this decoder and its data.
+ LiveThreadDecoder(Thread &thread, TraceIntelPT &trace);
+
+private:
+ DecodedThread DoDecode() override;
+
+ lldb::ThreadSP m_thread_sp;
+ TraceIntelPT &m_trace;
+};
+
} // namespace trace_intel_pt
} // namespace lldb_private
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
index 63a8b2dff4a76..96c4a89bd1cb8 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -13,7 +13,7 @@
#include "lldb/Core/PluginManager.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"
-#include "lldb/Target/ThreadTrace.h"
+#include "lldb/Target/ThreadPostMortemTrace.h"
using namespace lldb;
using namespace lldb_private;
@@ -22,18 +22,27 @@ using namespace llvm;
LLDB_PLUGIN_DEFINE(TraceIntelPT)
-CommandObjectSP GetStartCommand(CommandInterpreter &interpreter) {
- return CommandObjectSP(new CommandObjectTraceStartIntelPT(interpreter));
+lldb::CommandObjectSP
+TraceIntelPT::GetProcessTraceStartCommand(CommandInterpreter &interpreter) {
+ return CommandObjectSP(
+ new CommandObjectProcessTraceStartIntelPT(*this, interpreter));
+}
+
+lldb::CommandObjectSP
+TraceIntelPT::GetThreadTraceStartCommand(CommandInterpreter &interpreter) {
+ return CommandObjectSP(
+ new CommandObjectThreadTraceStartIntelPT(*this, interpreter));
}
void TraceIntelPT::Initialize() {
- PluginManager::RegisterPlugin(
- GetPluginNameStatic(), "Intel Processor Trace", CreateInstance,
- TraceIntelPTSessionFileParser::GetSchema(), GetStartCommand);
+ PluginManager::RegisterPlugin(GetPluginNameStatic(), "Intel Processor Trace",
+ CreateInstanceForSessionFile,
+ CreateInstanceForLiveProcess,
+ TraceIntelPTSessionFileParser::GetSchema());
}
void TraceIntelPT::Terminate() {
- PluginManager::UnregisterPlugin(CreateInstance);
+ PluginManager::UnregisterPlugin(CreateInstanceForSessionFile);
}
ConstString TraceIntelPT::GetPluginNameStatic() {
@@ -55,34 +64,41 @@ uint32_t TraceIntelPT::GetPluginVersion() { return 1; }
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();
}
+Expected<TraceSP> TraceIntelPT::CreateInstanceForLiveProcess(Process &process) {
+ TraceSP instance(new TraceIntelPT(process));
+ 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));
-}
-
-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())
+ const pt_cpu &cpu_info,
+ const std::vector<ThreadPostMortemTraceSP> &traced_threads)
+ : m_cpu_info(cpu_info) {
+ for (const ThreadPostMortemTraceSP &thread : traced_threads)
+ m_thread_decoders.emplace(
+ thread.get(), std::make_unique<PostMortemThreadDecoder>(thread, *this));
+}
+
+const DecodedThread *TraceIntelPT::Decode(Thread &thread) {
+ RefreshLiveProcessState();
+ if (m_failed_live_threads_decoder.hasValue())
+ return &*m_failed_live_threads_decoder;
+
+ 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) {
+size_t TraceIntelPT::GetCursorPosition(Thread &thread) {
const DecodedThread *decoded_thread = Decode(thread);
if (!decoded_thread)
return 0;
@@ -90,7 +106,7 @@ size_t TraceIntelPT::GetCursorPosition(const Thread &thread) {
}
void TraceIntelPT::TraverseInstructions(
- const Thread &thread, size_t position, TraceDirection direction,
+ Thread &thread, size_t position, TraceDirection direction,
std::function<bool(size_t index, Expected<lldb::addr_t> load_addr)>
callback) {
const DecodedThread *decoded_thread = Decode(thread);
@@ -106,9 +122,117 @@ void TraceIntelPT::TraverseInstructions(
break;
}
-size_t TraceIntelPT::GetInstructionCount(const Thread &thread) {
+size_t TraceIntelPT::GetInstructionCount(Thread &thread) {
if (const DecodedThread *decoded_thread = Decode(thread))
return decoded_thread->GetInstructions().size();
else
return 0;
}
+
+Expected<pt_cpu> TraceIntelPT::GetCPUInfoForLiveProcess() {
+ Expected<std::vector<uint8_t>> cpu_info = GetLiveProcessBinaryData("cpuInfo");
+ if (!cpu_info)
+ return cpu_info.takeError();
+
+ int64_t cpu_family = -1;
+ int64_t model = -1;
+ int64_t stepping = -1;
+ std::string vendor_id;
+
+ StringRef rest(reinterpret_cast<const char *>(cpu_info->data()),
+ cpu_info->size());
+ while (!rest.empty()) {
+ StringRef line;
+ std::tie(line, rest) = rest.split('\n');
+
+ SmallVector<StringRef, 2> columns;
+ line.split(columns, StringRef(":"), -1, false);
+
+ if (columns.size() < 2)
+ continue; // continue searching
+
+ columns[1] = columns[1].trim(" ");
+ if (columns[0].contains("cpu family") &&
+ columns[1].getAsInteger(10, cpu_family))
+ continue;
+
+ else if (columns[0].contains("model") && columns[1].getAsInteger(10, model))
+ continue;
+
+ else if (columns[0].contains("stepping") &&
+ columns[1].getAsInteger(10, stepping))
+ continue;
+
+ else if (columns[0].contains("vendor_id")) {
+ vendor_id = columns[1].str();
+ if (!vendor_id.empty())
+ continue;
+ }
+
+ if ((cpu_family != -1) && (model != -1) && (stepping != -1) &&
+ (!vendor_id.empty())) {
+ return pt_cpu{vendor_id == "GenuineIntel" ? pcv_intel : pcv_unknown,
+ static_cast<uint16_t>(cpu_family),
+ static_cast<uint8_t>(model),
+ static_cast<uint8_t>(stepping)};
+ }
+ }
+ return createStringError(inconvertibleErrorCode(),
+ "Failed parsing the target's /proc/cpuinfo file");
+}
+
+Expected<pt_cpu> TraceIntelPT::GetCPUInfo() {
+ if (!m_cpu_info) {
+ if (llvm::Expected<pt_cpu> cpu_info = GetCPUInfoForLiveProcess())
+ m_cpu_info = *cpu_info;
+ else
+ return cpu_info.takeError();
+ }
+ return *m_cpu_info;
+}
+
+void TraceIntelPT::DoRefreshLiveProcessState(
+ Expected<TraceGetStateResponse> state) {
+ m_thread_decoders.clear();
+
+ if (!state) {
+ m_failed_live_threads_decoder = DecodedThread(state.takeError());
+ return;
+ }
+
+ for (const TraceThreadState &thread_state : state->tracedThreads) {
+ Thread &thread =
+ *m_live_process->GetThreadList().FindThreadByID(thread_state.tid);
+ m_thread_decoders.emplace(
+ &thread, std::make_unique<LiveThreadDecoder>(thread, *this));
+ }
+}
+
+bool TraceIntelPT::IsTraced(const Thread &thread) {
+ return m_thread_decoders.count(&thread);
+}
+
+Error TraceIntelPT::Start(size_t thread_buffer_size,
+ size_t total_buffer_size_limit) {
+ TraceIntelPTStartRequest request;
+ request.threadBufferSize = thread_buffer_size;
+ request.processBufferSizeLimit = total_buffer_size_limit;
+ request.type = GetPluginName().AsCString();
+ return Trace::Start(toJSON(request));
+}
+
+llvm::Error TraceIntelPT::Start(const std::vector<lldb::tid_t> &tids,
+ size_t thread_buffer_size) {
+ TraceIntelPTStartRequest request;
+ request.threadBufferSize = thread_buffer_size;
+ request.type = GetPluginName().AsCString();
+ request.tids.emplace();
+ for (lldb::tid_t tid : tids)
+ request.tids->push_back(tid);
+ return Trace::Start(toJSON(request));
+}
+
+Expected<std::vector<uint8_t>>
+TraceIntelPT::GetLiveThreadBuffer(lldb::tid_t tid) {
+ return Trace::GetLiveThreadBinaryData(tid, "threadTraceBuffer");
+}
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
index 5058e6fd32f24..f19e516498e9f 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -45,33 +45,93 @@ class TraceIntelPT : public Trace {
/// \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();
uint32_t GetPluginVersion() override;
/// \}
+ lldb::CommandObjectSP
+ GetProcessTraceStartCommand(CommandInterpreter &interpreter) override;
+
+ lldb::CommandObjectSP
+ GetThreadTraceStartCommand(CommandInterpreter &interpreter) override;
+
llvm::StringRef GetSchema() override;
void TraverseInstructions(
- const Thread &thread, size_t position, TraceDirection direction,
+ Thread &thread, size_t position, TraceDirection direction,
std::function<bool(size_t index, llvm::Expected<lldb::addr_t> load_addr)>
callback) override;
- size_t GetInstructionCount(const Thread &thread) override;
+ size_t GetInstructionCount(Thread &thread) override;
+
+ size_t GetCursorPosition(Thread &thread) override;
+
+ void DoRefreshLiveProcessState(
+ llvm::Expected<TraceGetStateResponse> state) override;
+
+ bool IsTraced(const Thread &thread) override;
+
+ /// Start tracing a live process.
+ ///
+ /// \param[in] thread_buffer_size
+ /// Trace size per thread in bytes.
+ ///
+ /// \param[in] total_buffer_size_limit
+ /// 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 threads traced explicitly.
+ ///
+ /// Whenever a thread is attempted to be traced due to this operation and
+ /// the limit would be reached, the process is stopped with a "tracing"
+ /// reason, so that the user can retrace the process if needed.
+ ///
+ /// \return
+ /// \a llvm::Error::success if the operation was successful, or
+ /// \a llvm::Error otherwise.
+ llvm::Error Start(size_t thread_buffer_size, size_t total_buffer_size_limit);
+
+ /// Start tracing a live threads.
+ ///
+ /// \param[in] tids
+ /// Threads to trace.
+ ///
+ /// \param[in] thread_buffer_size
+ /// Trace size per thread in bytes.
+ ///
+ /// \return
+ /// \a llvm::Error::success if the operation was successful, or
+ /// \a llvm::Error otherwise.
+ llvm::Error Start(const std::vector<lldb::tid_t> &tids,
+ size_t thread_buffer_size);
+
+ /// Get the thread buffer content for a live thread
+ llvm::Expected<std::vector<uint8_t>> GetLiveThreadBuffer(lldb::tid_t tid);
- size_t GetCursorPosition(const Thread &thread) override;
+ llvm::Expected<pt_cpu> GetCPUInfo();
private:
friend class TraceIntelPTSessionFileParser;
+ llvm::Expected<pt_cpu> GetCPUInfoForLiveProcess();
+
/// \param[in] trace_threads
/// ThreadTrace instances, which are not live-processes and whose trace
/// files are fixed.
- TraceIntelPT(const pt_cpu &pt_cpu,
- const std::vector<std::shared_ptr<ThreadTrace>> &traced_threads);
+ TraceIntelPT(
+ const pt_cpu &cpu_info,
+ const std::vector<lldb::ThreadPostMortemTraceSP> &traced_threads);
+
+ /// Constructor for live processes
+ TraceIntelPT(Process &live_process)
+ : Trace(live_process), 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.
@@ -83,11 +143,15 @@ class TraceIntelPT : public Trace {
/// \return
/// A \a DecodedThread instance if decoding was successful, or a \b
/// nullptr if the thread's trace is not managed by this class.
- 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;
+ const DecodedThread *Decode(Thread &thread);
+
+ /// It is provided by either a session file or a live process' "cpuInfo"
+ /// binary data.
+ llvm::Optional<pt_cpu> m_cpu_info;
+ std::map<const Thread *, std::unique_ptr<ThreadDecoder>> m_thread_decoders;
+ /// Dummy DecodedThread used when decoding threads after there were errors
+ /// when refreshing the live process state.
+ llvm::Optional<DecodedThread> m_failed_live_threads_decoder;
};
} // namespace trace_intel_pt
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
index 6ffe949dbe7bc..0dd835bac649c 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
@@ -4,13 +4,25 @@ let Command = "thread trace start intel pt" in {
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<"Trace size in bytes per thread. It must be a power of 2 greater "
+ "than or equal to 4096 (2^12). The trace is circular keeping "
+ "the most recent data. Defaults to 4096 bytes.">;
+}
+
+let Command = "process trace start intel pt" in {
+ def process_trace_start_intel_pt_thread_size: Option<"thread-size", "s">,
+ Group<1>,
+ Arg<"Value">,
+ Desc<"Trace size in bytes per thread. It must be a power of 2 greater "
+ "than or equal to 4096 (2^12). The trace is circular keeping "
+ "the most recent data. Defaults to 4096 bytes.">;
+ def process_trace_start_intel_pt_process_size_limit: Option<"total-size-limit", "l">,
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/*`. "
- "See https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/perf-intel-pt.txt"
- " for more information. Defaults to 0.">;
+ 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. "
+ "Whenever a thread is attempted to be traced due to this command and "
+ "the limit would be reached, the process is stopped with a \"tracing\" "
+ "reason, so that the user can retrace the process if needed.">;
}
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
index beef5c3968eae..2883381422632 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
@@ -12,7 +12,8 @@
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/ThreadList.h"
-#include "lldb/Target/ThreadTrace.h"
+#include "lldb/Target/ThreadPostMortemTrace.h"
+#include "lldb/Utility/TraceOptions.h"
using namespace lldb;
using namespace lldb_private;
@@ -24,7 +25,7 @@ StringRef TraceIntelPTSessionFileParser::GetSchema() {
if (schema.empty()) {
schema = TraceSessionFileParser::BuildSchema(R"({
"type": "intel-pt",
- "pt_cpu": {
+ "cpuInfo": {
"vendor": "intel" | "unknown",
"family": integer,
"model": integer,
@@ -35,21 +36,22 @@ StringRef TraceIntelPTSessionFileParser::GetSchema() {
return schema;
}
-pt_cpu TraceIntelPTSessionFileParser::ParsePTCPU(const JSONPTCPU &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),
- static_cast<uint8_t>(pt_cpu.stepping)};
+pt_cpu TraceIntelPTSessionFileParser::ParsePTCPU(
+ const JSONTraceIntelPTCPUInfo &cpu_info) {
+ return {cpu_info.vendor.compare("intel") == 0 ? pcv_intel : pcv_unknown,
+ static_cast<uint16_t>(cpu_info.family),
+ static_cast<uint8_t>(cpu_info.model),
+ static_cast<uint8_t>(cpu_info.stepping)};
}
TraceSP TraceIntelPTSessionFileParser::CreateTraceIntelPTInstance(
- const pt_cpu &pt_cpu, std::vector<ParsedProcess> &parsed_processes) {
- std::vector<ThreadTraceSP> threads;
+ const pt_cpu &cpu_info, std::vector<ParsedProcess> &parsed_processes) {
+ std::vector<ThreadPostMortemTraceSP> threads;
for (const ParsedProcess &parsed_process : parsed_processes)
threads.insert(threads.end(), parsed_process.threads.begin(),
parsed_process.threads.end());
- TraceSP trace_instance(new TraceIntelPT(pt_cpu, threads));
+ TraceSP trace_instance(new TraceIntelPT(cpu_info, threads));
for (const ParsedProcess &parsed_process : parsed_processes)
parsed_process.target_sp->SetTrace(trace_instance);
@@ -64,7 +66,7 @@ Expected<TraceSP> TraceIntelPTSessionFileParser::Parse() {
if (Expected<std::vector<ParsedProcess>> parsed_processes =
ParseCommonSessionFile(session))
- return CreateTraceIntelPTInstance(ParsePTCPU(session.trace.pt_cpu),
+ return CreateTraceIntelPTInstance(ParsePTCPU(session.trace.cpuInfo),
*parsed_processes);
else
return parsed_processes.takeError();
@@ -73,25 +75,34 @@ Expected<TraceSP> TraceIntelPTSessionFileParser::Parse() {
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,
Path path) {
ObjectMapper o(value, path);
- return o && o.map("pt_cpu", plugin_settings.pt_cpu) &&
+ return o && o.map("cpuInfo", plugin_settings.cpuInfo) &&
fromJSON(
value,
(TraceSessionFileParser::JSONTracePluginSettings &)plugin_settings,
path);
}
+bool fromJSON(const json::Value &value,
+ TraceIntelPTSessionFileParser::JSONTraceIntelPTCPUInfo &cpu_info,
+ Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("vendor", cpu_info.vendor) &&
+ o.map("family", cpu_info.family) && o.map("model", cpu_info.model) &&
+ o.map("stepping", cpu_info.stepping);
+}
+
+Value toJSON(
+ const TraceIntelPTSessionFileParser::JSONTraceIntelPTCPUInfo &cpu_info) {
+ return Value(Object{{"family", cpu_info.family},
+ {"model", cpu_info.model},
+ {"stepping", cpu_info.stepping},
+ {"vendor", cpu_info.vendor}});
+}
+
} // namespace json
} // namespace llvm
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
index 6a896de09d000..ad902782ff68a 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
@@ -19,16 +19,16 @@ class TraceIntelPT;
class TraceIntelPTSessionFileParser : public TraceSessionFileParser {
public:
- struct JSONPTCPU {
- std::string vendor;
+ struct JSONTraceIntelPTCPUInfo {
int64_t family;
int64_t model;
int64_t stepping;
+ std::string vendor;
};
struct JSONTraceIntelPTSettings
: TraceSessionFileParser::JSONTracePluginSettings {
- JSONPTCPU pt_cpu;
+ JSONTraceIntelPTCPUInfo cpuInfo;
};
/// See \a TraceSessionFileParser::TraceSessionFileParser for the description
@@ -52,11 +52,11 @@ class TraceIntelPTSessionFileParser : public TraceSessionFileParser {
llvm::Expected<lldb::TraceSP> Parse();
lldb::TraceSP
- CreateTraceIntelPTInstance(const pt_cpu &pt_cpu,
+ CreateTraceIntelPTInstance(const pt_cpu &cpu_info,
std::vector<ParsedProcess> &parsed_processes);
private:
- pt_cpu ParsePTCPU(const JSONPTCPU &pt_cpu);
+ static pt_cpu ParsePTCPU(const JSONTraceIntelPTCPUInfo &cpu_info);
const llvm::json::Value &m_trace_session_file;
};
@@ -67,17 +67,20 @@ class TraceIntelPTSessionFileParser : public TraceSessionFileParser {
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,
Path path);
+bool fromJSON(const llvm::json::Value &value,
+ lldb_private::trace_intel_pt::TraceIntelPTSessionFileParser::
+ JSONTraceIntelPTCPUInfo &packet,
+ llvm::json::Path path);
+
+llvm::json::Value
+toJSON(const lldb_private::trace_intel_pt::TraceIntelPTSessionFileParser::
+ JSONTraceIntelPTCPUInfo &packet);
+
} // namespace json
} // namespace llvm
diff --git a/lldb/source/Plugins/Trace/intel-pt/forward-declarations.h b/lldb/source/Plugins/Trace/intel-pt/forward-declarations.h
new file mode 100644
index 0000000000000..3c5f811acc10e
--- /dev/null
+++ b/lldb/source/Plugins/Trace/intel-pt/forward-declarations.h
@@ -0,0 +1,20 @@
+//===-- forward-declarations.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 LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_FORWARD_DECLARATIONS_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_FORWARD_DECLARATIONS_H
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+class TraceIntelPT;
+class ThreadDecoder;
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_FORWARD_DECLARATIONS_H
diff --git a/lldb/source/Target/CMakeLists.txt b/lldb/source/Target/CMakeLists.txt
index 383ac5a6c53c2..ef92d26ef465d 100644
--- a/lldb/source/Target/CMakeLists.txt
+++ b/lldb/source/Target/CMakeLists.txt
@@ -66,7 +66,7 @@ add_lldb_library(lldbTarget
ThreadPlanTracer.cpp
ThreadPlanStack.cpp
ThreadSpec.cpp
- ThreadTrace.cpp
+ ThreadPostMortemTrace.cpp
Trace.cpp
TraceSessionFileParser.cpp
UnixSignals.cpp
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index 63673735ea836..6db410fa6bf0e 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -802,6 +802,7 @@ bool Process::HandleProcessStateChangedEvent(const EventSP &event_sp,
case eStopReasonExec:
case eStopReasonThreadExiting:
case eStopReasonInstrumentation:
+ case eStopReasonProcessorTrace:
if (!other_thread)
other_thread = thread;
break;
@@ -5958,7 +5959,7 @@ UtilityFunction *Process::GetLoadImageUtilityFunction(
return m_dlopen_utility_func_up.get();
}
-llvm::Expected<TraceTypeInfo> Process::GetSupportedTraceType() {
+llvm::Expected<TraceSupportedResponse> Process::TraceSupported() {
if (!IsLiveDebugSession())
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Can't trace a non-live process.");
diff --git a/lldb/source/Target/ProcessTrace.cpp b/lldb/source/Target/ProcessTrace.cpp
index 95f87745b7249..c878a2ac4eb9c 100644
--- a/lldb/source/Target/ProcessTrace.cpp
+++ b/lldb/source/Target/ProcessTrace.cpp
@@ -87,8 +87,6 @@ void ProcessTrace::RefreshStateAfterStop() {}
Status ProcessTrace::DoDestroy() { return Status(); }
-bool ProcessTrace::IsAlive() { return true; }
-
size_t ProcessTrace::ReadMemory(addr_t addr, void *buf, size_t size,
Status &error) {
// Don't allow the caching that lldb_private::Process::ReadMemory does since
diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp
index 95efd0a17127b..ae29b42e0e50c 100644
--- a/lldb/source/Target/StopInfo.cpp
+++ b/lldb/source/Target/StopInfo.cpp
@@ -1044,6 +1044,30 @@ class StopInfoException : public StopInfo {
}
};
+// StopInfoProcessorTrace
+
+class StopInfoProcessorTrace : public StopInfo {
+public:
+ StopInfoProcessorTrace(Thread &thread, const char *description)
+ : StopInfo(thread, LLDB_INVALID_UID) {
+ if (description)
+ SetDescription(description);
+ }
+
+ ~StopInfoProcessorTrace() override = default;
+
+ StopReason GetStopReason() const override {
+ return eStopReasonProcessorTrace;
+ }
+
+ const char *GetDescription() override {
+ if (m_description.empty())
+ return "processor trace event";
+ else
+ return m_description.c_str();
+ }
+};
+
// StopInfoThreadPlan
class StopInfoThreadPlan : public StopInfo {
@@ -1161,6 +1185,11 @@ StopInfoSP StopInfo::CreateStopReasonWithException(Thread &thread,
return StopInfoSP(new StopInfoException(thread, description));
}
+StopInfoSP StopInfo::CreateStopReasonProcessorTrace(Thread &thread,
+ const char *description) {
+ return StopInfoSP(new StopInfoProcessorTrace(thread, description));
+}
+
StopInfoSP StopInfo::CreateStopReasonWithExec(Thread &thread) {
return StopInfoSP(new StopInfoExec(thread));
}
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index c6667ce942cd0..7c62904302c90 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -3075,7 +3075,27 @@ Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) {
void Target::SetTrace(const TraceSP &trace_sp) { m_trace_sp = trace_sp; }
-const TraceSP &Target::GetTrace() { return m_trace_sp; }
+TraceSP &Target::GetTrace() { return m_trace_sp; }
+
+llvm::Expected<TraceSP &> Target::GetTraceOrCreate() {
+ if (!m_trace_sp && m_process_sp) {
+ llvm::Expected<TraceSupportedResponse> trace_type =
+ m_process_sp->TraceSupported();
+ if (!trace_type)
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(), "Tracing is not supported. %s",
+ llvm::toString(trace_type.takeError()).c_str());
+ if (llvm::Expected<TraceSP> trace_sp =
+ Trace::FindPluginForLiveProcess(trace_type->name, *m_process_sp))
+ m_trace_sp = *trace_sp;
+ else
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Couldn't start tracing the process. %s",
+ llvm::toString(trace_type.takeError()).c_str());
+ }
+ return m_trace_sp;
+}
Status Target::Attach(ProcessAttachInfo &attach_info, Stream *stream) {
auto state = eStateInvalid;
diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp
index e5271ccbe5148..57cfaddac6d23 100644
--- a/lldb/source/Target/Thread.cpp
+++ b/lldb/source/Target/Thread.cpp
@@ -1685,6 +1685,8 @@ std::string Thread::StopReasonAsString(lldb::StopReason reason) {
return "thread exiting";
case eStopReasonInstrumentation:
return "instrumentation break";
+ case eStopReasonProcessorTrace:
+ return "processor trace";
}
return "StopReason = " + std::to_string(reason);
diff --git a/lldb/source/Target/ThreadTrace.cpp b/lldb/source/Target/ThreadPostMortemTrace.cpp
similarity index 65%
rename from lldb/source/Target/ThreadTrace.cpp
rename to lldb/source/Target/ThreadPostMortemTrace.cpp
index 346f36a737d72..f8eb3e4f1f2d7 100644
--- a/lldb/source/Target/ThreadTrace.cpp
+++ b/lldb/source/Target/ThreadPostMortemTrace.cpp
@@ -1,4 +1,4 @@
-//===-- ThreadTrace.cpp ---------------------------------------------------===//
+//===-- ThreadPostMortemTrace.cpp -----------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-#include "lldb/Target/ThreadTrace.h"
+#include "lldb/Target/ThreadPostMortemTrace.h"
#include <memory>
@@ -17,9 +17,9 @@
using namespace lldb;
using namespace lldb_private;
-void ThreadTrace::RefreshStateAfterStop() {}
+void ThreadPostMortemTrace::RefreshStateAfterStop() {}
-RegisterContextSP ThreadTrace::GetRegisterContext() {
+RegisterContextSP ThreadPostMortemTrace::GetRegisterContext() {
if (!m_reg_context_sp)
m_reg_context_sp = CreateRegisterContextForFrame(nullptr);
@@ -27,13 +27,15 @@ RegisterContextSP ThreadTrace::GetRegisterContext() {
}
RegisterContextSP
-ThreadTrace::CreateRegisterContextForFrame(StackFrame *frame) {
+ThreadPostMortemTrace::CreateRegisterContextForFrame(StackFrame *frame) {
// Eventually this will calculate the register context based on the current
// trace position.
return std::make_shared<RegisterContextHistory>(
*this, 0, GetProcess()->GetAddressByteSize(), LLDB_INVALID_ADDRESS);
}
-bool ThreadTrace::CalculateStopInfo() { return false; }
+bool ThreadPostMortemTrace::CalculateStopInfo() { return false; }
-const FileSpec &ThreadTrace::GetTraceFile() const { return m_trace_file; }
+const FileSpec &ThreadPostMortemTrace::GetTraceFile() const {
+ return m_trace_file;
+}
diff --git a/lldb/source/Target/Trace.cpp b/lldb/source/Target/Trace.cpp
index d19115b2d1f9a..e862d17e73c51 100644
--- a/lldb/source/Target/Trace.cpp
+++ b/lldb/source/Target/Trace.cpp
@@ -16,6 +16,7 @@
#include "lldb/Target/Process.h"
#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPostMortemTrace.h"
#include "lldb/Utility/Stream.h"
using namespace lldb;
@@ -57,9 +58,10 @@ static Error createInvalidPlugInError(StringRef plugin_name) {
plugin_name.data());
}
-Expected<lldb::TraceSP> Trace::FindPlugin(Debugger &debugger,
- const json::Value &trace_session_file,
- StringRef session_file_dir) {
+Expected<lldb::TraceSP>
+Trace::FindPluginForPostMortemProcess(Debugger &debugger,
+ const json::Value &trace_session_file,
+ StringRef session_file_dir) {
JSONSimpleTraceSession json_session;
json::Path::Root root("traceSession");
if (!json::fromJSON(trace_session_file, json_session, root))
@@ -72,6 +74,20 @@ Expected<lldb::TraceSP> Trace::FindPlugin(Debugger &debugger,
return createInvalidPlugInError(json_session.trace.type);
}
+Expected<lldb::TraceSP>
+Trace::FindPluginForLiveProcess(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);
@@ -204,6 +220,12 @@ DumpInstructionInfo(Stream &s, const SymbolContext &sc,
void Trace::DumpTraceInstructions(Thread &thread, Stream &s, size_t count,
size_t end_position, bool raw) {
+ if (!IsTraced(thread)) {
+ s.Printf("thread #%u: tid = %" PRIu64 ", not traced\n", thread.GetIndexID(),
+ thread.GetID());
+ return;
+ }
+
size_t instructions_count = GetInstructionCount(thread);
s.Printf("thread #%u: tid = %" PRIu64 ", total instructions = %zu\n",
thread.GetIndexID(), thread.GetID(), instructions_count);
@@ -266,3 +288,121 @@ void Trace::DumpTraceInstructions(Thread &thread, Stream &s, size_t count,
return index < end_position;
});
}
+
+Error Trace::Start(const llvm::json::Value &request) {
+ if (!m_live_process)
+ return createStringError(inconvertibleErrorCode(),
+ "Tracing requires a live process.");
+ return m_live_process->TraceStart(request);
+}
+
+Error Trace::StopProcess() {
+ if (!m_live_process)
+ return createStringError(inconvertibleErrorCode(),
+ "Tracing requires a live process.");
+ return m_live_process->TraceStop(
+ TraceStopRequest(GetPluginName().AsCString()));
+}
+
+Error Trace::StopThreads(const std::vector<lldb::tid_t> &tids) {
+ if (!m_live_process)
+ return createStringError(inconvertibleErrorCode(),
+ "Tracing requires a live process.");
+ return m_live_process->TraceStop(
+ TraceStopRequest(GetPluginName().AsCString(), tids));
+}
+
+Expected<std::string> Trace::GetLiveProcessState() {
+ if (!m_live_process)
+ return createStringError(inconvertibleErrorCode(),
+ "Tracing requires a live process.");
+ return m_live_process->TraceGetState(GetPluginName().AsCString());
+}
+
+Optional<size_t> Trace::GetLiveThreadBinaryDataSize(lldb::tid_t tid,
+ llvm::StringRef kind) {
+ auto it = m_live_thread_data.find(tid);
+ if (it == m_live_thread_data.end())
+ return None;
+ std::unordered_map<std::string, size_t> &single_thread_data = it->second;
+ auto single_thread_data_it = single_thread_data.find(kind.str());
+ if (single_thread_data_it == single_thread_data.end())
+ return None;
+ return single_thread_data_it->second;
+}
+
+Optional<size_t> Trace::GetLiveProcessBinaryDataSize(llvm::StringRef kind) {
+ auto data_it = m_live_process_data.find(kind.str());
+ if (data_it == m_live_process_data.end())
+ return None;
+ return data_it->second;
+}
+
+Expected<std::vector<uint8_t>>
+Trace::GetLiveThreadBinaryData(lldb::tid_t tid, llvm::StringRef kind) {
+ if (!m_live_process)
+ return createStringError(inconvertibleErrorCode(),
+ "Tracing requires a live process.");
+ llvm::Optional<size_t> size = GetLiveThreadBinaryDataSize(tid, kind);
+ if (!size)
+ return createStringError(
+ inconvertibleErrorCode(),
+ "Tracing data \"%s\" is not available for thread %" PRIu64 ".",
+ kind.data(), tid);
+
+ TraceGetBinaryDataRequest request{GetPluginName().AsCString(), kind.str(),
+ static_cast<int64_t>(tid), 0,
+ static_cast<int64_t>(*size)};
+ return m_live_process->TraceGetBinaryData(request);
+}
+
+Expected<std::vector<uint8_t>>
+Trace::GetLiveProcessBinaryData(llvm::StringRef kind) {
+ if (!m_live_process)
+ return createStringError(inconvertibleErrorCode(),
+ "Tracing requires a live process.");
+ llvm::Optional<size_t> size = GetLiveProcessBinaryDataSize(kind);
+ if (!size)
+ return createStringError(
+ inconvertibleErrorCode(),
+ "Tracing data \"%s\" is not available for the process.", kind.data());
+
+ TraceGetBinaryDataRequest request{GetPluginName().AsCString(), kind.str(),
+ None, 0, static_cast<int64_t>(*size)};
+ return m_live_process->TraceGetBinaryData(request);
+}
+
+void Trace::RefreshLiveProcessState() {
+ if (!m_live_process)
+ return;
+
+ uint32_t new_stop_id = m_live_process->GetStopID();
+ if (new_stop_id == m_stop_id)
+ return;
+
+ m_stop_id = new_stop_id;
+ m_live_thread_data.clear();
+
+ Expected<std::string> json_string = GetLiveProcessState();
+ if (!json_string) {
+ DoRefreshLiveProcessState(json_string.takeError());
+ return;
+ }
+ Expected<TraceGetStateResponse> live_process_state =
+ json::parse<TraceGetStateResponse>(*json_string, "TraceGetStateResponse");
+ if (!live_process_state) {
+ DoRefreshLiveProcessState(live_process_state.takeError());
+ return;
+ }
+
+ for (const TraceThreadState &thread_state :
+ live_process_state->tracedThreads) {
+ for (const TraceBinaryData &item : thread_state.binaryData)
+ m_live_thread_data[thread_state.tid][item.kind] = item.size;
+ }
+
+ for (const TraceBinaryData &item : live_process_state->processBinaryData)
+ m_live_process_data[item.kind] = item.size;
+
+ DoRefreshLiveProcessState(std::move(live_process_state));
+}
diff --git a/lldb/source/Target/TraceSessionFileParser.cpp b/lldb/source/Target/TraceSessionFileParser.cpp
index 713fadc1eb4fe..006d74b28e8b0 100644
--- a/lldb/source/Target/TraceSessionFileParser.cpp
+++ b/lldb/source/Target/TraceSessionFileParser.cpp
@@ -14,7 +14,7 @@
#include "lldb/Core/Module.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"
-#include "lldb/Target/ThreadTrace.h"
+#include "lldb/Target/ThreadPostMortemTrace.h"
using namespace lldb;
using namespace lldb_private;
@@ -96,15 +96,16 @@ std::string TraceSessionFileParser::BuildSchema(StringRef plugin_schema) {
return schema_builder.str();
}
-ThreadTraceSP TraceSessionFileParser::ParseThread(ProcessSP &process_sp,
- const JSONThread &thread) {
+ThreadPostMortemTraceSP
+TraceSessionFileParser::ParseThread(ProcessSP &process_sp,
+ const JSONThread &thread) {
lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid);
FileSpec trace_file(thread.trace_file);
NormalizePath(trace_file);
- ThreadTraceSP thread_sp =
- std::make_shared<ThreadTrace>(*process_sp, tid, trace_file);
+ ThreadPostMortemTraceSP thread_sp =
+ std::make_shared<ThreadPostMortemTrace>(*process_sp, tid, trace_file);
process_sp->GetThreadList().AddThread(thread_sp);
return thread_sp;
}
diff --git a/lldb/source/Utility/CMakeLists.txt b/lldb/source/Utility/CMakeLists.txt
index 3aca72de6c659..6790e47d69f25 100644
--- a/lldb/source/Utility/CMakeLists.txt
+++ b/lldb/source/Utility/CMakeLists.txt
@@ -65,7 +65,8 @@ add_lldb_library(lldbUtility
StructuredData.cpp
TildeExpressionResolver.cpp
Timer.cpp
- TraceOptions.cpp
+ TraceGDBRemotePackets.cpp
+ TraceIntelPTGDBRemotePackets.cpp
UnimplementedError.cpp
UUID.cpp
UriParser.cpp
diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp
index 211026535518d..6815ddf989840 100644
--- a/lldb/source/Utility/StringExtractorGDBRemote.cpp
+++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp
@@ -303,18 +303,17 @@ StringExtractorGDBRemote::GetServerPacketType() const {
return eServerPacketType_jSignalsInfo;
if (PACKET_MATCHES("jThreadsInfo"))
return eServerPacketType_jThreadsInfo;
- if (PACKET_STARTS_WITH("jTraceBufferRead:"))
- return eServerPacketType_jTraceBufferRead;
- if (PACKET_STARTS_WITH("jTraceConfigRead:"))
- return eServerPacketType_jTraceConfigRead;
- if (PACKET_STARTS_WITH("jTraceMetaRead:"))
- return eServerPacketType_jTraceMetaRead;
- if (PACKET_STARTS_WITH("jTraceStart:"))
- return eServerPacketType_jTraceStart;
- if (PACKET_STARTS_WITH("jTraceStop:"))
- return eServerPacketType_jTraceStop;
- if (PACKET_MATCHES("jLLDBTraceSupportedType"))
- return eServerPacketType_jLLDBTraceSupportedType;
+
+ if (PACKET_MATCHES("jLLDBTraceSupported"))
+ return eServerPacketType_jLLDBTraceSupported;
+ if (PACKET_STARTS_WITH("jLLDBTraceStop:"))
+ return eServerPacketType_jLLDBTraceStop;
+ if (PACKET_STARTS_WITH("jLLDBTraceStart:"))
+ return eServerPacketType_jLLDBTraceStart;
+ if (PACKET_STARTS_WITH("jLLDBTraceGetState:"))
+ return eServerPacketType_jLLDBTraceGetState;
+ if (PACKET_STARTS_WITH("jLLDBTraceGetBinaryData:"))
+ return eServerPacketType_jLLDBTraceGetBinaryData;
break;
case 'v':
diff --git a/lldb/source/Utility/TraceGDBRemotePackets.cpp b/lldb/source/Utility/TraceGDBRemotePackets.cpp
new file mode 100644
index 0000000000000..5c1326a5f353a
--- /dev/null
+++ b/lldb/source/Utility/TraceGDBRemotePackets.cpp
@@ -0,0 +1,130 @@
+//===-- TraceGDBRemotePackets.cpp -------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/TraceGDBRemotePackets.h"
+
+using namespace llvm;
+using namespace llvm::json;
+
+namespace lldb_private {
+/// jLLDBTraceSupported
+/// \{
+bool fromJSON(const json::Value &value, TraceSupportedResponse &packet,
+ Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("description", packet.description) &&
+ o.map("name", packet.name);
+}
+
+json::Value toJSON(const TraceSupportedResponse &packet) {
+ return json::Value(
+ Object{{"description", packet.description}, {"name", packet.name}});
+}
+/// \}
+
+/// jLLDBTraceStart
+/// \{
+bool TraceStartRequest::IsProcessTracing() const { return !(bool)tids; }
+
+bool fromJSON(const json::Value &value, TraceStartRequest &packet, Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("type", packet.type) && o.map("tids", packet.tids);
+}
+
+json::Value toJSON(const TraceStartRequest &packet) {
+ return json::Value(Object{{"tids", packet.tids}, {"type", packet.type}});
+}
+/// \}
+
+/// jLLDBTraceStop
+/// \{
+TraceStopRequest::TraceStopRequest(llvm::StringRef type,
+ const std::vector<lldb::tid_t> &tids_)
+ : type(type) {
+ tids.emplace();
+ for (lldb::tid_t tid : tids_)
+ tids->push_back(static_cast<int64_t>(tid));
+}
+
+bool TraceStopRequest::IsProcessTracing() const { return !(bool)tids; }
+
+bool fromJSON(const json::Value &value, TraceStopRequest &packet, Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("type", packet.type) && o.map("tids", packet.tids);
+}
+
+json::Value toJSON(const TraceStopRequest &packet) {
+ return json::Value(Object{{"type", packet.type}, {"tids", packet.tids}});
+}
+/// \}
+
+/// jLLDBTraceGetState
+/// \{
+bool fromJSON(const json::Value &value, TraceGetStateRequest &packet,
+ Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("type", packet.type);
+}
+
+json::Value toJSON(const TraceGetStateRequest &packet) {
+ return json::Value(Object{{"type", packet.type}});
+}
+
+bool fromJSON(const json::Value &value, TraceBinaryData &packet, Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("kind", packet.kind) && o.map("size", packet.size);
+}
+
+json::Value toJSON(const TraceBinaryData &packet) {
+ return json::Value(Object{{"kind", packet.kind}, {"size", packet.size}});
+}
+
+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);
+}
+
+json::Value toJSON(const TraceThreadState &packet) {
+ return json::Value(
+ Object{{"tid", packet.tid}, {"binaryData", packet.binaryData}});
+}
+
+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);
+}
+
+json::Value toJSON(const TraceGetStateResponse &packet) {
+ return json::Value(Object{{"tracedThreads", packet.tracedThreads},
+ {"processBinaryData", packet.processBinaryData}});
+}
+/// \}
+
+/// jLLDBTraceGetBinaryData
+/// \{
+json::Value toJSON(const TraceGetBinaryDataRequest &packet) {
+ return json::Value(Object{{"type", packet.type},
+ {"kind", packet.kind},
+ {"offset", packet.offset},
+ {"tid", packet.tid},
+ {"size", packet.size}});
+}
+
+bool fromJSON(const json::Value &value, TraceGetBinaryDataRequest &packet,
+ Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("type", packet.type) && o.map("kind", packet.kind) &&
+ o.map("tid", packet.tid) && o.map("offset", packet.offset) &&
+ o.map("size", packet.size);
+}
+/// \}
+
+} // namespace lldb_private
diff --git a/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp b/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
new file mode 100644
index 0000000000000..69f773f673bb2
--- /dev/null
+++ b/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
@@ -0,0 +1,42 @@
+//===-- TraceIntelPTGDBRemotePackets.cpp ------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
+
+using namespace llvm;
+using namespace llvm::json;
+
+namespace lldb_private {
+
+bool fromJSON(const json::Value &value, TraceIntelPTStartRequest &packet,
+ Path path) {
+ ObjectMapper o(value, path);
+ if (!o || !fromJSON(value, (TraceStartRequest &)packet, path) ||
+ !o.map("threadBufferSize", packet.threadBufferSize) ||
+ !o.map("processBufferSizeLimit", packet.processBufferSizeLimit))
+ return false;
+ if (packet.tids && packet.processBufferSizeLimit) {
+ path.report("processBufferSizeLimit must be provided");
+ return false;
+ }
+ if (!packet.tids && !packet.processBufferSizeLimit) {
+ path.report("processBufferSizeLimit must not be provided");
+ return false;
+ }
+ return true;
+}
+
+json::Value toJSON(const TraceIntelPTStartRequest &packet) {
+ json::Value base = toJSON((const TraceStartRequest &)packet);
+ base.getAsObject()->try_emplace("threadBufferSize", packet.threadBufferSize);
+ base.getAsObject()->try_emplace("processBufferSizeLimit",
+ packet.processBufferSizeLimit);
+ return base;
+}
+
+} // namespace lldb_private
diff --git a/lldb/source/Utility/TraceOptions.cpp b/lldb/source/Utility/TraceOptions.cpp
deleted file mode 100644
index 292fb600e6ed9..0000000000000
--- a/lldb/source/Utility/TraceOptions.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-//===-- TraceOptions.cpp ----------------------------------------*- 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "lldb/Utility/TraceOptions.h"
-
-using namespace lldb_private;
-
-namespace llvm {
-namespace json {
-
-bool fromJSON(const Value &value, TraceTypeInfo &info, Path path) {
- ObjectMapper o(value, path);
- if (!o)
- return false;
- o.map("description", info.description);
- return o.map("name", info.name);
-}
-
-} // namespace json
-} // namespace llvm
diff --git a/lldb/test/API/commands/trace/TestTraceLoad.py b/lldb/test/API/commands/trace/TestTraceLoad.py
index c48d468ce7845..66cb3b0a10568 100644
--- a/lldb/test/API/commands/trace/TestTraceLoad.py
+++ b/lldb/test/API/commands/trace/TestTraceLoad.py
@@ -59,7 +59,7 @@ def testLoadInvalidTraces(self):
{
"trace": {
"type": "intel-pt",
- "pt_cpu": {
+ "cpuInfo": {
"vendor": "intel" | "unknown",
"family": integer,
"model": integer,
@@ -73,13 +73,13 @@ def testLoadInvalidTraces(self):
# Now we test a missing field in the intel-pt settings
self.expect("trace load -v " + os.path.join(src_dir, "intelpt-trace", "trace_bad4.json"), error=True,
- substrs=['''error: missing value at traceSession.trace.pt_cpu.family
+ substrs=['''error: missing value at traceSession.trace.cpuInfo.family
Context:
{
"processes": [],
"trace": {
- "pt_cpu": /* error: missing value */ {
+ "cpuInfo": /* error: missing value */ {
"model": 79,
"stepping": 1,
"vendor": "intel"
diff --git a/lldb/test/API/commands/trace/TestTraceSchema.py b/lldb/test/API/commands/trace/TestTraceSchema.py
index b30ed329152de..d0379014cd2aa 100644
--- a/lldb/test/API/commands/trace/TestTraceSchema.py
+++ b/lldb/test/API/commands/trace/TestTraceSchema.py
@@ -25,7 +25,7 @@ def testAllSchemas(self):
self.expect("trace schema all", substrs=['''{
"trace": {
"type": "intel-pt",
- "pt_cpu": {
+ "cpuInfo": {
"vendor": "intel" | "unknown",
"family": integer,
"model": integer,
diff --git a/lldb/test/API/commands/trace/TestTraceStartStop.py b/lldb/test/API/commands/trace/TestTraceStartStop.py
index 4984365cb898c..ad301a575b8f6 100644
--- a/lldb/test/API/commands/trace/TestTraceStartStop.py
+++ b/lldb/test/API/commands/trace/TestTraceStartStop.py
@@ -3,7 +3,9 @@
from lldbsuite.test import lldbutil
from lldbsuite.test.decorators import *
-class TestTraceLoad(TestBase):
+ADDRESS_REGEX = '0x[0-9a-fA-F]*'
+
+class TestTraceStartStop(TestBase):
mydir = TestBase.compute_mydir(__file__)
NO_DEBUG_INFO_TESTCASE = True
@@ -21,14 +23,30 @@ def testStartStopSessionFileThreads(self):
# it should fail for processes from json session files
self.expect("trace load -v " + os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"))
self.expect("thread trace start", error=True,
- substrs=["error: Tracing is not supported. Can't trace a non-live process"])
+ substrs=["error: Process must be alive"])
# the help command should be the generic one, as it's not a live process
self.expectGenericHelpMessageForStartCommand()
- # this should fail because 'trace stop' is not yet implemented
- self.expect("thread trace stop", error=True,
- substrs=["error: Failed stopping thread 3842849"])
+ self.expect("thread trace stop", error=True)
+
+ def testStartWithNoProcess(self):
+ self.expect("thread trace start", error=True,
+ substrs=["error: Process not available."])
+
+
+ def testStartSessionWithWrongSize(self):
+ self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
+ self.expect("b main")
+ self.expect("r")
+ self.expect("thread trace start -s 2000", error=True,
+ substrs=["The trace buffer size must be a power of 2", "It was 2000"])
+ self.expect("thread trace start -s 5000", error=True,
+ substrs=["The trace buffer size must be a power of 2", "It was 5000"])
+ self.expect("thread trace start -s 0", error=True,
+ substrs=["The trace buffer size must be a power of 2", "It was 0"])
+ self.expect("thread trace start -s 1048576")
+
@skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
def testStartStopLiveThreads(self):
@@ -49,20 +67,58 @@ def testStartStopLiveThreads(self):
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 1 --size 4096")
+
+ # We fail if we try to trace again
+ self.expect("thread trace start", error=True,
+ substrs=["error: Thread ", "already traced"])
+
+ # 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: Thread ", "not currently traced"])
+
+ # We trace again from scratch, this time letting LLDB to pick the current
+ # thread
+ 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
diff --git a/lldb/test/API/commands/trace/intelpt-trace-multi-file/multi-file-no-ld.json b/lldb/test/API/commands/trace/intelpt-trace-multi-file/multi-file-no-ld.json
index 5a553dc9dade7..b8c6d32473dbd 100644
--- a/lldb/test/API/commands/trace/intelpt-trace-multi-file/multi-file-no-ld.json
+++ b/lldb/test/API/commands/trace/intelpt-trace-multi-file/multi-file-no-ld.json
@@ -1,7 +1,7 @@
{
"trace": {
"type": "intel-pt",
- "pt_cpu": {
+ "cpuInfo": {
"vendor": "intel",
"family": 6,
"model": 79,
diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace.json b/lldb/test/API/commands/trace/intelpt-trace/trace.json
index 543b71d0cefec..9104c572e3fb7 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace.json
@@ -1,7 +1,7 @@
{
"trace": {
"type": "intel-pt",
- "pt_cpu": {
+ "cpuInfo": {
"vendor": "intel",
"family": 6,
"model": 79,
diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json b/lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json
index 87609f2e3b277..f73944d1fa186 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json
@@ -1,7 +1,7 @@
{
"trace": {
"type": "intel-pt",
- "pt_cpu": {
+ "cpuInfo": {
"vendor": "intel",
"family": 6,
"model": 79,
diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace_bad.json b/lldb/test/API/commands/trace/intelpt-trace/trace_bad.json
index 9d00a82ebcf78..0232f9ffb80d4 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_bad.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_bad.json
@@ -1,7 +1,7 @@
{
"trace": {
"type": "intel-pt",
- "pt_cpu": {
+ "cpuInfo": {
"vendor": "intel",
"family": 6,
"model": 79,
diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json b/lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json
index 16ece129c8d8b..f944c7362d048 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json
@@ -1,7 +1,7 @@
{
"trace": {
"type": "intel-pt",
- "pt_cpu": {
+ "cpuInfo": {
"vendor": "intel",
"family": 6,
"model": 79,
diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace_bad3.json b/lldb/test/API/commands/trace/intelpt-trace/trace_bad3.json
index dc587a8a4c523..63eafbc13efbf 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_bad3.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_bad3.json
@@ -1,7 +1,7 @@
{
"trace": {
"type": "intel-pt",
- "pt_cpu": {
+ "cpuInfo": {
"vendor": "intel",
"family": 6,
"model": 79,
diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace_bad4.json b/lldb/test/API/commands/trace/intelpt-trace/trace_bad4.json
index 08084829eda60..7224ee5f1f3c2 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_bad4.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_bad4.json
@@ -1,7 +1,7 @@
{
"trace": {
"type": "intel-pt",
- "pt_cpu": {
+ "cpuInfo": {
"vendor": "intel",
"model": 79,
"stepping": 1
diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace_bad5.json b/lldb/test/API/commands/trace/intelpt-trace/trace_bad5.json
index b75db43554598..1664072aa3b8d 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_bad5.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_bad5.json
@@ -1,7 +1,7 @@
{
"trace": {
"type": "intel-pt",
- "pt_cpu": {
+ "cpuInfo": {
"vendor": "intel",
"family": 6,
"model": 79,
diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace_bad_image.json b/lldb/test/API/commands/trace/intelpt-trace/trace_bad_image.json
index 013e4c5bd2bf6..901f41901842c 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_bad_image.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_bad_image.json
@@ -1,7 +1,7 @@
{
"trace": {
"type": "intel-pt",
- "pt_cpu": {
+ "cpuInfo": {
"vendor": "intel",
"family": 6,
"model": 79,
diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json b/lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json
index a39edc8a335e6..318aaba8154ff 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json
@@ -1,7 +1,7 @@
{
"trace": {
"type": "intel-pt",
- "pt_cpu": {
+ "cpuInfo": {
"vendor": "intel",
"family": 2123123,
"model": 12123123,
diff --git a/lldb/test/API/commands/trace/multiple-threads/Makefile b/lldb/test/API/commands/trace/multiple-threads/Makefile
new file mode 100644
index 0000000000000..d45370002a4bd
--- /dev/null
+++ b/lldb/test/API/commands/trace/multiple-threads/Makefile
@@ -0,0 +1,4 @@
+CXX_SOURCES := main.cpp
+ENABLE_THREADS = YES
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py b/lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py
new file mode 100644
index 0000000000000..d3fdb47605bd6
--- /dev/null
+++ b/lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py
@@ -0,0 +1,152 @@
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+from lldbsuite.test.decorators import *
+
+ADDRESS_REGEX = '0x[0-9a-fA-F]*'
+
+class TestTraceStartStopMultipleThreads(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def setUp(self):
+ TestBase.setUp(self)
+ if 'intel-pt' not in configuration.enabled_plugins:
+ self.skipTest("The intel-pt test plugin is not enabled")
+
+ @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
+ def testStartMultipleLiveThreads(self):
+ self.build()
+ exe = self.getBuildArtifact("a.out")
+ target = self.dbg.CreateTarget(exe)
+
+ self.expect("b main")
+ self.expect("b 6")
+ self.expect("b 11")
+
+ self.expect("r")
+ self.expect("proce trace start")
+
+
+ # We'll see here the first thread
+ self.expect("continue")
+ self.expect("thread trace dump instructions", substrs=['main.cpp:9'])
+
+ # We'll see here the second thread
+ self.expect("continue")
+ self.expect("thread trace dump instructions", substrs=['main.cpp:4'])
+
+ @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
+ def testStartMultipleLiveThreadsWithStops(self):
+ self.build()
+ exe = self.getBuildArtifact("a.out")
+ target = self.dbg.CreateTarget(exe)
+
+ self.expect("b main")
+ self.expect("b 6")
+ self.expect("b 11")
+
+ self.expect("r")
+ self.expect("process trace start")
+
+
+ # We'll see here the first thread
+ self.expect("continue")
+
+ # We are in thread 2
+ self.expect("thread trace dump instructions", substrs=['main.cpp:9'])
+ self.expect("thread trace dump instructions 2", substrs=['main.cpp:9'])
+
+ # We stop tracing it
+ self.expect("thread trace stop 2")
+
+ # The trace is still in memory
+ self.expect("thread trace dump instructions 2", substrs=['main.cpp:9'])
+
+ # We'll stop at the next breakpoint, thread 2 will be still alive, but not traced. Thread 3 will be traced
+ self.expect("continue")
+ self.expect("thread trace dump instructions", substrs=['main.cpp:4'])
+ self.expect("thread trace dump instructions 3", substrs=['main.cpp:4'])
+
+ self.expect("thread trace dump instructions 2", substrs=['not traced'])
+
+ @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
+ def testStartMultipleLiveThreadsWithStops(self):
+ self.build()
+ exe = self.getBuildArtifact("a.out")
+ target = self.dbg.CreateTarget(exe)
+
+ self.expect("b main")
+ self.expect("b 6")
+ self.expect("b 11")
+
+ self.expect("r")
+ self.expect("process trace start")
+
+ # We'll see here the first thread
+ self.expect("continue")
+
+ # We are in thread 2
+ self.expect("thread trace dump instructions", substrs=['main.cpp:9'])
+ self.expect("thread trace dump instructions 2", substrs=['main.cpp:9'])
+
+ # We stop tracing all
+ self.expect("thread trace stop all")
+
+ # The trace is still in memory
+ self.expect("thread trace dump instructions 2", substrs=['main.cpp:9'])
+
+ # We'll stop at the next breakpoint in thread 3, thread 2 and 3 will be alive, but only 3 traced.
+ self.expect("continue")
+ self.expect("thread trace dump instructions", substrs=['main.cpp:4'])
+ self.expect("thread trace dump instructions 3", substrs=['main.cpp:4'])
+ self.expect("thread trace dump instructions 1", substrs=['not traced'])
+ self.expect("thread trace dump instructions 2", substrs=['not traced'])
+
+ @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
+ def testStartMultipleLiveThreadsWithThreadStartAll(self):
+ self.build()
+ exe = self.getBuildArtifact("a.out")
+ target = self.dbg.CreateTarget(exe)
+
+ self.expect("b main")
+ self.expect("b 6")
+ self.expect("b 11")
+
+ self.expect("r")
+
+ self.expect("continue")
+ # We are in thread 2
+ self.expect("thread trace start all")
+ # Now we have instructions in thread's 2 trace
+ self.expect("n")
+
+ self.expect("thread trace dump instructions 2", substrs=['main.cpp:11'])
+
+ # We stop tracing all
+ self.runCmd("thread trace stop all")
+
+ # The trace is still in memory
+ self.expect("thread trace dump instructions 2", substrs=['main.cpp:11'])
+
+ # We'll stop at the next breakpoint in thread 3, and nothing should be traced
+ self.expect("continue")
+ self.expect("thread trace dump instructions 3", substrs=['not traced'])
+ self.expect("thread trace dump instructions 1", substrs=['not traced'])
+ self.expect("thread trace dump instructions 2", substrs=['not traced'])
+
+ @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
+ def testStartMultipleLiveThreadsWithSmallTotalLimit(self):
+ self.build()
+ exe = self.getBuildArtifact("a.out")
+ target = self.dbg.CreateTarget(exe)
+
+ self.expect("b main")
+ self.expect("r")
+ # trace the entire process with enough total size for 1 thread trace
+ self.expect("process trace start -l 5000")
+ # we get the stop event when trace 2 appears and can't be traced
+ self.expect("c", substrs=['Thread', "can't be traced"])
+ # we get the stop event when trace 3 appears and can't be traced
+ self.expect("c", substrs=['Thread', "can't be traced"])
diff --git a/lldb/test/API/commands/trace/multiple-threads/main.cpp b/lldb/test/API/commands/trace/multiple-threads/main.cpp
new file mode 100644
index 0000000000000..f6c58af9cbf71
--- /dev/null
+++ b/lldb/test/API/commands/trace/multiple-threads/main.cpp
@@ -0,0 +1,20 @@
+#include <chrono>
+#include <thread>
+
+void f3() {
+ int m;
+ m = 2; // thread 3
+}
+
+void f2() {
+ int n;
+ n = 1; // thread 2
+ std::thread t3(f3);
+ t3.join();
+}
+
+int main() { // main
+ std::thread t2(f2);
+ t2.join();
+ return 0;
+}
diff --git a/lldb/tools/lldb-vscode/JSONUtils.cpp b/lldb/tools/lldb-vscode/JSONUtils.cpp
index 0b37967e37a9d..3a9240749a7ff 100644
--- a/lldb/tools/lldb-vscode/JSONUtils.cpp
+++ b/lldb/tools/lldb-vscode/JSONUtils.cpp
@@ -867,9 +867,10 @@ llvm::json::Value CreateThreadStopped(lldb::SBThread &thread,
case lldb::eStopReasonInstrumentation:
body.try_emplace("reason", "breakpoint");
break;
- case lldb::eStopReasonSignal:
- body.try_emplace("reason", "exception");
+ case lldb::eStopReasonProcessorTrace:
+ body.try_emplace("reason", "processor trace");
break;
+ case lldb::eStopReasonSignal:
case lldb::eStopReasonException:
body.try_emplace("reason", "exception");
break;
diff --git a/lldb/tools/lldb-vscode/LLDBUtils.cpp b/lldb/tools/lldb-vscode/LLDBUtils.cpp
index 81b5a789d1da0..a5bc400dfccce 100644
--- a/lldb/tools/lldb-vscode/LLDBUtils.cpp
+++ b/lldb/tools/lldb-vscode/LLDBUtils.cpp
@@ -55,6 +55,7 @@ bool ThreadHasStopReason(lldb::SBThread &thread) {
case lldb::eStopReasonSignal:
case lldb::eStopReasonException:
case lldb::eStopReasonExec:
+ case lldb::eStopReasonProcessorTrace:
return true;
case lldb::eStopReasonThreadExiting:
case lldb::eStopReasonInvalid:
diff --git a/lldb/unittests/Process/Linux/CMakeLists.txt b/lldb/unittests/Process/Linux/CMakeLists.txt
index ab6eaa4454165..ae021023cf7f5 100644
--- a/lldb/unittests/Process/Linux/CMakeLists.txt
+++ b/lldb/unittests/Process/Linux/CMakeLists.txt
@@ -1,9 +1,9 @@
-add_lldb_unittest(ProcessorTraceTests
- ProcessorTraceTest.cpp
+add_lldb_unittest(TraceIntelPTTests
+ IntelPTManagerTests.cpp
LINK_LIBS
lldbPluginProcessLinux
)
-target_include_directories(ProcessorTraceTests PRIVATE
+target_include_directories(TraceIntelPTTests PRIVATE
${LLDB_SOURCE_DIR}/source/Plugins/Process/Linux)
diff --git a/lldb/unittests/Process/Linux/ProcessorTraceTest.cpp b/lldb/unittests/Process/Linux/IntelPTManagerTests.cpp
similarity index 96%
rename from lldb/unittests/Process/Linux/ProcessorTraceTest.cpp
rename to lldb/unittests/Process/Linux/IntelPTManagerTests.cpp
index 3615135cccb46..76eb78a51935d 100644
--- a/lldb/unittests/Process/Linux/ProcessorTraceTest.cpp
+++ b/lldb/unittests/Process/Linux/IntelPTManagerTests.cpp
@@ -1,4 +1,4 @@
-//===-- ProcessorTraceMonitorTest.cpp -------------------------------------===//
+//===-- IntelPTManagerTests.cpp -------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -8,7 +8,7 @@
#include "gtest/gtest.h"
-#include "ProcessorTrace.h"
+#include "IntelPTManager.h"
#include "llvm/ADT/ArrayRef.h"
@@ -22,7 +22,7 @@ size_t ReadCylicBufferWrapper(void *buf, size_t buf_size, void *cyc_buf,
buf_size);
llvm::MutableArrayRef<uint8_t> src(reinterpret_cast<uint8_t *>(cyc_buf),
cyc_buf_size);
- ProcessorTraceMonitor::ReadCyclicBuffer(dst, src, cyc_start, offset);
+ IntelPTThreadTrace::ReadCyclicBuffer(dst, src, cyc_start, offset);
return dst.size();
}
diff --git a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
index 2cca197f63a1e..0a9a42a81fde3 100644
--- a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
+++ b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
@@ -381,12 +381,12 @@ TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfoInvalidResponse) {
EXPECT_FALSE(result.get().Success());
}
-TEST_F(GDBRemoteCommunicationClientTest, SendTraceSupportedTypePacket) {
- TraceTypeInfo trace_type;
+TEST_F(GDBRemoteCommunicationClientTest, SendTraceSupportedPacket) {
+ TraceSupportedResponse trace_type;
std::string error_message;
auto callback = [&] {
- if (llvm::Expected<TraceTypeInfo> trace_type_or_err =
- client.SendGetSupportedTraceType()) {
+ if (llvm::Expected<TraceSupportedResponse> trace_type_or_err =
+ client.SendTraceSupported()) {
trace_type = *trace_type_or_err;
error_message = "";
return true;
@@ -402,7 +402,7 @@ TEST_F(GDBRemoteCommunicationClientTest, SendTraceSupportedTypePacket) {
std::future<bool> result = std::async(std::launch::async, callback);
HandlePacket(
- server, "jLLDBTraceSupportedType",
+ server, "jLLDBTraceSupported",
R"({"name":"intel-pt","description":"Intel Processor Trace"}])");
EXPECT_TRUE(result.get());
@@ -414,17 +414,17 @@ TEST_F(GDBRemoteCommunicationClientTest, SendTraceSupportedTypePacket) {
{
std::future<bool> result = std::async(std::launch::async, callback);
- HandlePacket(server, "jLLDBTraceSupportedType", R"({"type":"intel-pt"}])");
+ HandlePacket(server, "jLLDBTraceSupported", R"({"type":"intel-pt"}])");
EXPECT_FALSE(result.get());
- ASSERT_STREQ(error_message.c_str(), "missing value at (root).name");
+ ASSERT_STREQ(error_message.c_str(), "missing value at (root).description");
}
// Error response
{
std::future<bool> result = std::async(std::launch::async, callback);
- HandlePacket(server, "jLLDBTraceSupportedType", "E23");
+ HandlePacket(server, "jLLDBTraceSupported", "E23");
EXPECT_FALSE(result.get());
}
@@ -433,7 +433,7 @@ TEST_F(GDBRemoteCommunicationClientTest, SendTraceSupportedTypePacket) {
{
std::future<bool> result = std::async(std::launch::async, callback);
- HandlePacket(server, "jLLDBTraceSupportedType",
+ HandlePacket(server, "jLLDBTraceSupported",
"E23;50726F63657373206E6F742072756E6E696E672E");
EXPECT_FALSE(result.get());
@@ -441,197 +441,6 @@ TEST_F(GDBRemoteCommunicationClientTest, SendTraceSupportedTypePacket) {
}
}
-TEST_F(GDBRemoteCommunicationClientTest, SendStartTracePacket) {
- TraceOptions options;
- Status error;
-
- options.setType(lldb::TraceType::eTraceTypeProcessorTrace);
- options.setMetaDataBufferSize(8192);
- options.setTraceBufferSize(8192);
- options.setThreadID(0x23);
-
- StructuredData::DictionarySP custom_params =
- std::make_shared<StructuredData::Dictionary>();
- custom_params->AddStringItem("tracetech", "intel-pt");
- custom_params->AddIntegerItem("psb", 0x01);
-
- options.setTraceParams(custom_params);
-
- std::future<lldb::user_id_t> result = std::async(std::launch::async, [&] {
- return client.SendStartTracePacket(options, error);
- });
-
- // Since the line is exceeding 80 characters.
- std::string expected_packet1 =
- R"(jTraceStart:{"buffersize":8192,"metabuffersize":8192,"params":)";
- std::string expected_packet2 =
- R"({"psb":1,"tracetech":"intel-pt"},"threadid":35,"type":1})";
- HandlePacket(server, (expected_packet1 + expected_packet2), "1");
- ASSERT_TRUE(error.Success());
- ASSERT_EQ(result.get(), 1u);
-
- error.Clear();
- result = std::async(std::launch::async, [&] {
- return client.SendStartTracePacket(options, error);
- });
-
- HandlePacket(server, (expected_packet1 + expected_packet2), "E23");
- ASSERT_EQ(result.get(), LLDB_INVALID_UID);
- ASSERT_FALSE(error.Success());
-}
-
-TEST_F(GDBRemoteCommunicationClientTest, SendStopTracePacket) {
- lldb::tid_t thread_id = 0x23;
- lldb::user_id_t trace_id = 3;
-
- std::future<Status> result = std::async(std::launch::async, [&] {
- return client.SendStopTracePacket(trace_id, thread_id);
- });
-
- const char *expected_packet = R"(jTraceStop:{"threadid":35,"traceid":3})";
- HandlePacket(server, expected_packet, "OK");
- ASSERT_TRUE(result.get().Success());
-
- result = std::async(std::launch::async, [&] {
- return client.SendStopTracePacket(trace_id, thread_id);
- });
-
- HandlePacket(server, expected_packet, "E23");
- ASSERT_FALSE(result.get().Success());
-}
-
-TEST_F(GDBRemoteCommunicationClientTest, SendGetDataPacket) {
- lldb::tid_t thread_id = 0x23;
- lldb::user_id_t trace_id = 3;
-
- uint8_t buf[32] = {};
- llvm::MutableArrayRef<uint8_t> buffer(buf, 32);
- size_t offset = 0;
-
- std::future<Status> result = std::async(std::launch::async, [&] {
- return client.SendGetDataPacket(trace_id, thread_id, buffer, offset);
- });
-
- std::string expected_packet1 =
- R"(jTraceBufferRead:{"buffersize":32,"offset":0,"threadid":35,)";
- std::string expected_packet2 = R"("traceid":3})";
- HandlePacket(server, expected_packet1+expected_packet2, "123456");
- ASSERT_TRUE(result.get().Success());
- ASSERT_EQ(buffer.size(), 3u);
- ASSERT_EQ(buf[0], 0x12);
- ASSERT_EQ(buf[1], 0x34);
- ASSERT_EQ(buf[2], 0x56);
-
- llvm::MutableArrayRef<uint8_t> buffer2(buf, 32);
- result = std::async(std::launch::async, [&] {
- return client.SendGetDataPacket(trace_id, thread_id, buffer2, offset);
- });
-
- HandlePacket(server, expected_packet1+expected_packet2, "E23");
- ASSERT_FALSE(result.get().Success());
- ASSERT_EQ(buffer2.size(), 0u);
-}
-
-TEST_F(GDBRemoteCommunicationClientTest, SendGetMetaDataPacket) {
- lldb::tid_t thread_id = 0x23;
- lldb::user_id_t trace_id = 3;
-
- uint8_t buf[32] = {};
- llvm::MutableArrayRef<uint8_t> buffer(buf, 32);
- size_t offset = 0;
-
- std::future<Status> result = std::async(std::launch::async, [&] {
- return client.SendGetMetaDataPacket(trace_id, thread_id, buffer, offset);
- });
-
- std::string expected_packet1 =
- R"(jTraceMetaRead:{"buffersize":32,"offset":0,"threadid":35,)";
- std::string expected_packet2 = R"("traceid":3})";
- HandlePacket(server, expected_packet1+expected_packet2, "123456");
- ASSERT_TRUE(result.get().Success());
- ASSERT_EQ(buffer.size(), 3u);
- ASSERT_EQ(buf[0], 0x12);
- ASSERT_EQ(buf[1], 0x34);
- ASSERT_EQ(buf[2], 0x56);
-
- llvm::MutableArrayRef<uint8_t> buffer2(buf, 32);
- result = std::async(std::launch::async, [&] {
- return client.SendGetMetaDataPacket(trace_id, thread_id, buffer2, offset);
- });
-
- HandlePacket(server, expected_packet1+expected_packet2, "E23");
- ASSERT_FALSE(result.get().Success());
- ASSERT_EQ(buffer2.size(), 0u);
-}
-
-TEST_F(GDBRemoteCommunicationClientTest, SendGetTraceConfigPacket) {
- lldb::tid_t thread_id = 0x23;
- lldb::user_id_t trace_id = 3;
- TraceOptions options;
- options.setThreadID(thread_id);
-
- std::future<Status> result = std::async(std::launch::async, [&] {
- return client.SendGetTraceConfigPacket(trace_id, options);
- });
-
- const char *expected_packet =
- R"(jTraceConfigRead:{"threadid":35,"traceid":3})";
- std::string response1 =
- R"({"buffersize":8192,"params":{"psb":1,"tracetech":"intel-pt"})";
- std::string response2 = R"(],"metabuffersize":8192,"threadid":35,"type":1}])";
- HandlePacket(server, expected_packet, response1+response2);
- ASSERT_TRUE(result.get().Success());
- ASSERT_EQ(options.getTraceBufferSize(), 8192u);
- ASSERT_EQ(options.getMetaDataBufferSize(), 8192u);
- ASSERT_EQ(options.getType(), 1);
-
- auto custom_params = options.getTraceParams();
-
- uint64_t psb_value;
- llvm::StringRef trace_tech_value;
-
- ASSERT_TRUE(custom_params);
- ASSERT_EQ(custom_params->GetType(), eStructuredDataTypeDictionary);
- ASSERT_TRUE(custom_params->GetValueForKeyAsInteger("psb", psb_value));
- ASSERT_EQ(psb_value, 1u);
- ASSERT_TRUE(
- custom_params->GetValueForKeyAsString("tracetech", trace_tech_value));
- ASSERT_STREQ(trace_tech_value.data(), "intel-pt");
-
- // Checking error response.
- std::future<Status> result2 = std::async(std::launch::async, [&] {
- return client.SendGetTraceConfigPacket(trace_id, options);
- });
-
- HandlePacket(server, expected_packet, "E23");
- ASSERT_FALSE(result2.get().Success());
-
- // Wrong JSON as response.
- std::future<Status> result3 = std::async(std::launch::async, [&] {
- return client.SendGetTraceConfigPacket(trace_id, options);
- });
-
- std::string incorrect_json1 =
- R"("buffersize" : 8192,"params" : {"psb" : 1,"tracetech" : "intel-pt"})";
- std::string incorrect_json2 =
- R"(],"metabuffersize" : 8192,"threadid" : 35,"type" : 1}])";
- HandlePacket(server, expected_packet, incorrect_json1+incorrect_json2);
- ASSERT_FALSE(result3.get().Success());
-
- // Wrong JSON as custom_params.
- std::future<Status> result4 = std::async(std::launch::async, [&] {
- return client.SendGetTraceConfigPacket(trace_id, options);
- });
-
- std::string incorrect_custom_params1 =
- R"({"buffersize" : 8192,"params" : "psb" : 1,"tracetech" : "intel-pt"})";
- std::string incorrect_custom_params2 =
- R"(],"metabuffersize" : 8192,"threadid" : 35,"type" : 1}])";
- HandlePacket(server, expected_packet, incorrect_custom_params1+
- incorrect_custom_params2);
- ASSERT_FALSE(result4.get().Success());
-}
-
TEST_F(GDBRemoteCommunicationClientTest, GetQOffsets) {
const auto &GetQOffsets = [&](llvm::StringRef response) {
std::future<Optional<QOffsets>> result = std::async(
More information about the lldb-commits
mailing list