wallace updated this revision to Diff 298768.
wallace added a comment.
removing unwanted changes in lldb/tools/intel-features/intel-pt/Decoder.cpp
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/ProcessTrace.h
lldb/include/lldb/Target/Trace.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/TraceIntelPT.cpp
lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
lldb/source/Target/ProcessTrace.cpp
lldb/source/Target/Trace.cpp
lldb/source/Target/TraceSessionFileParser.cpp
lldb/test/API/commands/trace/TestTraceDumpInstructions.py
lldb/test/API/commands/trace/intelpt-trace-multi-file/a.out
lldb/test/API/commands/trace/intelpt-trace-multi-file/bar.cpp
lldb/test/API/commands/trace/intelpt-trace-multi-file/bar.h
lldb/test/API/commands/trace/intelpt-trace-multi-file/foo.cpp
lldb/test/API/commands/trace/intelpt-trace-multi-file/foo.h
lldb/test/API/commands/trace/intelpt-trace-multi-file/ld-2.17.so
lldb/test/API/commands/trace/intelpt-trace-multi-file/libbar.so
lldb/test/API/commands/trace/intelpt-trace-multi-file/libfoo.so
lldb/test/API/commands/trace/intelpt-trace-multi-file/main.cpp
lldb/test/API/commands/trace/intelpt-trace-multi-file/multi-file-no-ld.json
lldb/test/API/commands/trace/intelpt-trace-multi-file/multi-file.json
lldb/test/API/commands/trace/intelpt-trace-multi-file/multi-file.trace
lldb/test/API/commands/trace/intelpt-trace/trace_bad_image.json
lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json
llvm/include/llvm/Support/Error.h
Index: llvm/include/llvm/Support/Error.h
===================================================================
--- llvm/include/llvm/Support/Error.h
+++ llvm/include/llvm/Support/Error.h
@@ -1007,6 +1007,17 @@
handleAllErrors(std::move(Err), [](const ErrorInfoBase &) {});
}
+/// Consume an Expected without doing anything. This method should be used
+/// only where the expected result is irrelevant.
+///
+/// Uses of this method are potentially indicative of design problems: If it's
+/// legitimate to do nothing while processing an "expected", the
+/// expected-producer might be more clearly refactored to return an Optional<T>.
+template <typename T> inline void consumeExpected(Expected<T> E) {
+ if (!E)
+ consumeError(E.takeError());
+}
+
/// Convert an Expected to an Optional without doing anything. This method
/// should be used only where an error can be considered a reasonable and
/// expected return value.
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/intelpt-trace-multi-file/multi-file.json
===================================================================
--- /dev/null
+++ lldb/test/API/commands/trace/intelpt-trace-multi-file/multi-file.json
@@ -0,0 +1,49 @@
+{
+ "trace": {
+ "type": "intel-pt",
+ "pt_cpu": {
+ "vendor": "intel",
+ "family": 6,
+ "model": 79,
+ "stepping": 1
+ }
+ },
+ "processes": [
+ {
+ "pid": 815455,
+ "triple": "x86_64-*-linux",
+ "threads": [
+ {
+ "tid": 815455,
+ "traceFile": "multi-file.trace"
+ }
+ ],
+ "modules": [
+ {
+ "file": "a.out",
+ "systemPath": "a.out",
+ "loadAddress": "0x0000000000400000",
+ "uuid": "729BF711-43CB-0017-AADF-83304FEA5EC4-D46B45DD"
+ },
+ {
+ "file": "libfoo.so",
+ "systemPath": "libfoo.so",
+ "loadAddress": "0x00007ffff7bd9000",
+ "uuid": "B30FFEDA-8BB2-3D08-4580-C5937ED11E2B-21BE778C"
+ },
+ {
+ "file": "libbar.so",
+ "systemPath": "libbar.so",
+ "loadAddress": "0x00007ffff79d7000",
+ "uuid": "6633B038-EA73-D1A6-FF9A-7D0C0EDF733D-95FEA2CC"
+ },
+ {
+ "file": "ld-2.17.so",
+ "systemPath": "ld-2.17.so",
+ "loadAddress": "0x00007ffff7ddb000",
+ "uuid": "27FFD1FB-C695-69C7-76E6-66474EED7233-95E6D727"
+ }
+ ]
+ }
+ ]
+}
Index: lldb/test/API/commands/trace/intelpt-trace-multi-file/multi-file-no-ld.json
===================================================================
--- /dev/null
+++ lldb/test/API/commands/trace/intelpt-trace-multi-file/multi-file-no-ld.json
@@ -0,0 +1,43 @@
+{
+ "trace": {
+ "type": "intel-pt",
+ "pt_cpu": {
+ "vendor": "intel",
+ "family": 6,
+ "model": 79,
+ "stepping": 1
+ }
+ },
+ "processes": [
+ {
+ "pid": 815455,
+ "triple": "x86_64-*-linux",
+ "threads": [
+ {
+ "tid": 815455,
+ "traceFile": "multi-file.trace"
+ }
+ ],
+ "modules": [
+ {
+ "file": "a.out",
+ "systemPath": "a.out",
+ "loadAddress": "0x0000000000400000",
+ "uuid": "729BF711-43CB-0017-AADF-83304FEA5EC4-D46B45DD"
+ },
+ {
+ "file": "libfoo.so",
+ "systemPath": "libfoo.so",
+ "loadAddress": "0x00007ffff7bd9000",
+ "uuid": "B30FFEDA-8BB2-3D08-4580-C5937ED11E2B-21BE778C"
+ },
+ {
+ "file": "libbar.so",
+ "systemPath": "libbar.so",
+ "loadAddress": "0x00007ffff79d7000",
+ "uuid": "6633B038-EA73-D1A6-FF9A-7D0C0EDF733D-95FEA2CC"
+ }
+ ]
+ }
+ ]
+}
Index: lldb/test/API/commands/trace/intelpt-trace-multi-file/main.cpp
===================================================================
--- /dev/null
+++ lldb/test/API/commands/trace/intelpt-trace-multi-file/main.cpp
@@ -0,0 +1,7 @@
+#include "foo.h"
+
+int main() {
+ int res = foo();
+ res++;
+ return res;
+}
Index: lldb/test/API/commands/trace/intelpt-trace-multi-file/foo.h
===================================================================
--- /dev/null
+++ lldb/test/API/commands/trace/intelpt-trace-multi-file/foo.h
@@ -0,0 +1 @@
+int foo();
Index: lldb/test/API/commands/trace/intelpt-trace-multi-file/foo.cpp
===================================================================
--- /dev/null
+++ lldb/test/API/commands/trace/intelpt-trace-multi-file/foo.cpp
@@ -0,0 +1,7 @@
+#include "bar.h"
+
+int foo() {
+ int y = bar();
+ y++;
+ return y;
+}
Index: lldb/test/API/commands/trace/intelpt-trace-multi-file/bar.h
===================================================================
--- /dev/null
+++ lldb/test/API/commands/trace/intelpt-trace-multi-file/bar.h
@@ -0,0 +1 @@
+int bar();
Index: lldb/test/API/commands/trace/intelpt-trace-multi-file/bar.cpp
===================================================================
--- /dev/null
+++ lldb/test/API/commands/trace/intelpt-trace-multi-file/bar.cpp
@@ -0,0 +1,5 @@
+int bar() {
+ int x = 1;
+ x++;
+ return x;
+}
Index: lldb/test/API/commands/trace/TestTraceDumpInstructions.py
===================================================================
--- lldb/test/API/commands/trace/TestTraceDumpInstructions.py
+++ lldb/test/API/commands/trace/TestTraceDumpInstructions.py
@@ -12,7 +12,7 @@
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",
@@ -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 = 21
+ [ 0] 0x40052d
+ [ 1] 0x400529
+ [ 2] 0x400525
+ [ 3] 0x400521
+ [ 4] 0x40052d
+ [ 5] 0x400529
+ [ 6] 0x400525
+ [ 7] 0x400521
+ [ 8] 0x40052d
+ [ 9] 0x400529
+ [10] 0x400525
+ [11] 0x400521
+ [12] 0x40052d
+ [13] 0x400529
+ [14] 0x400525
+ [15] 0x400521
+ [16] 0x40052d
+ [17] 0x400529
+ [18] 0x40051f
+ [19] 0x400518'''])
# 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 = 21
+ [10] 0x400525
+ [11] 0x400521
+ [12] 0x40052d
+ [13] 0x400529
+ [14] 0x400525'''])
# 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 = 21
+ [ 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,141 @@
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 = 21
+ [0] 0x40052d
+ [1] 0x400529
+thread #2: tid = 3842850, total instructions = 21
+ [0] 0x40052d
+ [1] 0x400529'''])
# 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 = 21
+ [5] 0x400529
+ [6] 0x400525
+ [7] 0x400521
+ [8] 0x40052d
+thread #2: tid = 3842850, total instructions = 21
+ [5] 0x400529
+ [6] 0x400525
+ [7] 0x400521
+ [8] 0x40052d''', 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 = 21
+ [ 9] 0x400529
+ [10] 0x400525
+ [11] 0x400521
+ [12] 0x40052d
+thread #2: tid = 3842850, total instructions = 21
+ [ 9] 0x400529
+ [10] 0x400525
+ [11] 0x400521
+ [12] 0x40052d''', 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 = 21
+ [13] 0x400529
+ [14] 0x400525
+ [15] 0x400521
+ [16] 0x40052d
+thread #2: tid = 3842850, total instructions = 21
+ [13] 0x400529
+ [14] 0x400525
+ [15] 0x400521
+ [16] 0x40052d''', result.GetOutput())
+
+ def testInvalidBounds(self):
+ self.expect("trace load -v " + os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"))
+
+ # The output should be truncated if asking for too many instructions
+ self.expect("thread trace dump instructions --count 20 --start-position 20",
+ substrs=['''thread #1: tid = 3842849, total instructions = 21
+ [20] 0x400511'''])
+
+ # Should print no instructions if the start-position is out of bounds
+ self.expect("thread trace dump instructions --start-position 23",
+ endstr='thread #1: tid = 3842849, total instructions = 21\n')
+
+ # Should fail with negative bounds
+ self.expect("thread trace dump instructions --start-position -1", error=True)
+ self.expect("thread trace dump instructions --count -1", error=True)
+
+ def testWrongImage(self):
+ self.expect("trace load " + os.path.join(self.getSourceDir(), "intelpt-trace", "trace_bad_image.json"))
+ self.expect("thread trace dump instructions",
+ substrs=['''thread #1: tid = 3842849, total instructions = 2
+ [0] no memory mapped at this address: 0x400518
+ [1] no memory mapped at this address: 0x400511'''])
+
+ def testWrongCPU(self):
+ self.expect("trace load " + os.path.join(self.getSourceDir(), "intelpt-trace", "trace_wrong_cpu.json"))
+ self.expect("thread trace dump instructions",
+ substrs=['''thread #1: tid = 3842849, total instructions = 0
+ [0] Intel PT decoding error -27: 'unknown cpu'''])
+
+ def testMultiFileTraceWithMissingModule(self):
+ self.expect("trace load " + os.path.join(self.getSourceDir(), "intelpt-trace-multi-file", "multi-file-no-ld.json"))
+
+ # The trace will be composed of the first and last instructions of main.cpp, with an unmapped region in between
+ # corresponding to the dynamic linker, which is missing in the json file. Because of this error, the decoder moves
+ # to the next synchronization point, skipping the instructions of bar.cpp and foo.cpp.
+ self.expect("thread trace dump instructions",
+ substrs=['''thread #1: tid = 815455, total instructions = 11
+ [ 0] 0x40065f
+ [ 1] 0x40065a
+ [ 2] 0x400657
+ [ 3] 0x400654
+ [ 4] no memory mapped at this address: 0x7ffff7df1950
+ [ 5] 0x400516
+ [ 6] 0x400510
+ [ 7] 0x40054b
+ [ 8] 0x400546
+ [ 9] 0x400540
+ [10] 0x40064f'''])
+
+ def testMultiFileTrace(self):
+ self.expect("trace load " + os.path.join(self.getSourceDir(), "intelpt-trace-multi-file", "multi-file.json"))
+
+ # last instructions of main.cpp
+ self.expect("thread trace dump instructions --count 3",
+ substrs=['''thread #1: tid = 815455, total instructions = 1153''', '''
+ [0] 0x40065f
+ [1] 0x40065a
+ [2] 0x400657'''])
+
+ # bar.cpp
+ self.expect("thread trace dump instructions --count 3 --start-position 4",
+ substrs=['''
+ [4] 0x7ffff7bd9703
+ [5] 0x7ffff7bd9702
+ [6] 0x7ffff7bd96fe'''])
+
+ # foo.cpp
+ self.expect("thread trace dump instructions --count 3 --start-position 13",
+ substrs=['''
+ [13] 0x7ffff79d76a9
+ [14] 0x7ffff79d76a6
+ [15] 0x7ffff79d76a3'''])
+
+ # ld
+ self.expect("thread trace dump instructions --count 3 --start-position 21",
+ substrs=[ '''
+ [21] 0x7ffff7df1a16
+ [22] 0x7ffff7df1a12
+ [23] 0x7ffff7df1a0e'''])
+
+ # first instructions of main.cpp
+ self.expect("thread trace dump instructions --count 3 --start-position 1150",
+ substrs=[ '''
+ [1150] 0x400546
+ [1151] 0x400540
+ [1152] 0x40064f'''])
Index: lldb/source/Target/TraceSessionFileParser.cpp
===================================================================
--- lldb/source/Target/TraceSessionFileParser.cpp
+++ lldb/source/Target/TraceSessionFileParser.cpp
@@ -37,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);
@@ -45,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,
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,47 @@
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) {
+ s.Printf("thread #%u: tid = %" PRIu64 "", thread.GetIndexID(),
+ thread.GetID());
+
+ // Used later for formatting the instruction indices
+ int digits_count = GetNumberOfDigits(start_position + count);
+
+ if (SupportsInstructionsCount()) {
+ size_t instructions_count = GetInstructionCount(thread);
+ s.Printf(", total instructions = %zu", instructions_count);
+ digits_count =
+ std::min(digits_count, GetNumberOfDigits(instructions_count));
+ }
+
+ s.Printf("\n");
+
+ TraverseInstructions(
+ thread, start_position, TraceDirection::Backwards,
+ [&](size_t index, Expected<lldb::addr_t> load_address) -> bool {
+ if (index >= start_position + count) {
+ consumeExpected(std::move(load_address));
+ return false;
+ }
+
+ s.Printf(" [%*zu]", digits_count, index);
+ if (load_address)
+ s.Printf(" 0x%" PRIx64, *load_address);
+ else
+ s.Printf(" %s", toString(load_address.takeError()).c_str());
+ s.Printf("\n");
+
+ return true;
+ });
}
Index: lldb/source/Target/ProcessTrace.cpp
===================================================================
--- lldb/source/Target/ProcessTrace.cpp
+++ lldb/source/Target/ProcessTrace.cpp
@@ -12,6 +12,8 @@
#include "lldb/Core/Module.h"
#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/Target.h"
using namespace lldb;
@@ -121,5 +123,9 @@
size_t ProcessTrace::DoReadMemory(addr_t addr, void *buf, size_t size,
Status &error) {
- return 0;
+ Address resolved_address;
+ GetTarget().GetSectionLoadList().ResolveLoadAddress(addr, resolved_address);
+
+ return GetTarget().ReadMemoryFromFileCache(resolved_address, buf, size,
+ error);
}
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 {
@@ -59,6 +55,15 @@
llvm::StringRef GetSchema() override;
+ void TraverseInstructions(
+ const Thread &thread, size_t position, TraceDirection direction,
+ std::function<bool(size_t index, llvm::Expected<lldb::addr_t> load_addr)>
+ callback) override;
+
+ bool SupportsInstructionsCount() override;
+
+ size_t GetInstructionCount(const Thread &thread) override;
+
private:
friend class TraceIntelPTSessionFileParser;
@@ -68,8 +73,21 @@
TraceIntelPT(const pt_cpu &pt_cpu,
const std::vector<std::shared_ptr<ThreadTrace>> &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 ThreadTrace, 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<DecodedThread &> Decode(const Thread &thread);
+
pt_cpu m_pt_cpu;
- std::map<std::pair<lldb::pid_t, lldb::tid_t>, std::shared_ptr<ThreadTrace>>
+ std::map<std::pair<lldb::pid_t, lldb::tid_t>, ThreadTraceDecoder>
m_trace_threads;
};
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
@@ -64,5 +64,49 @@
: m_pt_cpu(pt_cpu) {
for (const std::shared_ptr<ThreadTrace> &thread : traced_threads)
m_trace_threads.emplace(
- std::make_pair(thread->GetProcess()->GetID(), thread->GetID()), thread);
+ std::piecewise_construct,
+ std::forward_as_tuple(
+ std::make_pair(thread->GetProcess()->GetID(), thread->GetID())),
+ std::forward_as_tuple(thread, pt_cpu));
+}
+
+Expected<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::TraverseInstructions(
+ const Thread &thread, size_t position, TraceDirection direction,
+ std::function<bool(size_t index, Expected<lldb::addr_t> load_addr)>
+ callback) {
+ Expected<DecodedThread &> decoded_thread = Decode(thread);
+ if (!decoded_thread) {
+ callback(position, decoded_thread.takeError());
+ return;
+ }
+
+ std::vector<IntelPTInstruction> instructions =
+ decoded_thread->GetInstructions();
+
+ int delta = direction == TraceDirection::Forwards ? -1 : 1;
+ for (uint64_t i = position; i < instructions.size() && i >= 0; i += delta)
+ if (!callback(i, instructions[i].GetLoadAddress()))
+ break;
+}
+
+bool TraceIntelPT::SupportsInstructionsCount() { return true; }
+
+size_t TraceIntelPT::GetInstructionCount(const Thread &thread) {
+ if (Expected<DecodedThread &> decoded_thread = Decode(thread))
+ return decoded_thread->GetInstructions().size();
+ else {
+ consumeError(decoded_thread.takeError());
+ return 0;
+ }
}
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::ThreadTrace decoder that stores the output from decoding,
+/// avoiding recomputations, as decoding is expensive.
+class ThreadTraceDecoder {
+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() {}
+
+ /// Decode the thread and store the result internally.
+ ///
+ /// \return
+ /// A \a DecodedThread instance or an error in case of failures.
+ llvm::Expected<DecodedThread &> Decode();
+
+private:
+ ThreadTraceDecoder(const ThreadTraceDecoder &other) = delete;
+
+ std::shared_ptr<ThreadTrace> 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,232 @@
+//===-- 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 "llvm/Support/MemoryBuffer.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadTrace.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+/// Move the decoder forward to the next synchronization point (i.e. next PSB
+/// packet).
+///
+/// Once the decoder is at that sync. point, it can start decoding instructions.
+///
+/// \return
+/// A negative number with the libipt error if we couldn't synchronize.
+/// Otherwise, a positive number with the synchronization status will be
+/// returned.
+int FindNextSynchronizationPoint(pt_insn_decoder &decoder) {
+ // 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.
+ int errcode = pt_insn_sync_forward(&decoder);
+
+ if (errcode != -pte_eos && errcode < 0) {
+ uint64_t decoder_offset = 0;
+ int errcode_off = pt_insn_get_offset(&decoder, &decoder_offset);
+ if (errcode_off >= 0) { // we could get the offset
+ while (true) {
+ errcode = pt_insn_sync_forward(&decoder);
+ if (errcode >= 0 || errcode == -pte_eos)
+ break;
+
+ uint64_t new_decoder_offset = 0;
+ errcode_off = pt_insn_get_offset(&decoder, &new_decoder_offset);
+ if (errcode_off < 0)
+ break; // We can't further synchronize.
+ 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,
+ // stopping in this situation.
+ break;
+ }
+ // We'll try again starting from a new offset.
+ decoder_offset = new_decoder_offset;
+ }
+ }
+ }
+
+ return errcode;
+}
+
+/// Before querying instructions, we need to query the events associated that
+/// instruction e.g. timing events like ptev_tick, or paging events like
+/// ptev_paging.
+///
+/// \return
+/// 0 if there were no errors processing the events, or a negative libipt
+/// error code in case of errors.
+int ProcessPTEvents(pt_insn_decoder &decoder, int errcode) {
+ while (errcode & pts_event_pending) {
+ pt_event event;
+ errcode = pt_insn_event(&decoder, &event, sizeof(event));
+ if (errcode < 0)
+ return errcode;
+ }
+ return 0;
+};
+
+/// 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.
+///
+/// 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
+///
+/// \param[in] decoder
+/// A configured libipt \a pt_insn_decoder.
+///
+/// \return
+/// The decoded instructions.
+static std::vector<IntelPTInstruction>
+DecodeInstructions(pt_insn_decoder &decoder) {
+ std::vector<IntelPTInstruction> instructions;
+
+ while (true) {
+ int errcode = FindNextSynchronizationPoint(decoder);
+ if (errcode == -pte_eos)
+ break;
+
+ if (errcode < 0) {
+ instructions.emplace_back(errcode);
+ break;
+ }
+
+ // We have synchronized, so we can start decoding
+ // instructions and events.
+ while (true) {
+ errcode = ProcessPTEvents(decoder, errcode);
+ if (errcode < 0) {
+ instructions.emplace_back(errcode);
+ break;
+ }
+ pt_insn insn;
+
+ errcode = pt_insn_next(&decoder, &insn, sizeof(insn));
+ if (errcode == -pte_eos)
+ break;
+
+ if (errcode < 0) {
+ instructions.emplace_back(insn, errcode);
+ break;
+ }
+
+ instructions.emplace_back(insn);
+ }
+ }
+
+ return instructions;
+}
+
+static Error CreateLibiptError(int errcode) {
+ return createStringError(std::errc::invalid_argument,
+ "Intel PT decoding error %d: '%s'", errcode,
+ pt_errstr(pt_errcode(errcode)));
+}
+
+/// Callback used by libipt for reading the process memory.
+///
+/// More information can be found in
+/// https://github.com/intel/libipt/blob/master/doc/man/pt_image_set_callback.3.md
+static int ReadProcessMemory(uint8_t *buffer, size_t size,
+ const pt_asid * /* unused */, uint64_t pc,
+ void *context) {
+ Process *process = reinterpret_cast<Process *>(context);
+
+ Status error;
+ int bytes_read = process->ReadMemory(pc, buffer, size, error);
+ if (error.Fail())
+ return -pte_nomap;
+ return bytes_read;
+}
+
+static Expected<std::vector<IntelPTInstruction>>
+CreateDecoderAndDecode(Process &process, const pt_cpu &pt_cpu,
+ MemoryBuffer &trace) {
+ pt_config config;
+ pt_config_init(&config);
+ config.cpu = pt_cpu;
+
+ if (int errcode = pt_cpu_errata(&config.errata, &config.cpu))
+ return CreateLibiptError(errcode);
+
+ config.begin =
+ reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferStart()));
+ config.end =
+ reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferEnd()));
+
+ pt_insn_decoder *decoder = pt_insn_alloc_decoder(&config);
+ if (!decoder)
+ return CreateLibiptError(pte_nomem);
+
+ pt_image *image = pt_insn_get_image(decoder);
+ if (int error = pt_image_set_callback(image, ReadProcessMemory, &process))
+ return CreateLibiptError(error);
+
+ std::vector<IntelPTInstruction> instructions = DecodeInstructions(*decoder);
+ // 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 instructions;
+}
+
+Expected<DecodedThread>
+IntelPTDecoder::DecodeSingleThreadTraceFile(const FileSpec &trace_file) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> buffer_or_error =
+ MemoryBuffer::getFile(trace_file.GetPath());
+ if (std::error_code err = buffer_or_error.getError())
+ return errorCodeToError(err);
+
+ if (Expected<std::vector<IntelPTInstruction>> instructions =
+ CreateDecoderAndDecode(m_process, m_pt_cpu, **buffer_or_error))
+ return DecodedThread(std::move(*instructions));
+ else
+ return instructions.takeError();
+}
+
+Expected<DecodedThread &> ThreadTraceDecoder::Decode() {
+ if (!m_decoded_thread.hasValue() && !m_error_message.hasValue()) {
+ IntelPTDecoder decoder(*m_trace_thread->GetProcess(), m_pt_cpu);
+
+ if (llvm::Expected<DecodedThread> decoded_thread =
+ decoder.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,95 @@
+//===-- 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:
+ IntelPTInstruction() = delete;
+
+ IntelPTInstruction(const pt_insn &pt_insn, int libipt_error_code = 0)
+ : m_pt_insn(pt_insn), m_libipt_error_code(libipt_error_code) {}
+
+ IntelPTInstruction(int libipt_error_code)
+ : m_pt_insn(), 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;
+
+ /// Get the instruction pointer if this is not an error.
+ ///
+ /// \return
+ /// The instruction pointer address.
+ llvm::Expected<lldb::addr_t> GetLoadAddress();
+
+ /// 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();
+
+private:
+ pt_insn m_pt_insn;
+ int m_libipt_error_code;
+
+ std::string m_custom_error_message;
+};
+
+/// \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(std::move(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.
+ std::vector<IntelPTInstruction> &GetInstructions();
+
+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,43 @@
+//===-- 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"
+
+#include <sstream>
+
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+bool IntelPTInstruction::IsError() const { return m_libipt_error_code < 0; }
+
+Expected<lldb::addr_t> IntelPTInstruction::GetLoadAddress() {
+ if (IsError())
+ return createStringError(std::errc::invalid_argument,
+ GetErrorMessage().data());
+ return m_pt_insn.ip;
+}
+
+int IntelPTInstruction::GetErrorCode() const { return m_libipt_error_code; }
+
+llvm::StringRef IntelPTInstruction::GetErrorMessage() {
+ if (GetErrorCode() == -pte_nomap) {
+ if (m_custom_error_message.empty()) {
+ std::ostringstream os;
+ os << pt_errstr(pt_errcode(GetErrorCode())) << ": 0x" << std::hex
+ << m_pt_insn.ip;
+ m_custom_error_message = os.str();
+ }
+ return m_custom_error_message;
+ }
+ return pt_errstr(pt_errcode(GetErrorCode()));
+}
+
+std::vector<IntelPTInstruction> &DecodedThread::GetInstructions() {
+ 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,6 +10,8 @@
find_library(LIBIPT_LIBRARY ipt PATHS ${LIBIPT_LIBRARY_PATH} REQUIRED)
add_lldb_library(lldbPluginTraceIntelPT PLUGIN
+ DecodedThread.cpp
+ IntelPTDecoder.cpp
TraceIntelPT.cpp
TraceIntelPTSessionFileParser.cpp
Index: lldb/include/lldb/Target/Trace.h
===================================================================
--- lldb/include/lldb/Target/Trace.h
+++ lldb/include/lldb/Target/Trace.h
@@ -35,6 +35,11 @@
class Trace : public PluginInterface,
public std::enable_shared_from_this<Trace> {
public:
+ enum class TraceDirection {
+ Forwards = 0,
+ Backwards,
+ };
+
/// 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 +122,61 @@
/// \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] position
+ /// The instruction position to start iterating on.
+ ///
+ /// \param[in] direction
+ /// If \b TraceDirection::Forwards, then then instructions will be
+ /// traversed forwards chronologically, i.e. with decrementing indices. If
+ /// \b TraceDirection::Backwards, the traversal is done backwards
+ /// chronologically, i.e. with incrementing indices.
+ ///
+ /// \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.
+ virtual void TraverseInstructions(
+ const Thread &thread, size_t position, TraceDirection direction,
+ std::function<bool(size_t index, llvm::Expected<lldb::addr_t> load_addr)>
+ callback) = 0;
+
+ /// Determine whether the trace plug-in supports the \a GetInstructionCount
+ /// method.
+ ///
+ /// Some trace plug-ins might prefer to provide instructions lazily on the
+ /// fly, being unable to provide the total number of instructions in a single
+ /// call.
+ ///
+ /// \return
+ /// \b true if invoking \a GetInstructionCount will return a correct
+ /// value, \b false otherwise.
+ virtual bool SupportsInstructionsCount() { return false; }
+
+ /// 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) { return 0; }
};
} // namespace lldb_private
Index: lldb/include/lldb/Target/ProcessTrace.h
===================================================================
--- lldb/include/lldb/Target/ProcessTrace.h
+++ lldb/include/lldb/Target/ProcessTrace.h
@@ -9,6 +9,7 @@
#ifndef LLDB_TARGET_PROCESSTRACE_H
#define LLDB_TARGET_PROCESSTRACE_H
+#include "lldb/Target/MemoryRegionInfo.h"
#include "lldb/Target/Process.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/Utility/Status.h"
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits