[Lldb-commits] [lldb] 26d861c - [trace] Scaffold "thread trace dump instructions"
Walter Erquinigo via lldb-commits
lldb-commits at lists.llvm.org
Mon Oct 12 12:08:26 PDT 2020
Author: Walter Erquinigo
Date: 2020-10-12T12:08:18-07:00
New Revision: 26d861cbbd5f40182b3b7f0ac7ed0e58e0e8feaa
URL: https://github.com/llvm/llvm-project/commit/26d861cbbd5f40182b3b7f0ac7ed0e58e0e8feaa
DIFF: https://github.com/llvm/llvm-project/commit/26d861cbbd5f40182b3b7f0ac7ed0e58e0e8feaa.diff
LOG: [trace] Scaffold "thread trace dump instructions"
Depends on D88841
As per the discussion in the RFC, we'll implement both
thread trace dump [instructions | functions]
This is the first step in implementing the "instructions" dumping command.
It includes:
- A minimal ProcessTrace plugin for representing processes from a trace file. I noticed that it was a required step to mimic how core-based processes are initialized, e.g. ProcessElfCore and ProcessMinidump. I haven't had the need to create ThreadTrace yet, though. So far HistoryThread seems good enough.
- The command handling itself in CommandObjectThread, which outputs a placeholder text instead of the actual instructions. I'll do that part in the next diff.
- Tests
{F13132325}
Differential Revision: https://reviews.llvm.org/D88769
Added:
lldb/source/Plugins/Process/Trace/CMakeLists.txt
lldb/source/Plugins/Process/Trace/ProcessTrace.cpp
lldb/source/Plugins/Process/Trace/ProcessTrace.h
lldb/test/API/commands/trace/TestTraceDumpInstructions.py
lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json
Modified:
lldb/include/lldb/Target/Target.h
lldb/include/lldb/Target/Thread.h
lldb/include/lldb/Target/Trace.h
lldb/source/Commands/CommandObjectThread.cpp
lldb/source/Commands/Options.td
lldb/source/Plugins/Process/CMakeLists.txt
lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
lldb/source/Target/Target.cpp
lldb/source/Target/Trace.cpp
lldb/test/API/commands/trace/TestTraceLoad.py
Removed:
################################################################################
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 7ee27a9776d5c..f371c4fd69565 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -1105,6 +1105,20 @@ class Target : public std::enable_shared_from_this<Target>,
void ClearAllLoadedSections();
+ /// Set the \a Trace object containing processor trace information of this
+ /// target.
+ ///
+ /// \param[in] trace_sp
+ /// The trace object.
+ void SetTrace(const lldb::TraceSP &trace_sp);
+
+ /// Get the \a Trace object containing processor trace information of this
+ /// target.
+ ///
+ /// \return
+ /// The trace object. It might be undefined.
+ const lldb::TraceSP &GetTrace();
+
// Since expressions results can persist beyond the lifetime of a process,
// and the const expression results are available after a process is gone, we
// provide a way for expressions to be evaluated from the Target itself. If
@@ -1402,6 +1416,9 @@ class Target : public std::enable_shared_from_this<Target>,
bool m_suppress_stop_hooks;
bool m_is_dummy_target;
unsigned m_next_persistent_variable_index = 0;
+ /// An optional \a lldb_private::Trace object containing processor trace
+ /// information of this target.
+ lldb::TraceSP m_trace_sp;
/// Stores the frame recognizers of this target.
lldb::StackFrameRecognizerManagerUP m_frame_recognizer_manager_up;
diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h
index 59e4d8a8f8727..4b148063ec6ee 100644
--- a/lldb/include/lldb/Target/Thread.h
+++ b/lldb/include/lldb/Target/Thread.h
@@ -469,6 +469,24 @@ class Thread : public std::enable_shared_from_this<Thread>,
// the backing thread for all memory threads each time we stop.
}
+ /// Dump \a count instructions of the thread's \a Trace starting at the \a
+ /// start_position position in reverse order.
+ ///
+ /// The instructions are indexed in reverse order, which means that the \a
+ /// start_position 0 represents the last instruction of the trace
+ /// chronologically.
+ ///
+ /// \param[in] s
+ /// The stream object where the instructions are printed.
+ ///
+ /// \param[in] count
+ /// The number of instructions to print.
+ ///
+ /// \param[in] start_position
+ /// The position of the first instruction to print.
+ void DumpTraceInstructions(Stream &s, size_t count,
+ size_t start_position = 0) const;
+
// If stop_format is true, this will be the form used when we print stop
// info. If false, it will be the form we use for thread list and co.
void DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,
diff --git a/lldb/include/lldb/Target/Trace.h b/lldb/include/lldb/Target/Trace.h
index 09d18525a5583..7ecab7262cbf0 100644
--- a/lldb/include/lldb/Target/Trace.h
+++ b/lldb/include/lldb/Target/Trace.h
@@ -32,7 +32,8 @@ namespace lldb_private {
/// Processor trace information can also be fetched through the process
/// interfaces during a live debug session if your process supports gathering
/// this information.
-class Trace : public PluginInterface {
+class Trace : public PluginInterface,
+ public std::enable_shared_from_this<Trace> {
public:
/// Dump the trace data that this plug-in has access to.
///
@@ -96,6 +97,27 @@ class Trace : public PluginInterface {
/// \return
/// The JSON schema of this Trace plug-in.
virtual llvm::StringRef GetSchema() = 0;
+
+ /// Dump \a count instructions of the given thread's \a Trace starting at the
+ /// \a start_position position in reverse order.
+ ///
+ /// The instructions are indexed in reverse order, which means that the \a
+ /// start_position 0 represents the last instruction of the trace
+ /// chronologically.
+ ///
+ /// \param[in] thread
+ /// The thread whose trace will be dumped.
+ ///
+ /// \param[in] s
+ /// The stream object where the instructions are printed.
+ ///
+ /// \param[in] count
+ /// The number of instructions to print.
+ ///
+ /// \param[in] start_position
+ /// The position of the first instruction to print.
+ void DumpTraceInstructions(Thread &thread, Stream &s, size_t count,
+ size_t start_position) const;
};
} // namespace lldb_private
diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp
index 26e150f1ccc5c..6fbcf7cbbc681 100644
--- a/lldb/source/Commands/CommandObjectThread.cpp
+++ b/lldb/source/Commands/CommandObjectThread.cpp
@@ -8,6 +8,8 @@
#include "CommandObjectThread.h"
+#include <sstream>
+
#include "lldb/Core/ValueObject.h"
#include "lldb/Host/OptionParser.h"
#include "lldb/Interpreter/CommandInterpreter.h"
@@ -26,6 +28,7 @@
#include "lldb/Target/Thread.h"
#include "lldb/Target/ThreadPlan.h"
#include "lldb/Target/ThreadPlanStepInRange.h"
+#include "lldb/Target/Trace.h"
#include "lldb/Utility/State.h"
using namespace lldb;
@@ -2165,6 +2168,170 @@ class CommandObjectMultiwordThreadPlan : public CommandObjectMultiword {
~CommandObjectMultiwordThreadPlan() override = default;
};
+// Next are the subcommands of CommandObjectMultiwordTrace
+
+// CommandObjectTraceDumpInstructions
+#define LLDB_OPTIONS_thread_trace_dump_instructions
+#include "CommandOptions.inc"
+
+class CommandObjectTraceDumpInstructions
+ : public CommandObjectIterateOverThreads {
+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 {
+ Status error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'c': {
+ int32_t count;
+ if (option_arg.empty() || option_arg.getAsInteger(0, count) ||
+ count < 0)
+ error.SetErrorStringWithFormat(
+ "invalid integer value for option '%s'",
+ option_arg.str().c_str());
+ else
+ m_count = count;
+ break;
+ }
+ case 's': {
+ int32_t start_position;
+ if (option_arg.empty() || option_arg.getAsInteger(0, start_position) ||
+ start_position < 0)
+ error.SetErrorStringWithFormat(
+ "invalid integer value for option '%s'",
+ option_arg.str().c_str());
+ else
+ m_start_position = start_position;
+ break;
+ }
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_count = kDefaultCount;
+ m_start_position = kDefaultStartPosition;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return llvm::makeArrayRef(g_thread_trace_dump_instructions_options);
+ }
+
+ static const uint32_t kDefaultCount = 20;
+ static const uint32_t kDefaultStartPosition = 0;
+
+ // Instance variables to hold the values for command options.
+ uint32_t m_count;
+ uint32_t m_start_position;
+ };
+
+ CommandObjectTraceDumpInstructions(CommandInterpreter &interpreter)
+ : CommandObjectIterateOverThreads(
+ interpreter, "thread trace dump instructions",
+ "Dump the traced instructions for one or more threads. If no "
+ "threads are specified, show the current thread. Use the "
+ "thread-index \"all\" to see all threads.",
+ nullptr,
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused),
+ m_options(), m_create_repeat_command_just_invoked(false) {}
+
+ ~CommandObjectTraceDumpInstructions() override = default;
+
+ Options *GetOptions() override { return &m_options; }
+
+ const char *GetRepeatCommand(Args ¤t_command_args,
+ uint32_t index) override {
+ current_command_args.GetCommandString(m_repeat_command);
+ m_create_repeat_command_just_invoked = true;
+ return m_repeat_command.c_str();
+ }
+
+protected:
+ bool DoExecute(Args &args, CommandReturnObject &result) override {
+ bool status = CommandObjectIterateOverThreads::DoExecute(args, result);
+ PrepareRepeatArguments();
+ return status;
+ }
+
+ void PrepareRepeatArguments() {
+ m_repeat_start_position = m_options.m_count + GetStartPosition();
+ m_create_repeat_command_just_invoked = false;
+ }
+
+ bool IsRepeatCommand() {
+ return !m_repeat_command.empty() && !m_create_repeat_command_just_invoked;
+ }
+
+ uint32_t GetStartPosition() {
+ return IsRepeatCommand() ? m_repeat_start_position
+ : m_options.m_start_position;
+ }
+
+ bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override {
+ const TraceSP &trace_sp = m_exe_ctx.GetTargetSP()->GetTrace();
+ if (!trace_sp) {
+ result.SetError("error: this thread is not being traced");
+ return false;
+ }
+
+ ThreadSP thread_sp =
+ m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid);
+
+ trace_sp->DumpTraceInstructions(*thread_sp, result.GetOutputStream(),
+ m_options.m_count, GetStartPosition());
+ return true;
+ }
+
+ CommandOptions m_options;
+
+ // Repeat command helpers
+ std::string m_repeat_command;
+ bool m_create_repeat_command_just_invoked;
+ uint32_t m_repeat_start_position;
+};
+
+// CommandObjectMultiwordTraceDump
+class CommandObjectMultiwordTraceDump : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordTraceDump(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "dump",
+ "Commands for displaying trace information of the threads "
+ "in the current process.",
+ "thread trace dump <subcommand> [<subcommand objects>]") {
+ LoadSubCommand(
+ "instructions",
+ CommandObjectSP(new CommandObjectTraceDumpInstructions(interpreter)));
+ }
+ ~CommandObjectMultiwordTraceDump() override = default;
+};
+
+// CommandObjectMultiwordTrace
+class CommandObjectMultiwordTrace : public CommandObjectMultiword {
+public:
+ CommandObjectMultiwordTrace(CommandInterpreter &interpreter)
+ : CommandObjectMultiword(
+ interpreter, "trace",
+ "Commands for operating on traces of the threads in the current "
+ "process.",
+ "thread trace <subcommand> [<subcommand objects>]") {
+ LoadSubCommand("dump", CommandObjectSP(new CommandObjectMultiwordTraceDump(
+ interpreter)));
+ }
+
+ ~CommandObjectMultiwordTrace() override = default;
+};
+
// CommandObjectMultiwordThread
CommandObjectMultiwordThread::CommandObjectMultiwordThread(
@@ -2240,6 +2407,8 @@ CommandObjectMultiwordThread::CommandObjectMultiwordThread(
LoadSubCommand("plan", CommandObjectSP(new CommandObjectMultiwordThreadPlan(
interpreter)));
+ LoadSubCommand("trace",
+ CommandObjectSP(new CommandObjectMultiwordTrace(interpreter)));
}
CommandObjectMultiwordThread::~CommandObjectMultiwordThread() = default;
diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index ad2f5fdae8e73..2932cf029224a 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -1005,6 +1005,21 @@ let Command = "thread plan list" in {
Desc<"Display thread plans for unreported threads">;
}
+let Command = "thread trace dump instructions" in {
+ def thread_trace_dump_instructions_count : Option<"count", "c">, Group<1>,
+ Arg<"Count">,
+ Desc<"The number of instructions to display starting at the current "
+ "position in reverse order chronologically.">;
+ def thread_trace_dump_instructions_start_position:
+ Option<"start-position", "s">,
+ Group<1>,
+ Arg<"Index">,
+ Desc<"The position of the first instruction to print. Defaults to the "
+ "current position, i.e. where the thread is stopped. The instructions are "
+ "indexed in reverse order, which means that a start position of 0 refers "
+ "to the last instruction chronologically.">;
+}
+
let Command = "type summary add" in {
def type_summary_add_category : Option<"category", "w">, Arg<"Name">,
Desc<"Add this to the given category instead of the default one.">;
diff --git a/lldb/source/Plugins/Process/CMakeLists.txt b/lldb/source/Plugins/Process/CMakeLists.txt
index 91f20ec22ac55..a028793e32f38 100644
--- a/lldb/source/Plugins/Process/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/CMakeLists.txt
@@ -18,3 +18,4 @@ add_subdirectory(Utility)
add_subdirectory(elf-core)
add_subdirectory(mach-core)
add_subdirectory(minidump)
+add_subdirectory(Trace)
diff --git a/lldb/source/Plugins/Process/Trace/CMakeLists.txt b/lldb/source/Plugins/Process/Trace/CMakeLists.txt
new file mode 100644
index 0000000000000..10a5c60b112b1
--- /dev/null
+++ b/lldb/source/Plugins/Process/Trace/CMakeLists.txt
@@ -0,0 +1,13 @@
+add_lldb_library(lldbPluginProcessTrace PLUGIN
+ ProcessTrace.cpp
+
+ LINK_LIBS
+ lldbCore
+ lldbTarget
+ lldbUtility
+ lldbPluginProcessUtility
+ LINK_COMPONENTS
+ BinaryFormat
+ Object
+ Support
+ )
diff --git a/lldb/source/Plugins/Process/Trace/ProcessTrace.cpp b/lldb/source/Plugins/Process/Trace/ProcessTrace.cpp
new file mode 100644
index 0000000000000..f727336e4d216
--- /dev/null
+++ b/lldb/source/Plugins/Process/Trace/ProcessTrace.cpp
@@ -0,0 +1,128 @@
+//===-- ProcessTrace.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 "ProcessTrace.h"
+
+#include <memory>
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_trace;
+
+LLDB_PLUGIN_DEFINE(ProcessTrace)
+
+ConstString ProcessTrace::GetPluginNameStatic() {
+ static ConstString g_name("trace");
+ return g_name;
+}
+
+const char *ProcessTrace::GetPluginDescriptionStatic() {
+ return "Trace process plug-in.";
+}
+
+void ProcessTrace::Terminate() {
+ PluginManager::UnregisterPlugin(ProcessTrace::CreateInstance);
+}
+
+ProcessSP ProcessTrace::CreateInstance(TargetSP target_sp,
+ ListenerSP listener_sp,
+ const FileSpec *crash_file) {
+ return std::make_shared<ProcessTrace>(target_sp, listener_sp);
+}
+
+bool ProcessTrace::CanDebug(TargetSP target_sp, bool plugin_specified_by_name) {
+ return plugin_specified_by_name;
+}
+
+ProcessTrace::ProcessTrace(TargetSP target_sp, ListenerSP listener_sp)
+ : Process(target_sp, listener_sp) {}
+
+ProcessTrace::~ProcessTrace() {
+ Clear();
+ // We need to call finalize on the process before destroying ourselves to
+ // make sure all of the broadcaster cleanup goes as planned. If we destruct
+ // this class, then Process::~Process() might have problems trying to fully
+ // destroy the broadcaster.
+ Finalize();
+}
+
+ConstString ProcessTrace::GetPluginName() { return GetPluginNameStatic(); }
+
+uint32_t ProcessTrace::GetPluginVersion() { return 1; }
+
+void ProcessTrace::DidAttach(ArchSpec &process_arch) {
+ ListenerSP listener_sp(
+ Listener::MakeListener("lldb.process_trace.did_attach_listener"));
+ HijackProcessEvents(listener_sp);
+
+ SetCanJIT(false);
+ StartPrivateStateThread();
+ SetPrivateState(eStateStopped);
+
+ EventSP event_sp;
+ WaitForProcessToStop(llvm::None, &event_sp, true, listener_sp);
+
+ RestoreProcessEvents();
+
+ Process::DidAttach(process_arch);
+}
+
+bool ProcessTrace::UpdateThreadList(ThreadList &old_thread_list,
+ ThreadList &new_thread_list) {
+ return false;
+}
+
+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
+ // we have it all cached in the trace files.
+ return DoReadMemory(addr, buf, size, error);
+}
+
+void ProcessTrace::Clear() { m_thread_list.Clear(); }
+
+void ProcessTrace::Initialize() {
+ static llvm::once_flag g_once_flag;
+
+ llvm::call_once(g_once_flag, []() {
+ PluginManager::RegisterPlugin(GetPluginNameStatic(),
+ GetPluginDescriptionStatic(), CreateInstance);
+ });
+}
+
+ArchSpec ProcessTrace::GetArchitecture() {
+ return GetTarget().GetArchitecture();
+}
+
+bool ProcessTrace::GetProcessInfo(ProcessInstanceInfo &info) {
+ info.Clear();
+ info.SetProcessID(GetID());
+ info.SetArchitecture(GetArchitecture());
+ ModuleSP module_sp = GetTarget().GetExecutableModule();
+ if (module_sp) {
+ const bool add_exe_file_as_first_arg = false;
+ info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(),
+ add_exe_file_as_first_arg);
+ }
+ return true;
+}
+
+size_t ProcessTrace::DoReadMemory(addr_t addr, void *buf, size_t size,
+ Status &error) {
+ return 0;
+}
diff --git a/lldb/source/Plugins/Process/Trace/ProcessTrace.h b/lldb/source/Plugins/Process/Trace/ProcessTrace.h
new file mode 100644
index 0000000000000..450aa1e91d8fc
--- /dev/null
+++ b/lldb/source/Plugins/Process/Trace/ProcessTrace.h
@@ -0,0 +1,86 @@
+//===-- ProcessTrace.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_PROCESS_TRACE_PROCESSTRACE_H
+#define LLDB_SOURCE_PLUGINS_PROCESS_TRACE_PROCESSTRACE_H
+
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Status.h"
+
+namespace lldb_private {
+namespace process_trace {
+
+class ProcessTrace : public Process {
+public:
+ static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp,
+ lldb::ListenerSP listener_sp,
+ const FileSpec *crash_file_path);
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static ConstString GetPluginNameStatic();
+
+ static const char *GetPluginDescriptionStatic();
+
+ ProcessTrace(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp);
+
+ ~ProcessTrace() override;
+
+ bool CanDebug(lldb::TargetSP target_sp,
+ bool plugin_specified_by_name) override;
+
+ void DidAttach(ArchSpec &process_arch) override;
+
+ DynamicLoader *GetDynamicLoader() override { return nullptr; }
+
+ SystemRuntime *GetSystemRuntime() override { return nullptr; }
+
+ ConstString GetPluginName() override;
+
+ uint32_t GetPluginVersion() override;
+
+ Status DoDestroy() override;
+
+ void RefreshStateAfterStop() override;
+
+ Status WillResume() override {
+ Status error;
+ error.SetErrorStringWithFormat(
+ "error: %s does not support resuming processes",
+ GetPluginName().GetCString());
+ return error;
+ }
+
+ bool IsAlive() override;
+
+ bool WarnBeforeDetach() const override { return false; }
+
+ size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ Status &error) override;
+
+ size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ Status &error) override;
+
+ ArchSpec GetArchitecture();
+
+ bool GetProcessInfo(ProcessInstanceInfo &info) override;
+
+protected:
+ void Clear();
+
+ bool UpdateThreadList(ThreadList &old_thread_list,
+ ThreadList &new_thread_list) override;
+};
+
+} // namespace process_trace
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_PROCESS_TRACE_PROCESSTRACE_H
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
index c4e38e668c257..e2b24672b086f 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -10,6 +10,7 @@
#include "TraceIntelPTSessionFileParser.h"
#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/Target.h"
using namespace lldb;
using namespace lldb_private;
@@ -45,12 +46,21 @@ ConstString TraceIntelPT::GetPluginName() { return GetPluginNameStatic(); }
uint32_t TraceIntelPT::GetPluginVersion() { return 1; }
-void TraceIntelPT::Dump(lldb_private::Stream *s) const {}
+void TraceIntelPT::Dump(Stream *s) const {}
-Expected<lldb::TraceSP>
+Expected<TraceSP>
TraceIntelPT::CreateInstance(const json::Value &trace_session_file,
StringRef session_file_dir, Debugger &debugger) {
return TraceIntelPTSessionFileParser(debugger, trace_session_file,
session_file_dir)
.Parse();
}
+
+TraceSP TraceIntelPT::CreateInstance(const pt_cpu &pt_cpu,
+ const std::vector<TargetSP> &targets) {
+ TraceSP trace_instance(new TraceIntelPT(pt_cpu, targets));
+ for (const TargetSP &target_sp : targets)
+ target_sp->SetTrace(trace_instance);
+
+ return trace_instance;
+}
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
index ac8bccd0d932d..bf34d89a347e5 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -52,6 +52,21 @@ class TraceIntelPT : public Trace {
CreateInstance(const llvm::json::Value &trace_session_file,
llvm::StringRef session_file_dir, Debugger &debugger);
+ /// Create an instance of this class.
+ ///
+ /// \param[in] pt_cpu
+ /// The libipt.h cpu information needed for decoding correctling the
+ /// traces.
+ ///
+ /// \param[in] targets
+ /// The list of targets to associate with this trace instance
+ ///
+ /// \return
+ /// An intel-pt trace instance.
+ static lldb::TraceSP
+ CreateInstance(const pt_cpu &pt_cpu,
+ const std::vector<lldb::TargetSP> &targets);
+
static ConstString GetPluginNameStatic();
uint32_t GetPluginVersion() override;
@@ -59,13 +74,13 @@ class TraceIntelPT : public Trace {
llvm::StringRef GetSchema() override;
+private:
TraceIntelPT(const pt_cpu &pt_cpu, const std::vector<lldb::TargetSP> &targets)
: m_pt_cpu(pt_cpu) {
for (const lldb::TargetSP &target_sp : targets)
m_targets.push_back(target_sp);
}
-private:
pt_cpu m_pt_cpu;
std::vector<std::weak_ptr<Target>> m_targets;
};
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
index 33db8facdcec2..4b439000a153e 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
@@ -41,7 +41,8 @@ void TraceIntelPTSessionFileParser::ParseThread(
FileSpec trace_file(thread.trace_file);
NormalizePath(trace_file);
- ThreadSP thread_sp(new ThreadIntelPT(*process_sp, tid, trace_file));
+ ThreadSP thread_sp =
+ std::make_shared<ThreadIntelPT>(*process_sp, tid, trace_file);
process_sp->GetThreadList().AddThread(thread_sp);
}
@@ -60,7 +61,7 @@ Error TraceIntelPTSessionFileParser::ParseProcess(
m_debugger.GetTargetList().SetSelectedTarget(target_sp.get());
ProcessSP process_sp(target_sp->CreateProcess(
- /*listener*/ nullptr, /*plugin_name*/ StringRef(),
+ /*listener*/ nullptr, "trace",
/*crash_file*/ nullptr));
process_sp->SetID(static_cast<lldb::pid_t>(process.pid));
@@ -71,7 +72,16 @@ Error TraceIntelPTSessionFileParser::ParseProcess(
if (Error err = ParseModule(target_sp, module))
return err;
}
- return Error::success();
+
+ if (!process.threads.empty())
+ process_sp->GetThreadList().SetSelectedThreadByIndexID(0);
+
+ // We invoke DidAttach to create a correct stopped state for the process and
+ // its threads.
+ ArchSpec process_arch;
+ process_sp->DidAttach(process_arch);
+
+ return llvm::Error::success();
}
void TraceIntelPTSessionFileParser::ParsePTCPU(const JSONPTCPU &pt_cpu) {
@@ -105,7 +115,7 @@ Expected<TraceSP> TraceIntelPTSessionFileParser::Parse() {
return std::move(err);
}
- return std::make_shared<TraceIntelPT>(m_pt_cpu, m_targets);
+ return TraceIntelPT::CreateInstance(m_pt_cpu, m_targets);
}
namespace llvm {
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index 49af6c297cbcb..6ce613697825b 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -2997,6 +2997,10 @@ Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) {
return error;
}
+void Target::SetTrace(const TraceSP &trace_sp) { m_trace_sp = trace_sp; }
+
+const TraceSP &Target::GetTrace() { return m_trace_sp; }
+
Status Target::Attach(ProcessAttachInfo &attach_info, Stream *stream) {
auto state = eStateInvalid;
auto process_sp = GetProcessSP();
diff --git a/lldb/source/Target/Trace.cpp b/lldb/source/Target/Trace.cpp
index 1b83aec486b71..31b09376db24b 100644
--- a/lldb/source/Target/Trace.cpp
+++ b/lldb/source/Target/Trace.cpp
@@ -13,6 +13,8 @@
#include "llvm/Support/Format.h"
#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Stream.h"
using namespace lldb;
using namespace lldb_private;
@@ -76,3 +78,11 @@ Expected<StringRef> Trace::FindPluginSchema(StringRef name) {
return createInvalidPlugInError(name);
}
+
+void Trace::DumpTraceInstructions(Thread &thread, Stream &s, size_t count,
+ size_t start_position) const {
+ s.Printf("thread #%u: tid = %" PRIu64 ", total instructions = 1000\n",
+ thread.GetIndexID(), thread.GetID());
+ s.Printf(" would print %zu instructions from position %zu\n", count,
+ start_position);
+}
diff --git a/lldb/test/API/commands/trace/TestTraceDumpInstructions.py b/lldb/test/API/commands/trace/TestTraceDumpInstructions.py
new file mode 100644
index 0000000000000..d71e734a4c445
--- /dev/null
+++ b/lldb/test/API/commands/trace/TestTraceDumpInstructions.py
@@ -0,0 +1,92 @@
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+from lldbsuite.test.decorators import *
+
+class TestTraceDumpInstructions(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")
+
+ def testErrorMessages(self):
+ # We first check the output when there are no targets
+ self.expect("thread trace dump instructions",
+ substrs=["error: invalid target, create a target using the 'target create' command"],
+ error=True)
+
+ # We now check the output when there's a non-running target
+ self.expect("target create " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
+
+ self.expect("thread trace dump instructions",
+ substrs=["error: invalid process"],
+ error=True)
+
+ # Now we check the output when there's a running target without a trace
+ self.expect("b main")
+ self.expect("run")
+
+ self.expect("thread trace dump instructions",
+ substrs=["error: this thread is not being traced"],
+ error=True)
+
+ def testDumpInstructions(self):
+ self.expect("trace load -v " + os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"),
+ substrs=["intel-pt"])
+
+ self.expect("thread trace dump instructions",
+ substrs=['thread #1: tid = 3842849, total instructions = 1000',
+ 'would print 20 instructions from position 0'])
+
+ # We check if we can pass count and offset
+ self.expect("thread trace dump instructions --count 5 --start-position 10",
+ substrs=['thread #1: tid = 3842849, total instructions = 1000',
+ 'would print 5 instructions from position 10'])
+
+ # We check if we can access the thread by index id
+ self.expect("thread trace dump instructions 1",
+ substrs=['thread #1: tid = 3842849, total instructions = 1000',
+ 'would print 20 instructions from position 0'])
+
+ # We check that we get an error when using an invalid thread index id
+ self.expect("thread trace dump instructions 10", error=True,
+ substrs=['error: no thread with index: "10"'])
+
+ def testDumpInstructionsWithMultipleThreads(self):
+ # We load a trace with two threads
+ self.expect("trace load -v " + os.path.join(self.getSourceDir(), "intelpt-trace", "trace_2threads.json"))
+
+ # We print the instructions of two threads simultaneously
+ self.expect("thread trace dump instructions 1 2",
+ substrs=['''thread #1: tid = 3842849, total instructions = 1000
+ would print 20 instructions from position 0
+thread #2: tid = 3842850, total instructions = 1000
+ would print 20 instructions from position 0'''])
+
+ # We use custom --count and --start-position, saving the command to history for later
+ ci = self.dbg.GetCommandInterpreter()
+
+ result = lldb.SBCommandReturnObject()
+ ci.HandleCommand("thread trace dump instructions 1 2 --count 12 --start-position 5", result, True)
+ self.assertIn('''thread #1: tid = 3842849, total instructions = 1000
+ would print 12 instructions from position 5
+thread #2: tid = 3842850, total instructions = 1000
+ would print 12 instructions from position 5''', result.GetOutput())
+
+ # We use a repeat command and ensure the previous count is used and the start-position has moved to the next position
+ result = lldb.SBCommandReturnObject()
+ ci.HandleCommand("", result)
+ self.assertIn('''thread #1: tid = 3842849, total instructions = 1000
+ would print 12 instructions from position 17
+thread #2: tid = 3842850, total instructions = 1000
+ would print 12 instructions from position 17''', result.GetOutput())
+
+ ci.HandleCommand("", result)
+ self.assertIn('''thread #1: tid = 3842849, total instructions = 1000
+ would print 12 instructions from position 29
+thread #2: tid = 3842850, total instructions = 1000
+ would print 12 instructions from position 29''', result.GetOutput())
diff --git a/lldb/test/API/commands/trace/TestTraceLoad.py b/lldb/test/API/commands/trace/TestTraceLoad.py
index 673536a09dd62..c48d468ce7845 100644
--- a/lldb/test/API/commands/trace/TestTraceLoad.py
+++ b/lldb/test/API/commands/trace/TestTraceLoad.py
@@ -35,6 +35,10 @@ def testLoadTrace(self):
self.assertEqual("6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A", module.GetUUIDString())
+ # check that the Process and Thread objects were created correctly
+ self.expect("thread info", substrs=["tid = 3842849"])
+ self.expect("thread list", substrs=["Process 1234 stopped", "tid = 3842849"])
+
def testLoadInvalidTraces(self):
src_dir = self.getSourceDir()
diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json b/lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json
new file mode 100644
index 0000000000000..87609f2e3b277
--- /dev/null
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json
@@ -0,0 +1,35 @@
+{
+ "trace": {
+ "type": "intel-pt",
+ "pt_cpu": {
+ "vendor": "intel",
+ "family": 6,
+ "model": 79,
+ "stepping": 1
+ }
+ },
+ "processes": [
+ {
+ "pid": 1234,
+ "triple": "x86_64-*-linux",
+ "threads": [
+ {
+ "tid": 3842849,
+ "traceFile": "3842849.trace"
+ },
+ {
+ "tid": 3842850,
+ "traceFile": "3842849.trace"
+ }
+ ],
+ "modules": [
+ {
+ "file": "a.out",
+ "systemPath": "a.out",
+ "loadAddress": "0x0000000000400000",
+ "uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
+ }
+ ]
+ }
+ ]
+}
More information about the lldb-commits
mailing list