wallace updated this revision to Diff 298673.
wallace added a comment.
rebase
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
lldb/tools/intel-features/intel-pt/Decoder.cpp
Index: lldb/tools/intel-features/intel-pt/Decoder.cpp
===================================================================
--- lldb/tools/intel-features/intel-pt/Decoder.cpp
+++ lldb/tools/intel-features/intel-pt/Decoder.cpp
@@ -248,6 +248,8 @@
}
}
+#include <fstream>
+
void Decoder::ReadTraceDataAndImageInfo(lldb::SBProcess &sbprocess,
lldb::tid_t tid, lldb::SBError &sberror,
ThreadTraceInfo &threadTraceInfo) {
@@ -255,6 +257,7 @@
// for the first time in class
lldb::SBTrace &trace = threadTraceInfo.GetUniqueTraceInstance();
Buffer &pt_buffer = threadTraceInfo.GetPTBuffer();
+
lldb::SBError error;
if (pt_buffer.size() == 0) {
lldb::SBTraceOptions traceoptions;
@@ -295,6 +298,11 @@
}
std::fill(pt_buffer.begin() + bytes_written, pt_buffer.end(), 0);
+ std::ofstream of("/tmp/multi-file.trace", std::ios::binary | std::ios::out);
+ for (auto bait : pt_buffer)
+ of << bait;
+ of.close();
+
// Get information of all the modules of the inferior
lldb::SBTarget sbtarget = sbprocess.GetTarget();
ReadExecuteSectionInfos &readExecuteSectionInfos =
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,40 @@
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'''])
# 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 +83,125 @@
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 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
+ [1] no memory mapped at this address'''])
+
+ 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
+ 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
+ [ 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,41 @@
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 = GetTraceErrorStatus(thread))
+ s.Printf(" %s\n", llvm::toString(std::move(err)).c_str());
+
+ ForEachInstruction(
+ thread,
+ [&](size_t index, Expected<lldb::addr_t> &load_address) -> bool {
+ if (index >= start_position + count)
+ 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;
+ },
+ start_position);
}
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;
@@ -106,6 +108,33 @@
return GetTarget().GetArchitecture();
}
+const std::map<MemoryRegionInfo::RangeType, SectionSP> &
+ProcessTrace::GetSectionMap() {
+ if (m_section_map)
+ return *m_section_map;
+
+ m_section_map.emplace();
+
+ ModuleList &modules = GetTarget().GetImages();
+ SectionLoadList &load_list = GetTarget().GetSectionLoadList();
+ modules.ForEach([&](const ModuleSP &module_sp) {
+ SectionList *sections = module_sp->GetSectionList();
+ for (size_t i = 0; i < sections->GetSize(); i++) {
+ SectionSP section_sp = sections->GetSectionAtIndex(i);
+
+ addr_t load_addr = load_list.GetSectionLoadAddress(section_sp);
+ if (load_addr == LLDB_INVALID_ADDRESS)
+ continue;
+ MemoryRegionInfo::RangeType section_range(load_addr,
+ section_sp->GetByteSize());
+ m_section_map->insert(std::make_pair(section_range, section_sp));
+ }
+ return true;
+ });
+
+ return *m_section_map;
+}
+
bool ProcessTrace::GetProcessInfo(ProcessInstanceInfo &info) {
info.Clear();
info.SetProcessID(GetID());
@@ -121,5 +150,25 @@
size_t ProcessTrace::DoReadMemory(addr_t addr, void *buf, size_t size,
Status &error) {
+ const std::map<MemoryRegionInfo::RangeType, SectionSP> §ion_map =
+ GetSectionMap();
+
+ MemoryRegionInfo::RangeType address_range(addr, size);
+ // Doing (upper_bound - 1) gives us the maximum range whose base address is <=
+ // addr
+ auto it = section_map.upper_bound(address_range);
+ if (it != section_map.begin()) {
+ --it;
+ const SectionSP §ion = it->second;
+ const MemoryRegionInfo::RangeType §ion_range = it->first;
+
+ if (section_range.Contains(address_range) && section->GetObjectFile()) {
+ section->GetObjectFile()->CopyData(addr - section_range.GetRangeBase(),
+ size, buf);
+ return size;
+ }
+ }
+
+ error.SetErrorString("could not parse memory info");
return 0;
}
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -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,16 @@
llvm::StringRef GetSchema() override;
+ void ForEachInstruction(
+ const Thread &thread,
+ std::function<bool(size_t index, llvm::Expected<lldb::addr_t> &load_addr)>
+ callback,
+ size_t from = 0) override;
+
+ size_t GetInstructionCount(const Thread &thread) override;
+
+ llvm::Error GetTraceErrorStatus(const Thread &thread) override;
+
private:
friend class TraceIntelPTSessionFileParser;
@@ -68,8 +74,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<const 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,54 @@
: 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<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, Expected<lldb::addr_t> &load_addr)>
+ 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++) {
+ Expected<lldb::addr_t> load_addr = instructions[i].GetLoadAddress();
+ // We check the expected here to avoid forcing the callback check it.
+ (void)(bool) load_addr;
+
+ if (!callback(i, load_addr))
+ break;
+ }
+}
+
+size_t TraceIntelPT::GetInstructionCount(const Thread &thread) {
+ if (Expected<const DecodedThread &> decoded_thread = Decode(thread))
+ return decoded_thread->GetInstructions().size();
+ else
+ return 0;
+}
+
+Error TraceIntelPT::GetTraceErrorStatus(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/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<const 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(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<const 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,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:
+ 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;
+
+ /// Get the instruction pointer if this is not an error.
+ ///
+ /// \return
+ /// The instruction pointer address.
+ llvm::Expected<lldb::addr_t> GetLoadAddress() const;
+
+ /// 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;
+
+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(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.
+ llvm::ArrayRef<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,32 @@
+//===-- 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;
+using namespace llvm;
+
+bool IntelPTInstruction::IsError() const { return m_libipt_error_code < 0; }
+
+Expected<lldb::addr_t> IntelPTInstruction::GetLoadAddress() const {
+ 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() const {
+ return pt_errstr(pt_errcode(GetErrorCode()));
+}
+
+ArrayRef<IntelPTInstruction> DecodedThread::GetInstructions() const {
+ return makeArrayRef(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
@@ -117,7 +117,58 @@
/// \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, llvm::Expected<lldb::addr_t> &load_addr)>
+ 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 GetTraceErrorStatus(const Thread &thread) = 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"
@@ -78,6 +79,12 @@
static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp,
lldb::ListenerSP listener_sp,
const FileSpec *crash_file_path);
+
+ /// Create a map from virtual memory ranges to the sections that contain them.
+ const std::map<MemoryRegionInfo::RangeType, lldb::SectionSP> &GetSectionMap();
+
+ llvm::Optional<std::map<MemoryRegionInfo::RangeType, lldb::SectionSP>>
+ m_section_map;
};
} // namespace lldb_private
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits