wallace updated this revision to Diff 297736.
wallace added a comment.
nits
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D89283/new/
https://reviews.llvm.org/D89283
Files:
lldb/include/lldb/Target/Trace.h
lldb/include/lldb/Target/TraceSessionFileParser.h
lldb/include/lldb/Target/TraceThread.h
lldb/include/lldb/lldb-forward.h
lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
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/ThreadIntelPT.cpp
lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.h
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/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
lldb/source/Target/CMakeLists.txt
lldb/source/Target/Thread.cpp
lldb/source/Target/Trace.cpp
lldb/source/Target/TraceSessionFileParser.cpp
lldb/source/Target/TraceThread.cpp
lldb/test/API/commands/trace/TestTraceDumpInstructions.py
lldb/test/API/commands/trace/intelpt-trace/trace_bad_image.json
lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json
Index: lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json
===================================================================
--- /dev/null
+++ lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json
@@ -0,0 +1,31 @@
+{
+ "trace": {
+ "type": "intel-pt",
+ "pt_cpu": {
+ "vendor": "intel",
+ "family": 2123123,
+ "model": 12123123,
+ "stepping": 1231231
+ }
+ },
+ "processes": [
+ {
+ "pid": 1234,
+ "triple": "x86_64-*-linux",
+ "threads": [
+ {
+ "tid": 3842849,
+ "traceFile": "3842849.trace"
+ }
+ ],
+ "modules": [
+ {
+ "file": "a.out",
+ "systemPath": "a.out",
+ "loadAddress": "0x0000000000400000",
+ "uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
+ }
+ ]
+ }
+ ]
+}
Index: lldb/test/API/commands/trace/intelpt-trace/trace_bad_image.json
===================================================================
--- /dev/null
+++ lldb/test/API/commands/trace/intelpt-trace/trace_bad_image.json
@@ -0,0 +1,31 @@
+{
+ "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"
+ }
+ ],
+ "modules": [
+ {
+ "file": "a.out",
+ "systemPath": "a.out",
+ "loadAddress": "0x0000000000FFFFF0",
+ "uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
+ }
+ ]
+ }
+ ]
+}
Index: lldb/test/API/commands/trace/TestTraceDumpInstructions.py
===================================================================
--- lldb/test/API/commands/trace/TestTraceDumpInstructions.py
+++ lldb/test/API/commands/trace/TestTraceDumpInstructions.py
@@ -39,18 +39,41 @@
substrs=["intel-pt"])
self.expect("thread trace dump instructions",
- substrs=['thread #1: tid = 3842849, total instructions = 1000',
- 'would print 20 instructions from position 0'])
+ substrs=['''thread #1: tid = 3842849, total instructions = 22
+ [ 0] 0x40052d
+ [ 1] 0x40052d
+ [ 2] 0x400529
+ [ 3] 0x400525
+ [ 4] 0x400521
+ [ 5] 0x40052d
+ [ 6] 0x400529
+ [ 7] 0x400525
+ [ 8] 0x400521
+ [ 9] 0x40052d
+ [10] 0x400529
+ [11] 0x400525
+ [12] 0x400521
+ [13] 0x40052d
+ [14] 0x400529
+ [15] 0x400525
+ [16] 0x400521
+ [17] 0x40052d
+ [18] 0x400529
+ [19] 0x40051f'''])
# 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'])
+ substrs=['''thread #1: tid = 3842849, total instructions = 22
+ [10] 0x400529
+ [11] 0x400525
+ [12] 0x400521
+ [13] 0x40052d
+ [14] 0x400529'''])
# 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'])
+ substrs=['''thread #1: tid = 3842849, total instructions = 22
+ [ 0] 0x40052d'''])
# We check that we get an error when using an invalid thread index id
self.expect("thread trace dump instructions 10", error=True,
@@ -61,32 +84,68 @@
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'''])
+ self.expect("thread trace dump instructions 1 2 --count 2",
+ substrs=['''thread #1: tid = 3842849, total instructions = 22
+ [0] 0x40052d
+ [1] 0x40052d
+thread #2: tid = 3842850, total instructions = 22
+ [0] 0x40052d
+ [1] 0x40052d'''])
# 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())
+ ci.HandleCommand("thread trace dump instructions 1 2 --count 4 --start-position 5", result, True)
+ self.assertIn('''thread #1: tid = 3842849, total instructions = 22
+ [5] 0x40052d
+ [6] 0x400529
+ [7] 0x400525
+ [8] 0x400521
+thread #2: tid = 3842850, total instructions = 22
+ [5] 0x40052d
+ [6] 0x400529
+ [7] 0x400525
+ [8] 0x400521''', 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())
+ self.assertIn('''thread #1: tid = 3842849, total instructions = 22
+ [ 9] 0x40052d
+ [10] 0x400529
+ [11] 0x400525
+ [12] 0x400521
+thread #2: tid = 3842850, total instructions = 22
+ [ 9] 0x40052d
+ [10] 0x400529
+ [11] 0x400525
+ [12] 0x400521''', result.GetOutput())
+ result = lldb.SBCommandReturnObject()
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())
+ self.assertIn('''thread #1: tid = 3842849, total instructions = 22
+ [13] 0x40052d
+ [14] 0x400529
+ [15] 0x400525
+ [16] 0x400521
+thread #2: tid = 3842850, total instructions = 22
+ [13] 0x40052d
+ [14] 0x400529
+ [15] 0x400525
+ [16] 0x400521''', result.GetOutput())
+
+ def testWrongImage(self):
+ trace_definition_file = os.path.join(self.getSourceDir(), "intelpt-trace", "trace_bad_image.json")
+ self.expect("trace load " + trace_definition_file)
+ self.expect("thread trace dump instructions",
+ substrs=['''thread #1: tid = 3842849, total instructions = 3
+ [0] no memory mapped at this address'''])
+
+ def testWrongCPU(self):
+ trace_definition_file = os.path.join(self.getSourceDir(), "intelpt-trace", "trace_wrong_cpu.json")
+ self.expect("trace load " + trace_definition_file)
+ self.expect("thread trace dump instructions",
+ substrs=['''thread #1: tid = 3842849, total instructions = 0
+ Intel PT decoding error -27: 'unknown cpu'''])
Index: lldb/source/Target/TraceThread.cpp
===================================================================
--- lldb/source/Target/TraceThread.cpp
+++ lldb/source/Target/TraceThread.cpp
@@ -1,4 +1,4 @@
-//===-- ThreadIntelPT.cpp -------------------------------------------------===//
+//===-- TraceThread.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 "ThreadIntelPT.h"
+#include "lldb/Target/TraceThread.h"
#include <memory>
@@ -16,11 +16,10 @@
using namespace lldb;
using namespace lldb_private;
-using namespace lldb_private::trace_intel_pt;
-void ThreadIntelPT::RefreshStateAfterStop() {}
+void TraceThread::RefreshStateAfterStop() {}
-RegisterContextSP ThreadIntelPT::GetRegisterContext() {
+RegisterContextSP TraceThread::GetRegisterContext() {
if (!m_reg_context_sp)
m_reg_context_sp = CreateRegisterContextForFrame(nullptr);
@@ -28,11 +27,13 @@
}
RegisterContextSP
-ThreadIntelPT::CreateRegisterContextForFrame(StackFrame *frame) {
+TraceThread::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 ThreadIntelPT::CalculateStopInfo() { return false; }
+bool TraceThread::CalculateStopInfo() { return false; }
+
+const FileSpec &TraceThread::GetTraceFile() const { return m_trace_file; }
Index: lldb/source/Target/TraceSessionFileParser.cpp
===================================================================
--- lldb/source/Target/TraceSessionFileParser.cpp
+++ lldb/source/Target/TraceSessionFileParser.cpp
@@ -10,8 +10,11 @@
#include <sstream>
+#include "lldb/Core/Debugger.h"
#include "lldb/Core/Module.h"
+#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"
+#include "lldb/Target/TraceThread.h"
using namespace lldb;
using namespace lldb_private;
@@ -34,7 +37,6 @@
ModuleSpec module_spec;
module_spec.GetFileSpec() = local_file_spec;
module_spec.GetPlatformFileSpec() = system_file_spec;
- module_spec.SetObjectOffset(module.load_address.value);
if (module.uuid.hasValue())
module_spec.GetUUID().SetFromStringRef(*module.uuid);
@@ -42,7 +44,14 @@
Status error;
ModuleSP module_sp =
target_sp->GetOrCreateModule(module_spec, /*notify*/ false, &error);
- return error.ToError();
+
+ if (error.Fail())
+ return error.ToError();
+
+ bool load_addr_changed = false;
+ module_sp->SetLoadAddress(*target_sp, module.load_address.value, false,
+ load_addr_changed);
+ return llvm::Error::success();
}
Error TraceSessionFileParser::CreateJSONError(json::Path::Root &root,
@@ -87,6 +96,55 @@
return schema_builder.str();
}
+void 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);
+
+ ThreadSP thread_sp =
+ std::make_shared<TraceThread>(*process_sp, tid, trace_file);
+ process_sp->GetThreadList().AddThread(thread_sp);
+}
+
+Expected<TargetSP>
+TraceSessionFileParser::ParseProcess(const JSONProcess &process) {
+ TargetSP target_sp;
+ Status error = m_debugger.GetTargetList().CreateTarget(
+ m_debugger, /*user_exe_path*/ StringRef(), process.triple,
+ eLoadDependentsNo,
+ /*platform_options*/ nullptr, target_sp);
+
+ if (!target_sp)
+ return error.ToError();
+
+ m_debugger.GetTargetList().SetSelectedTarget(target_sp.get());
+
+ ProcessSP process_sp(target_sp->CreateProcess(
+ /*listener*/ nullptr, "trace",
+ /*crash_file*/ nullptr));
+ process_sp->SetID(static_cast<lldb::pid_t>(process.pid));
+
+ for (const JSONThread &thread : process.threads)
+ ParseThread(process_sp, thread);
+
+ for (const JSONModule &module : process.modules) {
+ if (Error err = ParseModule(target_sp, module))
+ return std::move(err);
+ }
+
+ 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 target_sp;
+}
+
namespace llvm {
namespace json {
Index: lldb/source/Target/Trace.cpp
===================================================================
--- lldb/source/Target/Trace.cpp
+++ lldb/source/Target/Trace.cpp
@@ -8,8 +8,6 @@
#include "lldb/Target/Trace.h"
-#include <sstream>
-
#include "llvm/Support/Format.h"
#include "lldb/Core/PluginManager.h"
@@ -79,10 +77,40 @@
return createInvalidPlugInError(name);
}
+static int GetNumberOfDigits(size_t num) {
+ int digits_count = 0;
+ do {
+ digits_count++;
+ num /= 10;
+ } while (num);
+ return digits_count;
+}
+
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);
+ size_t start_position) {
+ size_t instruction_count = GetInstructionCount(thread);
+ s.Printf("thread #%u: tid = %" PRIu64 ", total instructions = %zu\n",
+ thread.GetIndexID(), thread.GetID(), instruction_count);
+
+ int digits_count =
+ GetNumberOfDigits(std::min(instruction_count, start_position + count));
+
+ if (Error err = IsTraceFailed(thread))
+ s.Printf(" %s\n", llvm::toString(std::move(err)).c_str());
+
+ ForEachInstruction(thread,
+ [&](size_t index, const Trace::Instruction &insn) -> bool {
+ if (index >= start_position + count)
+ return false;
+
+ s.Printf(" [%*zu]", digits_count, index);
+ if (insn.IsError())
+ s.Printf(" %s", insn.GetErrorMessage().data());
+ else
+ s.Printf(" 0x%" PRIx64, insn.GetLoadAddress());
+ s.Printf("\n");
+
+ return true;
+ },
+ start_position);
}
Index: lldb/source/Target/Thread.cpp
===================================================================
--- lldb/source/Target/Thread.cpp
+++ lldb/source/Target/Thread.cpp
@@ -42,6 +42,7 @@
#include "lldb/Target/ThreadPlanStepThrough.h"
#include "lldb/Target/ThreadPlanStepUntil.h"
#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Target/Trace.h"
#include "lldb/Target/UnwindLLDB.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/RegularExpression.h"
@@ -51,6 +52,7 @@
#include "lldb/lldb-enumerations.h"
#include <memory>
+#include <sstream>
using namespace lldb;
using namespace lldb_private;
Index: lldb/source/Target/CMakeLists.txt
===================================================================
--- lldb/source/Target/CMakeLists.txt
+++ lldb/source/Target/CMakeLists.txt
@@ -67,6 +67,7 @@
ThreadSpec.cpp
Trace.cpp
TraceSessionFileParser.cpp
+ TraceThread.cpp
UnixSignals.cpp
UnwindAssembly.cpp
UnwindLLDB.cpp
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
@@ -9,8 +9,6 @@
#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTSESSIONFILEPARSER_H
#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTSESSIONFILEPARSER_H
-#include "intel-pt.h"
-
#include "TraceIntelPT.h"
#include "lldb/Target/TraceSessionFileParser.h"
@@ -38,8 +36,8 @@
TraceIntelPTSessionFileParser(Debugger &debugger,
const llvm::json::Value &trace_session_file,
llvm::StringRef session_file_dir)
- : TraceSessionFileParser(session_file_dir, GetSchema()),
- m_debugger(debugger), m_trace_session_file(trace_session_file) {}
+ : TraceSessionFileParser(debugger, session_file_dir, GetSchema()),
+ m_trace_session_file(trace_session_file) {}
/// \return
/// The JSON schema for the session data.
@@ -53,24 +51,14 @@
/// errors, return a null pointer.
llvm::Expected<lldb::TraceSP> Parse();
-private:
- llvm::Error ParseImpl();
-
- llvm::Error ParseProcess(const TraceSessionFileParser::JSONProcess &process);
+ lldb::TraceSP
+ CreateTraceIntelPTInstance(const pt_cpu &pt_cpu,
+ std::vector<lldb::TargetSP> &targets);
- void ParseThread(lldb::ProcessSP &process_sp,
- const TraceSessionFileParser::JSONThread &thread);
-
- void ParsePTCPU(const JSONPTCPU &pt_cpu);
+private:
+ pt_cpu ParsePTCPU(const JSONPTCPU &pt_cpu);
- Debugger &m_debugger;
const llvm::json::Value &m_trace_session_file;
-
- /// Objects created as product of the parsing
- /// \{
- pt_cpu m_pt_cpu;
- std::vector<lldb::TargetSP> m_targets;
- /// \}
};
} // namespace trace_intel_pt
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
@@ -8,10 +8,10 @@
#include "TraceIntelPTSessionFileParser.h"
-#include "ThreadIntelPT.h"
#include "lldb/Core/Debugger.h"
-#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadList.h"
+#include "lldb/Target/TraceThread.h"
using namespace lldb;
using namespace lldb_private;
@@ -34,88 +34,54 @@
return schema;
}
-void TraceIntelPTSessionFileParser::ParseThread(
- ProcessSP &process_sp, const TraceSessionFileParser::JSONThread &thread) {
- lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid);
-
- FileSpec trace_file(thread.trace_file);
- NormalizePath(trace_file);
-
- ThreadSP thread_sp =
- std::make_shared<ThreadIntelPT>(*process_sp, tid, trace_file);
- process_sp->GetThreadList().AddThread(thread_sp);
+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)};
}
-Error TraceIntelPTSessionFileParser::ParseProcess(
- const TraceSessionFileParser::JSONProcess &process) {
- TargetSP target_sp;
- Status error = m_debugger.GetTargetList().CreateTarget(
- m_debugger, /*user_exe_path*/ StringRef(), process.triple,
- eLoadDependentsNo,
- /*platform_options*/ nullptr, target_sp);
-
- if (!target_sp)
- return error.ToError();
-
- m_targets.push_back(target_sp);
- m_debugger.GetTargetList().SetSelectedTarget(target_sp.get());
-
- ProcessSP process_sp(target_sp->CreateProcess(
- /*listener*/ nullptr, "trace",
- /*crash_file*/ nullptr));
- process_sp->SetID(static_cast<lldb::pid_t>(process.pid));
-
- for (const TraceSessionFileParser::JSONThread &thread : process.threads)
- ParseThread(process_sp, thread);
-
- for (const TraceSessionFileParser::JSONModule &module : process.modules) {
- if (Error err = ParseModule(target_sp, module))
- return err;
+TraceSP TraceIntelPTSessionFileParser::CreateTraceIntelPTInstance(
+ const pt_cpu &pt_cpu, std::vector<TargetSP> &targets) {
+ std::vector<std::shared_ptr<TraceThread>> threads;
+ for (TargetSP &target_sp : targets) {
+ ThreadList &thread_list = target_sp->GetProcessSP()->GetThreadList();
+ for (size_t i = 0; i < thread_list.GetSize(); i++) {
+ // The top-level parser creates TraceThreads, so this is safe
+ threads.push_back(std::static_pointer_cast<TraceThread>(
+ thread_list.GetThreadAtIndex(i)));
+ }
}
- 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();
-}
+ TraceSP trace_instance(new TraceIntelPT(pt_cpu, threads));
+ for (const TargetSP &target_sp : targets)
+ target_sp->SetTrace(trace_instance);
-void TraceIntelPTSessionFileParser::ParsePTCPU(const JSONPTCPU &pt_cpu) {
- m_pt_cpu = {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)};
+ return trace_instance;
}
-Error TraceIntelPTSessionFileParser::ParseImpl() {
+Expected<TraceSP> TraceIntelPTSessionFileParser::Parse() {
json::Path::Root root("traceSession");
TraceSessionFileParser::JSONTraceSession<JSONTraceIntelPTSettings> session;
- if (!json::fromJSON(m_trace_session_file, session, root)) {
+ if (!json::fromJSON(m_trace_session_file, session, root))
return CreateJSONError(root, m_trace_session_file);
- }
-
- ParsePTCPU(session.trace.pt_cpu);
- for (const TraceSessionFileParser::JSONProcess &process : session.processes) {
- if (Error err = ParseProcess(process))
- return err;
- }
- return Error::success();
-}
-Expected<TraceSP> TraceIntelPTSessionFileParser::Parse() {
- if (Error err = ParseImpl()) {
- // Delete all targets that were created
- for (auto target_sp : m_targets)
+ std::vector<TargetSP> targets;
+ auto onError = [this, &targets]() {
+ // Delete all targets that were created so far in case of failures
+ for (TargetSP &target_sp : targets)
m_debugger.GetTargetList().DeleteTarget(target_sp);
- m_targets.clear();
- return std::move(err);
- }
+ };
- return TraceIntelPT::CreateInstance(m_pt_cpu, m_targets);
+ for (const TraceSessionFileParser::JSONProcess &process : session.processes) {
+ if (Expected<TargetSP> target_sp = ParseProcess(process))
+ targets.push_back(*target_sp);
+ else {
+ onError();
+ return target_sp.takeError();
+ }
+ }
+ return CreateTraceIntelPTInstance(ParsePTCPU(session.trace.pt_cpu), targets);
}
namespace llvm {
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -9,12 +9,8 @@
#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPT_H
#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPT_H
-#include "intel-pt.h"
-#include "llvm/ADT/Optional.h"
-
+#include "IntelPTDecoder.h"
#include "TraceIntelPTSessionFileParser.h"
-#include "lldb/Target/Trace.h"
-#include "lldb/lldb-private.h"
namespace lldb_private {
namespace trace_intel_pt {
@@ -52,21 +48,6 @@
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;
@@ -74,15 +55,42 @@
llvm::StringRef GetSchema() override;
+ const pt_cpu &GetIntelPTCPU();
+
+ void ForEachInstruction(
+ const Thread &thread,
+ std::function<bool(size_t index, const Instruction &insn)> callback,
+ size_t from = 0) override;
+
+ size_t GetInstructionCount(const Thread &thread) override;
+
+ llvm::Error IsTraceFailed(const Thread &thread) 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);
- }
+ friend class TraceIntelPTSessionFileParser;
+
+ /// \param[in] trace_threads
+ /// TraceThread instances, which are not live-processes and whose trace
+ /// files are fixed.
+ TraceIntelPT(const pt_cpu &pt_cpu,
+ const std::vector<std::shared_ptr<TraceThread>> &traced_threads);
+
+ /// Decode the trace of the given thread that, i.e. recontruct the traced
+ /// instructions. That trace must be managed by this class.
+ ///
+ /// \param[in] thread
+ /// If \a thread is a \a TraceThread, then its internal trace file will be
+ /// decoded. Live threads are not currently supported.
+ ///
+ /// \return
+ /// A \a DecodedThread instance if decoding was successful, or an error if
+ /// the thread's trace is not managed by this class or if the trace couldn't
+ /// be decoded.
+ llvm::Expected<const DecodedThread &> Decode(const Thread &thread);
pt_cpu m_pt_cpu;
- std::vector<std::weak_ptr<Target>> m_targets;
+ std::map<std::pair<lldb::pid_t, lldb::tid_t>, TraceThreadDecoder>
+ m_trace_threads;
};
} // namespace trace_intel_pt
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -11,6 +11,7 @@
#include "TraceIntelPTSessionFileParser.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Target/Target.h"
+#include "lldb/Target/TraceThread.h"
using namespace lldb;
using namespace lldb_private;
@@ -56,11 +57,57 @@
.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);
+TraceIntelPT::TraceIntelPT(
+ const pt_cpu &pt_cpu,
+ const std::vector<std::shared_ptr<TraceThread>> &traced_threads)
+ : m_pt_cpu(pt_cpu) {
+ for (const std::shared_ptr<TraceThread> &thread : traced_threads)
+ m_trace_threads.emplace(
+ std::piecewise_construct,
+ std::forward_as_tuple(
+ std::make_pair(thread->GetProcess()->GetID(), thread->GetID())),
+ std::forward_as_tuple(thread, pt_cpu));
+}
+
+const pt_cpu &TraceIntelPT::GetIntelPTCPU() { return m_pt_cpu; }
+
+Expected<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())
+ return createStringError(std::errc::invalid_argument,
+ "The thread %" PRIu64 " is not traced.",
+ thread.GetID());
+
+ return it->second.Decode();
+}
+
+void TraceIntelPT::ForEachInstruction(
+ const Thread &thread,
+ std::function<bool(size_t index, const Instruction &insn)> callback,
+ size_t from) {
+ Expected<const DecodedThread &> decoded_thread = Decode(thread);
+ if (!decoded_thread)
+ return;
+
+ const std::vector<IntelPTInstruction> &instructions =
+ decoded_thread->GetInstructions();
+ for (size_t i = from; i < instructions.size(); i++) {
+ if (!callback(i, instructions[i]))
+ break;
+ }
+}
+
+size_t TraceIntelPT::GetInstructionCount(const Thread &thread) {
+ if (Expected<const DecodedThread &> decoded_thread = Decode(thread))
+ return decoded_thread->GetInstructions().size();
+ else
+ return 0;
+}
- return trace_instance;
+Error TraceIntelPT::IsTraceFailed(const Thread &thread) {
+ if (Expected<const DecodedThread &> decoded_thread = Decode(thread))
+ return Error::success();
+ else
+ return decoded_thread.takeError();
}
Index: lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/ThreadIntelPT.h
+++ /dev/null
@@ -1,54 +0,0 @@
-//===-- ThreadIntelPT.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_THREADINTELPT_H
-#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_THREADINTELPT_H
-
-#include "lldb/Target/Thread.h"
-
-namespace lldb_private {
-namespace trace_intel_pt {
-
-class ThreadIntelPT : public Thread {
-public:
- /// Create an Intel PT-traced thread.
- ///
- /// \param[in] process
- /// The process that owns this thread.
- ///
- /// \param[in] tid
- /// The thread id of this thread.
- ///
- /// \param[in] trace_file
- /// The trace file for this thread.
- ///
- /// \param[in] pt_cpu
- /// The Intel CPU information required to decode the \a trace_file.
- ThreadIntelPT(Process &process, lldb::tid_t tid, const FileSpec &trace_file)
- : Thread(process, tid), m_trace_file(trace_file) {}
-
- void RefreshStateAfterStop() override;
-
- lldb::RegisterContextSP GetRegisterContext() override;
-
- lldb::RegisterContextSP
- CreateRegisterContextForFrame(StackFrame *frame) override;
-
-protected:
- bool CalculateStopInfo() override;
-
- lldb::RegisterContextSP m_thread_reg_ctx_sp;
-
-private:
- FileSpec m_trace_file;
-};
-
-} // namespace trace_intel_pt
-} // namespace lldb_private
-
-#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_THREADINTELPT_H
Index: lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
@@ -0,0 +1,78 @@
+//===-- IntelPTDecoder.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_DECODER_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODER_H
+
+#include "intel-pt.h"
+
+#include "DecodedThread.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/FileSpec.h"
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+class IntelPTDecoder {
+public:
+ /// \param[in] process
+ /// The process associated with the traces to decode.
+ ///
+ /// \param[in] pt_cpu
+ /// The libipt \a pt_cpu information of the CPU where the traces were
+ /// recorded.
+ IntelPTDecoder(Process &process, const pt_cpu &pt_cpu)
+ : m_process(process), m_pt_cpu(pt_cpu) {}
+
+ /// Decode a single-thread trace file.
+ ///
+ /// \param[in] trace_file
+ /// The binary trace file obtained from tracing a thread.
+ ///
+ /// \return
+ /// A \a DecodedTrace instance, or an error if decoding failed.
+ llvm::Expected<DecodedThread>
+ DecodeSingleThreadTraceFile(const FileSpec &trace_file);
+
+private:
+ Process &m_process;
+ pt_cpu m_pt_cpu;
+};
+
+/// \a lldb_private::TraceThread decoder that stores the output from decoding,
+/// avoiding recomputations, as decoding is expensive.
+class TraceThreadDecoder {
+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.
+ TraceThreadDecoder(const std::shared_ptr<TraceThread> &trace_thread,
+ const pt_cpu &pt_cpu)
+ : m_trace_thread(trace_thread), m_pt_cpu(pt_cpu), m_decoded_thread() {}
+
+ /// Decode the thread and store the result internally.
+ ///
+ /// \return
+ /// A \a DecodedThread instance or an error in case of failures.
+ llvm::Expected<const DecodedThread &> Decode();
+
+private:
+ TraceThreadDecoder(const TraceThreadDecoder &other) = delete;
+
+ std::shared_ptr<TraceThread> m_trace_thread;
+ pt_cpu m_pt_cpu;
+ llvm::Optional<DecodedThread> m_decoded_thread;
+ llvm::Optional<std::string> m_error_message;
+};
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODER_H
Index: lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
@@ -0,0 +1,244 @@
+//===-- IntelPTDecoder.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 "IntelPTDecoder.h"
+
+#include <fstream>
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/TraceThread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+/// Read the bytes from a trace file
+static std::vector<uint8_t> ReadTraceFile(const FileSpec &trace_file) {
+ char file_path[PATH_MAX];
+ trace_file.GetPath(file_path, sizeof(file_path));
+ std::ifstream src(file_path, std::ios::in | std::ios_base::binary);
+ std::vector<uint8_t> trace((std::istreambuf_iterator<char>(src)),
+ std::istreambuf_iterator<char>());
+ return trace;
+}
+
+/// Get a list of the sections of the given process that are Read or Exec,
+/// which are the only sections that could correspond to instructions traced.
+std::vector<lldb::SectionSP> static GetReadExecSections(Process &process) {
+ Target &target = process.GetTarget();
+
+ ModuleList &module_list = target.GetImages();
+ std::vector<SectionSP> sections;
+ for (size_t i = 0; i < module_list.GetSize(); i++) {
+ ModuleSP module_sp = module_list.GetModuleAtIndex(i);
+ SectionList *section_list = module_sp->GetSectionList();
+ for (size_t j = 0; j < section_list->GetSize(); j++) {
+ SectionSP section_sp = section_list->GetSectionAtIndex(j);
+ if (!section_sp)
+ continue;
+
+ uint32_t permissions = section_sp->GetPermissions();
+ if ((permissions & Permissions::ePermissionsReadable) &&
+ (permissions & Permissions::ePermissionsExecutable)) {
+ sections.push_back(section_sp);
+ }
+ }
+ }
+ return sections;
+}
+
+/// Decode all the instructions from a configured decoder.
+/// The decoding flow is based on
+/// https://github.com/intel/libipt/blob/master/doc/howto_libipt.md#the-instruction-flow-decode-loop
+/// but with some relaxation to allow for gaps in the trace.
+///
+/// \param[in] decoder
+/// A configured libipt \a pt_insn_decoder.
+///
+/// \param[out] instructions
+/// The instructions decoded.
+void DecodeInstructions(pt_insn_decoder &decoder,
+ std::vector<IntelPTInstruction> &instructions) {
+ // Error codes returned by libipt while decoding are:
+ // - negative: actual errors
+ // - positive or zero: not an error, but a list of bits signaling the status
+ // of the decoder
+ auto handle_pt_events = [&](int errcode) -> int {
+ while (errcode & pts_event_pending) {
+ pt_event event;
+ errcode = pt_insn_event(&decoder, &event, sizeof(event));
+ if (errcode < 0)
+ return errcode;
+
+ // The list of events are in
+ // https://github.com/intel/libipt/blob/master/doc/man/pt_qry_event.3.md
+ if (event.type == ptev_overflow)
+ instructions.push_back(IntelPTInstruction(-pte_overflow));
+ else if (event.type == ptev_enabled &&
+ event.variant.enabled.resumed == 0 && !instructions.empty()) {
+ /// This means that tracing is enabled from a different IP where it
+ /// stopped before
+ instructions.push_back(IntelPTInstruction(-pte_noip));
+ }
+ // Other events don't signal stream errors
+ }
+ return 0;
+ };
+
+ int errcode = 0;
+ while (true) {
+ // Try to sync the decoder. If it fails, then get
+ // the decoder_offset and try to sync again from
+ // the next synchronization point. If the
+ // new_decoder_offset is same as decoder_offset
+ // then we can't move to the next synchronization
+ // point. Otherwise, keep resyncing until either
+ // end of trace stream (eos) is reached or
+ // pt_insn_sync_forward() passes.
+ errcode = pt_insn_sync_forward(&decoder);
+ if (errcode == -pte_eos)
+ return;
+
+ if (errcode < 0) {
+ // There's a gap in the trace because we
+ // couldn't synchronize.
+ instructions.push_back(IntelPTInstruction(errcode));
+
+ uint64_t decoder_offset = 0;
+ int errcode_off = pt_insn_get_offset(&decoder, &decoder_offset);
+ if (errcode_off < 0) {
+ // We can't further synchronize.
+ return;
+ }
+
+ while (true) {
+ errcode = pt_insn_sync_forward(&decoder);
+ if (errcode >= 0)
+ break;
+
+ if (errcode == -pte_eos)
+ return;
+
+ uint64_t new_decoder_offset = 0;
+ errcode_off = pt_insn_get_offset(&decoder, &new_decoder_offset);
+ if (errcode_off < 0) {
+ // We can't further synchronize.
+ return;
+ } else if (new_decoder_offset <= decoder_offset) {
+ // We tried resyncing the decoder and
+ // decoder didn't make any progress because
+ // the offset didn't change. We will not
+ // make any progress further. Hence,
+ // returning in this situation.
+ return;
+ }
+ // We'll try again starting from a new offset.
+ decoder_offset = new_decoder_offset;
+ }
+ }
+
+ // We have synchronized, so we can start decoding
+ // instructions and events.
+ while (true) {
+ errcode = handle_pt_events(errcode);
+ if (errcode < 0) {
+ instructions.push_back(IntelPTInstruction(errcode));
+ break;
+ }
+ pt_insn insn;
+ errcode = pt_insn_next(&decoder, &insn, sizeof(insn));
+ if (insn.iclass != ptic_error)
+ instructions.push_back(IntelPTInstruction(insn));
+
+ if (errcode == -pte_eos)
+ return;
+
+ if (errcode < 0) {
+ instructions.push_back(IntelPTInstruction(errcode));
+ break;
+ }
+ }
+ }
+}
+
+/// \param[out] instructions
+/// Container were decoded instructions will be stored.
+///
+/// \return
+/// A libipt error code in case of failure or 0 otherwise.
+int CreateDecoderAndDecode(Process &process, const pt_cpu &pt_cpu,
+ std::vector<uint8_t> &trace,
+ std::vector<IntelPTInstruction> &instructions) {
+ pt_config config;
+ pt_config_init(&config);
+ config.cpu = pt_cpu;
+
+ if (int errcode = pt_cpu_errata(&config.errata, &config.cpu))
+ return errcode;
+
+ config.begin = trace.data();
+ config.end = trace.data() + trace.size();
+
+ pt_insn_decoder *decoder = pt_insn_alloc_decoder(&config);
+ if (decoder == nullptr)
+ return pte_nomem;
+
+ pt_image *image = pt_insn_get_image(decoder);
+ for (SectionSP §ion_sp : GetReadExecSections(process)) {
+ char path[PATH_MAX];
+ section_sp->GetModule()->GetPlatformFileSpec().GetPath(path, sizeof(path));
+ if (int errcode = pt_image_add_file(
+ image, path, section_sp->GetFileOffset(), section_sp->GetByteSize(),
+ nullptr, section_sp->GetLoadBaseAddress(&process.GetTarget()))) {
+ pt_insn_free_decoder(decoder);
+ return errcode;
+ }
+ }
+ DecodeInstructions(*decoder, instructions);
+ // We'll need the instructions in reverse order chronologically, so we
+ // reverse them now
+ std::reverse(instructions.begin(), instructions.end());
+
+ pt_insn_free_decoder(decoder);
+ return pte_ok;
+}
+
+Expected<DecodedThread>
+IntelPTDecoder::DecodeSingleThreadTraceFile(const FileSpec &trace_file) {
+ std::vector<IntelPTInstruction> instructions;
+ std::vector<uint8_t> trace = ReadTraceFile(trace_file);
+ if (int errcode =
+ CreateDecoderAndDecode(m_process, m_pt_cpu, trace, instructions))
+ return createStringError(std::errc::invalid_argument,
+ "Intel PT decoding error %d: '%s'", errcode,
+ pt_errstr(pt_errcode(errcode)));
+ return DecodedThread(std::move(instructions));
+}
+
+Expected<const DecodedThread &> TraceThreadDecoder::Decode() {
+ if (!m_decoded_thread.hasValue() && !m_error_message.hasValue()) {
+ if (llvm::Expected<DecodedThread> decoded_thread =
+ IntelPTDecoder(*m_trace_thread->GetProcess(), m_pt_cpu)
+ .DecodeSingleThreadTraceFile(m_trace_thread->GetTraceFile()))
+ m_decoded_thread = *decoded_thread;
+ else
+ // We create a copy of the error message, as we'll create a new instance
+ // of llvm::Error whenever this function is invoked. We have to do this
+ // because llvm::Error is only movable, not copyable.
+ m_error_message = llvm::toString(decoded_thread.takeError());
+ }
+
+ if (m_decoded_thread.hasValue())
+ return *m_decoded_thread;
+ else
+ return llvm::createStringError(std::errc::invalid_argument,
+ m_error_message->c_str());
+}
Index: lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
@@ -0,0 +1,93 @@
+//===-- DecodedThread.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_DECODEDTHREAD_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H
+
+#include <vector>
+
+#include "lldb/Target/Trace.h"
+
+#include "intel-pt.h"
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+/// \class IntelPTInstruction
+/// An instruction obtained from decoding a trace. It is either an actual
+/// instruction or an error indicating a gap in the trace.
+///
+/// Gaps in the trace can come in a few flavors:
+/// - tracing gaps (e.g. tracing was paused and then resumed)
+/// - tracing errors (e.g. buffer overflow)
+/// - decoding errors (e.g. some memory region couldn't be decoded)
+/// As mentioned, any gap is represented as an error in this class.
+class IntelPTInstruction : public Trace::Instruction {
+public:
+ IntelPTInstruction() = delete;
+
+ IntelPTInstruction(const pt_insn &pt_insn)
+ : m_pt_insn(pt_insn), m_libipt_error_code(0) {}
+
+ IntelPTInstruction(int libipt_error_code)
+ : m_libipt_error_code(libipt_error_code) {}
+
+ /// Check if this object represents an error (i.e. a gap).
+ ///
+ /// \return
+ /// Whether this object represents an error.
+ bool IsError() const override;
+
+ /// Get the instruction pointer if this is not an error.
+ ///
+ /// \return
+ /// The instruction pointer address.
+ lldb::addr_t GetLoadAddress() const override;
+
+ /// Return the libipt error code.
+ ///
+ /// libipt error codes are negative numbers.
+ ///
+ /// \return
+ /// 0 if it is not an error, or the error value otherwise.
+ int GetErrorCode() const;
+
+ /// Return a descriptive error message if this is an error.
+ ///
+ /// \return
+ /// The error message.
+ llvm::StringRef GetErrorMessage() const override;
+
+private:
+ pt_insn m_pt_insn;
+ int m_libipt_error_code;
+};
+
+/// \class DecodedThread
+/// Class holding the instructions and function call hierarchy obtained from
+/// decoding a trace.
+class DecodedThread {
+public:
+ DecodedThread(std::vector<IntelPTInstruction> &&instructions)
+ : m_instructions(instructions) {}
+
+ /// Get the instructions from the decoded trace. Some of them might indicate
+ /// errors (i.e. gaps) in the trace.
+ ///
+ /// \return
+ /// The instructions of the trace.
+ const std::vector<IntelPTInstruction> &GetInstructions() const;
+
+private:
+ std::vector<IntelPTInstruction> m_instructions;
+};
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_DECODEDTHREAD_H
Index: lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
@@ -0,0 +1,26 @@
+//===-- DecodedThread.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 "DecodedThread.h"
+
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+
+bool IntelPTInstruction::IsError() const { return m_libipt_error_code < 0; }
+
+lldb::addr_t IntelPTInstruction::GetLoadAddress() const { return m_pt_insn.ip; }
+
+int IntelPTInstruction::GetErrorCode() const { return m_libipt_error_code; }
+
+llvm::StringRef IntelPTInstruction::GetErrorMessage() const {
+ return pt_errstr(pt_errcode(GetErrorCode()));
+}
+
+const std::vector<IntelPTInstruction> &DecodedThread::GetInstructions() const {
+ return m_instructions;
+}
Index: lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
+++ lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt
@@ -10,9 +10,10 @@
find_library(LIBIPT_LIBRARY ipt PATHS ${LIBIPT_LIBRARY_PATH} REQUIRED)
add_lldb_library(lldbPluginTraceIntelPT PLUGIN
+ DecodedThread.cpp
+ IntelPTDecoder.cpp
TraceIntelPT.cpp
TraceIntelPTSessionFileParser.cpp
- ThreadIntelPT.cpp
LINK_LIBS
lldbCore
Index: lldb/include/lldb/lldb-forward.h
===================================================================
--- lldb/include/lldb/lldb-forward.h
+++ lldb/include/lldb/lldb-forward.h
@@ -228,6 +228,7 @@
class ThreadSpec;
class Trace;
class TraceSessionFileParser;
+class TraceThread;
class TraceOptions;
class Type;
class TypeAndOrName;
Index: lldb/include/lldb/Target/TraceThread.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Target/TraceThread.h
@@ -0,0 +1,59 @@
+//===-- TraceThread.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_TARGET_TRACETHREAD_H
+#define LLDB_TARGET_TRACETHREAD_H
+
+#include "lldb/Target/Thread.h"
+
+namespace lldb_private {
+
+/// \class TraceThread TraceThread.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 TraceThread : public Thread {
+public:
+ /// \param[in] process
+ /// The process who owns this thread.
+ ///
+ /// \param[in] tid
+ /// The tid of this thread.
+ ///
+ /// \param[in] trace_file.
+ /// The file that contains the list of instructions that were traced when
+ /// this thread was being executed.
+ TraceThread(Process &process, lldb::tid_t tid, const FileSpec trace_file)
+ : Thread(process, tid), m_trace_file(trace_file) {}
+
+ void RefreshStateAfterStop() override;
+
+ lldb::RegisterContextSP GetRegisterContext() override;
+
+ lldb::RegisterContextSP
+ CreateRegisterContextForFrame(StackFrame *frame) override;
+
+ /// \return
+ /// The trace file of this thread.
+ const FileSpec &GetTraceFile() const;
+
+protected:
+ bool CalculateStopInfo() override;
+
+ lldb::RegisterContextSP m_thread_reg_ctx_sp;
+
+private:
+ FileSpec m_trace_file;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_TARGET_TRACETHREAD_H
Index: lldb/include/lldb/Target/TraceSessionFileParser.h
===================================================================
--- lldb/include/lldb/Target/TraceSessionFileParser.h
+++ lldb/include/lldb/Target/TraceSessionFileParser.h
@@ -61,9 +61,10 @@
};
/// \}
- TraceSessionFileParser(llvm::StringRef session_file_dir,
+ TraceSessionFileParser(Debugger &debugger, llvm::StringRef session_file_dir,
llvm::StringRef schema)
- : m_session_file_dir(session_file_dir), m_schema(schema) {}
+ : m_debugger(debugger), m_session_file_dir(session_file_dir),
+ m_schema(schema) {}
/// Build the full schema for a Trace plug-in.
///
@@ -80,6 +81,10 @@
/// modifies the given file_spec.
void NormalizePath(lldb_private::FileSpec &file_spec);
+ void ParseThread(lldb::ProcessSP &process_sp, const JSONThread &thread);
+
+ llvm::Expected<lldb::TargetSP> ParseProcess(const JSONProcess &process);
+
llvm::Error ParseModule(lldb::TargetSP &target_sp, const JSONModule &module);
/// Create a user-friendly error message upon a JSON-parsing failure using the
@@ -96,6 +101,7 @@
llvm::Error CreateJSONError(llvm::json::Path::Root &root,
const llvm::json::Value &value);
+ Debugger &m_debugger;
std::string m_session_file_dir;
llvm::StringRef m_schema;
};
Index: lldb/include/lldb/Target/Trace.h
===================================================================
--- lldb/include/lldb/Target/Trace.h
+++ lldb/include/lldb/Target/Trace.h
@@ -35,6 +35,29 @@
class Trace : public PluginInterface,
public std::enable_shared_from_this<Trace> {
public:
+ /// Basic instruction gotten from a trace.
+ ///
+ /// Some instructions might represent errors (i.e. gaps) in the trace, as
+ /// there can be collection or reconstruction problems. Thus, users of this
+ /// class should always check if the instruction corresponds to an error or
+ /// not.
+ struct Instruction {
+ virtual ~Instruction() = default;
+
+ /// \return
+ /// \b true if this instruction corresponds to an error, or \b false
+ /// otherwise.
+ virtual bool IsError() const = 0;
+
+ /// \return
+ /// An error message if this instructions corresponds to an error.
+ virtual llvm::StringRef GetErrorMessage() const = 0;
+
+ /// \return
+ /// The load address of this instruction if it is not an error.
+ virtual lldb::addr_t GetLoadAddress() const = 0;
+ };
+
/// Dump the trace data that this plug-in has access to.
///
/// This function will dump all of the trace data for all threads in a user
@@ -117,7 +140,57 @@
/// \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;
+ size_t start_position);
+
+ /// Run the provided callback on the instructions of the trace of the given
+ /// thread.
+ ///
+ /// The instructions will be traversed starting at the \a from position
+ /// sequentially until the callback returns \b false, in which case no more
+ /// instructions are inspected.
+ ///
+ /// The purpose of this method is to allow inspecting traced instructions
+ /// without exposing the internal representation of how they are stored on
+ /// memory.
+ ///
+ /// \param[in] thread
+ /// The thread whose trace will be traversed.
+ ///
+ /// \param[in] callback
+ /// The callback to execute on each instruction. If it returns \b true for
+ /// the \a i-th instruction, then the instruction with index \a i + 1 will
+ /// be inspected. If it returns \b false, no more instructions will be
+ /// inspected.
+ ///
+ /// \param[in] from
+ /// The first index to execute the callback on.
+ virtual void ForEachInstruction(
+ const Thread &thread,
+ std::function<bool(size_t index, const Instruction &insn)> callback,
+ size_t from = 0) = 0;
+
+ /// Get the number of instructions of the trace of the given thread.
+ ///
+ /// \param[in] thread
+ /// The thread whose trace will be inspected.
+ ///
+ /// \return
+ /// The total number of instructions of the trace.
+ virtual size_t GetInstructionCount(const Thread &thread) = 0;
+
+ /// Get any errors in case the trace of the given thread can't be
+ /// reconstructed.
+ ///
+ /// If this returns an actual error, then no instructions are available in the
+ /// trace.
+ ///
+ /// \param[in] thread
+ /// The thread whose trace will be inspected.
+ ///
+ /// \return
+ /// An \a llvm::Error::success instance if the trace can be reconstructed,
+ /// or an actual Error in case of failures.
+ virtual llvm::Error IsTraceFailed(const Thread &thread) = 0;
};
} // namespace lldb_private
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits