wallace updated this revision to Diff 357371.
wallace added a comment.

Address comments.

- I ended up creating new method Next(int count) and Prev(int count) in the 
cursor for the skip operation. It might be useful for other things. I didn't 
add the count parameter to the existing Prev and Next methods because it makes 
it ambiguous because of the
- I got skip out of the constructor of the Dumper and now the dumper doesn't 
reset the initial position of the cursor. The cursor is set up outside.
- Other improvements here and there


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D105531/new/

https://reviews.llvm.org/D105531

Files:
  lldb/include/lldb/Target/Trace.h
  lldb/include/lldb/Target/TraceCursor.h
  lldb/include/lldb/Target/TraceInstructionDumper.h
  lldb/source/Commands/CommandObjectThread.cpp
  lldb/source/Commands/Options.td
  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/TraceCursorIntelPT.cpp
  lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
  lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
  lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
  lldb/source/Target/CMakeLists.txt
  lldb/source/Target/Trace.cpp
  lldb/source/Target/TraceCursor.cpp
  lldb/source/Target/TraceInstructionDumper.cpp
  lldb/test/API/commands/trace/TestTraceDumpInstructions.py
  lldb/test/API/commands/trace/TestTraceStartStop.py
  
lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py

Index: lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py
===================================================================
--- lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py
+++ lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py
@@ -97,8 +97,8 @@
         self.expect("continue")
         self.expect("thread trace dump instructions", substrs=['main.cpp:4'])
         self.expect("thread trace dump instructions 3", substrs=['main.cpp:4'])
-        self.expect("thread trace dump instructions 1", error=True, substrs=['not traced'])
-        self.expect("thread trace dump instructions 2", error=True, substrs=['not traced'])
+        self.expect("thread trace dump instructions 1", substrs=['not traced'])
+        self.expect("thread trace dump instructions 2", substrs=['not traced'])
 
     @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
     def testStartMultipleLiveThreadsWithThreadStartAll(self):
@@ -128,9 +128,9 @@
 
         # We'll stop at the next breakpoint in thread 3, and nothing should be traced
         self.expect("continue")
-        self.expect("thread trace dump instructions 3", error=True, substrs=['not traced'])
-        self.expect("thread trace dump instructions 1", error=True, substrs=['not traced'])
-        self.expect("thread trace dump instructions 2", error=True, substrs=['not traced'])
+        self.expect("thread trace dump instructions 3", substrs=['not traced'])
+        self.expect("thread trace dump instructions 1", substrs=['not traced'])
+        self.expect("thread trace dump instructions 2", substrs=['not traced'])
 
     @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
     @testSBAPIAndCommands
Index: lldb/test/API/commands/trace/TestTraceStartStop.py
===================================================================
--- lldb/test/API/commands/trace/TestTraceStartStop.py
+++ lldb/test/API/commands/trace/TestTraceStartStop.py
@@ -65,11 +65,12 @@
         self.expect("r")
         self.expect("thread trace start")
         self.expect("n")
-        self.expect("thread trace dump instructions", substrs=["total instructions"])
+        self.expect("thread trace dump instructions", substrs=["""0x0000000000400511    movl   $0x0, -0x4(%rbp)
+    no more data"""])
         # process stopping should stop the thread
         self.expect("process trace stop")
         self.expect("n")
-        self.expect("thread trace dump instructions", error=True, substrs=["not traced"])
+        self.expect("thread trace dump instructions", substrs=["not traced"])
 
 
     @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
@@ -110,22 +111,32 @@
 
         # We can reconstruct the single instruction executed in the first line
         self.expect("n")
-        self.expect("thread trace dump instructions",
-            patterns=[f'''thread #1: tid = .*, total instructions = 1
+        self.expect("thread trace dump instructions -f",
+            patterns=[f'''thread #1: tid = .*
   a.out`main \+ 4 at main.cpp:2
-    \[0\] {ADDRESS_REGEX}    movl'''])
+    \[ 0\] {ADDRESS_REGEX}    movl'''])
 
         # We can reconstruct the instructions up to the second line
         self.expect("n")
-        self.expect("thread trace dump instructions",
-            patterns=[f'''thread #1: tid = .*, total instructions = 5
+        self.expect("thread trace dump instructions -f",
+            patterns=[f'''thread #1: tid = .*
   a.out`main \+ 4 at main.cpp:2
-    \[0\] {ADDRESS_REGEX}    movl .*
+    \[ 0\] {ADDRESS_REGEX}    movl .*
   a.out`main \+ 11 at main.cpp:4
-    \[1\] {ADDRESS_REGEX}    movl .*
-    \[2\] {ADDRESS_REGEX}    jmp  .* ; <\+28> at main.cpp:4
-    \[3\] {ADDRESS_REGEX}    cmpl .*
-    \[4\] {ADDRESS_REGEX}    jle  .* ; <\+20> at main.cpp:5'''])
+    \[ 1\] {ADDRESS_REGEX}    movl .*
+    \[ 2\] {ADDRESS_REGEX}    jmp  .* ; <\+28> at main.cpp:4
+    \[ 3\] {ADDRESS_REGEX}    cmpl .*
+    \[ 4\] {ADDRESS_REGEX}    jle  .* ; <\+20> at main.cpp:5'''])
+
+        self.expect("thread trace dump instructions",
+            patterns=[f'''thread #1: tid = .*
+  a.out`main \+ 32 at main.cpp:4
+    \[  0\] {ADDRESS_REGEX}    jle  .* ; <\+20> at main.cpp:5
+    \[ -1\] {ADDRESS_REGEX}    cmpl .*
+    \[ -2\] {ADDRESS_REGEX}    jmp  .* ; <\+28> at main.cpp:4
+    \[ -3\] {ADDRESS_REGEX}    movl .*
+  a.out`main \+ 4 at main.cpp:2
+    \[ -4\] {ADDRESS_REGEX}    movl .* '''])
 
         # We stop tracing
         self.expect("thread trace stop")
@@ -138,10 +149,15 @@
         # thread
         self.expect("thread trace start")
         self.expect("n")
+        self.expect("thread trace dump instructions -f",
+            patterns=[f'''thread #1: tid = .*
+  a.out`main \+ 20 at main.cpp:5
+    \[ 0\] {ADDRESS_REGEX}    xorl'''])
+
         self.expect("thread trace dump instructions",
-            patterns=[f'''thread #1: tid = .*, total instructions = 1
+            patterns=[f'''thread #1: tid = .*
   a.out`main \+ 20 at main.cpp:5
-    \[0\] {ADDRESS_REGEX}    xorl'''])
+    \[  0\] {ADDRESS_REGEX}    xorl'''])
 
         self.expect("c")
         # Now the process has finished, so the commands should fail
Index: lldb/test/API/commands/trace/TestTraceDumpInstructions.py
===================================================================
--- lldb/test/API/commands/trace/TestTraceDumpInstructions.py
+++ lldb/test/API/commands/trace/TestTraceDumpInstructions.py
@@ -35,8 +35,9 @@
             os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"),
             substrs=["intel-pt"])
 
-        self.expect("thread trace dump instructions --raw",
-            substrs=['''thread #1: tid = 3842849, total instructions = 21
+        self.expect("thread trace dump instructions --raw --count 21 -f",
+            substrs=['''thread #1: tid = 3842849
+    [ 0] 0x0000000000400511
     [ 1] 0x0000000000400518
     [ 2] 0x000000000040051f
     [ 3] 0x0000000000400529
@@ -58,19 +59,27 @@
     [19] 0x0000000000400529
     [20] 0x000000000040052d'''])
 
-        # We check if we can pass count and position
-        self.expect("thread trace dump instructions --count 5 --position 10 --raw",
-            substrs=['''thread #1: tid = 3842849, total instructions = 21
+        # We check if we can pass count and skip
+        self.expect("thread trace dump instructions --count 5 --skip 6 --raw -f",
+            substrs=['''thread #1: tid = 3842849
     [ 6] 0x0000000000400525
     [ 7] 0x0000000000400529
     [ 8] 0x000000000040052d
     [ 9] 0x0000000000400521
     [10] 0x0000000000400525'''])
 
+        self.expect("thread trace dump instructions --count 5 --skip 6 --raw",
+            substrs=['''thread #1: tid = 3842849
+    [ -6] 0x0000000000400525
+    [ -7] 0x0000000000400521
+    [ -8] 0x000000000040052d
+    [ -9] 0x0000000000400529
+    [-10] 0x0000000000400525'''])
+
         # We check if we can access the thread by index id
         self.expect("thread trace dump instructions 1 --raw",
-            substrs=['''thread #1: tid = 3842849, total instructions = 21
-    [ 1] 0x0000000000400518'''])
+            substrs=['''thread #1: tid = 3842849
+    [  0] 0x000000000040052d'''])
 
         # We check that we get an error when using an invalid thread index id
         self.expect("thread trace dump instructions 10", error=True,
@@ -83,85 +92,87 @@
 
         # We print the instructions of two threads simultaneously
         self.expect("thread trace dump instructions 1 2 --count 2",
-            substrs=['''thread #1: tid = 3842849, total instructions = 21
-  a.out`main + 28 at main.cpp:4
-    [19] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)
-    [20] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
-thread #2: tid = 3842850, total instructions = 21
-  a.out`main + 28 at main.cpp:4
-    [19] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)
-    [20] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5'''])
-
-        # We use custom --count and --position, saving the command to history for later
-        self.expect("thread trace dump instructions 1 2 --count 2 --position 20", inHistory=True,
-            substrs=['''thread #1: tid = 3842849, total instructions = 21
-  a.out`main + 28 at main.cpp:4
-    [19] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)
-    [20] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
-thread #2: tid = 3842850, total instructions = 21
-  a.out`main + 28 at main.cpp:4
-    [19] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)
-    [20] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5'''])
+            substrs=['''thread #1: tid = 3842849
+  a.out`main + 32 at main.cpp:4
+    [ 0] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
+    [-1] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)
+thread #2: tid = 3842850
+  a.out`main + 32 at main.cpp:4
+    [ 0] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
+    [-1] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)'''])
+
+        # We use custom --count and --skip, saving the command to history for later
+        self.expect("thread trace dump instructions 1 2 --count 2 --skip 2", inHistory=True,
+            substrs=['''thread #1: tid = 3842849
+  a.out`main + 24 at main.cpp:4
+    [-2] 0x0000000000400525    addl   $0x1, -0x8(%rbp)
+  a.out`main + 20 at main.cpp:5
+    [-3] 0x0000000000400521    xorl   $0x1, -0x4(%rbp)
+thread #2: tid = 3842850
+  a.out`main + 24 at main.cpp:4
+    [-2] 0x0000000000400525    addl   $0x1, -0x8(%rbp)
+  a.out`main + 20 at main.cpp:5
+    [-3] 0x0000000000400521    xorl   $0x1, -0x4(%rbp)'''])
 
         # We use a repeat command twice and ensure the previous count is used and the
         # start position moves with each command.
         self.expect("", inHistory=True,
-            substrs=['''thread #1: tid = 3842849, total instructions = 21
-  a.out`main + 20 at main.cpp:5
-    [17] 0x0000000000400521    xorl   $0x1, -0x4(%rbp)
+            substrs=['''thread #1: tid = 3842849
+  a.out`main + 32 at main.cpp:4
+    [-4] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
+    [-5] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)
+thread #2: tid = 3842850
+  a.out`main + 32 at main.cpp:4
+    [-4] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
+    [-5] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)'''])
+
+        self.expect("", inHistory=True,
+            substrs=['''thread #1: tid = 3842849
   a.out`main + 24 at main.cpp:4
-    [18] 0x0000000000400525    addl   $0x1, -0x8(%rbp)
-thread #2: tid = 3842850, total instructions = 21
+    [-6] 0x0000000000400525    addl   $0x1, -0x8(%rbp)
   a.out`main + 20 at main.cpp:5
-    [17] 0x0000000000400521    xorl   $0x1, -0x4(%rbp)
+    [-7] 0x0000000000400521    xorl   $0x1, -0x4(%rbp)
+thread #2: tid = 3842850
   a.out`main + 24 at main.cpp:4
-    [18] 0x0000000000400525    addl   $0x1, -0x8(%rbp)'''])
-
-        self.expect("", inHistory=True,
-            substrs=['''thread #1: tid = 3842849, total instructions = 21
-  a.out`main + 28 at main.cpp:4
-    [15] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)
-    [16] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
-thread #2: tid = 3842850, total instructions = 21
-  a.out`main + 28 at main.cpp:4
-    [15] 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)
-    [16] 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5'''])
+    [-6] 0x0000000000400525    addl   $0x1, -0x8(%rbp)
+  a.out`main + 20 at main.cpp:5
+    [-7] 0x0000000000400521    xorl   $0x1, -0x4(%rbp)'''])
 
     def testInvalidBounds(self):
         self.expect("trace load -v " +
             os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"))
 
         # The output should be work when too many instructions are asked
-        self.expect("thread trace dump instructions --count 20 --position 2",
-            substrs=['''thread #1: tid = 3842849, total instructions = 21
+        self.expect("thread trace dump instructions --count 20 -f",
+            substrs=['''thread #1: tid = 3842849
   a.out`main + 4 at main.cpp:2
-    [0] 0x0000000000400511    movl   $0x0, -0x4(%rbp)
+    [ 0] 0x0000000000400511    movl   $0x0, -0x4(%rbp)
   a.out`main + 11 at main.cpp:4
-    [1] 0x0000000000400518    movl   $0x0, -0x8(%rbp)
-    [2] 0x000000000040051f    jmp    0x400529                  ; <+28> at main.cpp:4'''])
+    [ 1] 0x0000000000400518    movl   $0x0, -0x8(%rbp)
+    [ 2] 0x000000000040051f    jmp    0x400529                  ; <+28> at main.cpp:4'''])
 
         # Should print no instructions if the position is out of bounds
-        self.expect("thread trace dump instructions --position 23",
-            endstr='thread #1: tid = 3842849, total instructions = 21\n')
+        self.expect("thread trace dump instructions --skip 23",
+            endstr='no more data\n')
 
         # Should fail with negative bounds
-        self.expect("thread trace dump instructions --position -1", error=True)
+        self.expect("thread trace dump instructions --skip -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] 0x0000000000400511    error: no memory mapped at this address
-    [1] 0x0000000000400518    error: no memory mapped at this address'''])
+        self.expect("thread trace dump instructions -f",
+            substrs=['''thread #1: tid = 3842849
+    [ 0] 0x0000000000400511    error: no memory mapped at this address
+    [ 1] 0x0000000000400518    error: 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 = 1
-    [0] error: unknown cpu'''])
+        self.expect("thread trace dump instructions -f",
+            substrs=['''thread #1: tid = 3842849
+    [ 0] error: unknown cpu'''])
 
     def testMultiFileTraceWithMissingModule(self):
         self.expect("trace load " +
@@ -181,8 +192,8 @@
         # line is printed showing the symbol context change.
         #
         # Finally, the instruction disassembly is included in the dump.
-        self.expect("thread trace dump instructions --count 50",
-            substrs=['''thread #1: tid = 815455, total instructions = 46
+        self.expect("thread trace dump instructions --count 50 -f",
+            substrs=['''thread #1: tid = 815455
   a.out`main + 15 at main.cpp:10
     [ 0] 0x000000000040066f    callq  0x400540                  ; symbol stub for: foo()
   a.out`symbol stub for: foo()
@@ -252,3 +263,83 @@
     [43] 0x00000000004006a4    movl   -0xc(%rbp), %ecx
     [44] 0x00000000004006a7    addl   %eax, %ecx
     [45] 0x00000000004006a9    movl   %ecx, -0xc(%rbp)'''])
+
+
+        self.expect("thread trace dump instructions --count 50",
+            substrs=['''thread #1: tid = 815455
+  a.out`main + 73 at main.cpp:16
+    [  0] 0x00000000004006a9    movl   %ecx, -0xc(%rbp)
+    [ -1] 0x00000000004006a7    addl   %eax, %ecx
+    [ -2] 0x00000000004006a4    movl   -0xc(%rbp), %ecx
+  libfoo.so`foo() + 35 at foo.cpp:6
+    [ -3] 0x00007ffff7bd9703    retq''',
+    '''[ -4] 0x00007ffff7bd9702    popq   %rbp
+    [ -5] 0x00007ffff7bd96fe    addq   $0x10, %rsp
+    [ -6] 0x00007ffff7bd96fb    movl   -0x4(%rbp), %eax
+  libfoo.so`foo() + 24 at foo.cpp:5
+    [ -7] 0x00007ffff7bd96f8    movl   %eax, -0x4(%rbp)
+    [ -8] 0x00007ffff7bd96f3    addl   $0x1, %eax
+    [ -9] 0x00007ffff7bd96f0    movl   -0x4(%rbp), %eax
+  libfoo.so`foo() + 13 at foo.cpp:4
+    [-10] 0x00007ffff7bd96ed    movl   %eax, -0x4(%rbp)
+  libbar.so`bar() + 26 at bar.cpp:4
+    [-11] 0x00007ffff79d76aa    retq''',
+    '''[-12] 0x00007ffff79d76a9    popq   %rbp
+    [-13] 0x00007ffff79d76a6    movl   -0x4(%rbp), %eax
+  libbar.so`bar() + 19 at bar.cpp:3
+    [-14] 0x00007ffff79d76a3    movl   %eax, -0x4(%rbp)
+    [-15] 0x00007ffff79d769e    addl   $0x1, %eax
+    [-16] 0x00007ffff79d769b    movl   -0x4(%rbp), %eax
+  libbar.so`bar() + 4 at bar.cpp:2
+    [-17] 0x00007ffff79d7694    movl   $0x1, -0x4(%rbp)
+  libbar.so`bar() + 1 at bar.cpp:1
+    [-18] 0x00007ffff79d7691    movq   %rsp, %rbp
+    [-19] 0x00007ffff79d7690    pushq  %rbp
+  libfoo.so`symbol stub for: bar()
+    [-20] 0x00007ffff7bd95d0    jmpq   *0x200a4a(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 32
+  libfoo.so`foo() + 8 at foo.cpp:4
+    [-21] 0x00007ffff7bd96e8    callq  0x7ffff7bd95d0            ; symbol stub for: bar()
+    [-22] 0x00007ffff7bd96e4    subq   $0x10, %rsp
+  libfoo.so`foo() + 1 at foo.cpp:3
+    [-23] 0x00007ffff7bd96e1    movq   %rsp, %rbp
+    [-24] 0x00007ffff7bd96e0    pushq  %rbp
+  a.out`symbol stub for: foo()
+    [-25] 0x0000000000400540    jmpq   *0x200ae2(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 40
+  a.out`main + 63 at main.cpp:16
+    [-26] 0x000000000040069f    callq  0x400540                  ; symbol stub for: foo()
+  a.out`main + 60 at main.cpp:14
+    [-27] 0x000000000040069c    movl   %ecx, -0xc(%rbp)
+    [-28] 0x000000000040069a    addl   %eax, %ecx
+    [-29] 0x0000000000400697    movl   -0xc(%rbp), %ecx
+  a.out`main + 52 [inlined] inline_function() + 18 at main.cpp:6
+    [-30] 0x0000000000400694    movl   -0x4(%rbp), %eax
+  a.out`main + 49 [inlined] inline_function() + 15 at main.cpp:5
+    [-31] 0x0000000000400691    movl   %eax, -0x4(%rbp)
+    [-32] 0x000000000040068c    addl   $0x1, %eax
+    [-33] 0x0000000000400689    movl   -0x4(%rbp), %eax
+  a.out`main + 34 [inlined] inline_function() at main.cpp:4
+    [-34] 0x0000000000400682    movl   $0x0, -0x4(%rbp)
+  a.out`main + 31 at main.cpp:12
+    [-35] 0x000000000040067f    movl   %eax, -0xc(%rbp)
+    [-36] 0x000000000040067a    addl   $0x1, %eax
+    [-37] 0x0000000000400677    movl   -0xc(%rbp), %eax
+  a.out`main + 20 at main.cpp:10
+    [-38] 0x0000000000400674    movl   %eax, -0xc(%rbp)
+    ...missing instructions
+    [-39] 0x00007ffff7df1950    error: no memory mapped at this address
+  a.out`(none)
+    [-40] 0x0000000000400516    jmpq   *0x200af4(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 16
+    [-41] 0x0000000000400510    pushq  0x200af2(%rip)            ; _GLOBAL_OFFSET_TABLE_ + 8
+  a.out`symbol stub for: foo() + 11
+    [-42] 0x000000000040054b    jmp    0x400510
+    [-43] 0x0000000000400546    pushq  $0x2
+    [-44] 0x0000000000400540    jmpq   *0x200ae2(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 40
+  a.out`main + 15 at main.cpp:10
+    [-45] 0x000000000040066f    callq  0x400540                  ; symbol stub for: foo()'''])
+
+        self.expect("thread trace dump instructions --skip 100 -f", inHistory=True,
+            substrs=['''thread #1: tid = 815455
+    no more data'''])
+
+        self.expect("", substrs=['''thread #1: tid = 815455
+    no more data'''])
Index: lldb/source/Target/TraceInstructionDumper.cpp
===================================================================
--- /dev/null
+++ lldb/source/Target/TraceInstructionDumper.cpp
@@ -0,0 +1,291 @@
+//===-- TraceInstructionDumper.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 "lldb/Target/TraceInstructionDumper.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/SectionLoadList.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm;
+
+TraceInstructionDumper::TraceInstructionDumper(lldb::TraceCursorUP &&cursor_up,
+                                               bool forwards, bool raw)
+    : m_cursor_up(std::move(cursor_up)), m_forwards(forwards), m_raw(raw) {}
+
+bool TraceInstructionDumper::TryMoveOneInstruction() {
+  if (m_forwards) {
+    if (!m_cursor_up->Next()) {
+      m_no_more_data = true;
+      return false;
+    }
+    m_index++;
+  } else {
+    if (!m_cursor_up->Prev()) {
+      m_no_more_data = true;
+      return false;
+    }
+    m_index--;
+  }
+  return true;
+}
+
+void TraceInstructionDumper::Skip(size_t count) {
+  size_t moved;
+  if (m_forwards) {
+    moved = m_cursor_up->Next(count);
+    m_index += moved;
+  } else {
+    moved = m_cursor_up->Prev(count);
+    m_index -= moved;
+  }
+  if (moved != count)
+    m_no_more_data = true;
+}
+
+static int GetNumberOfDigits(int num) {
+  if (num == 0)
+    return 1;
+  return (num < 0 ? 1 : 0) + static_cast<int>(log10(abs(num))) + 1;
+}
+
+/// Helper struct that holds symbol, disassembly and address information of an
+/// instruction.
+struct InstructionSymbolInfo {
+  SymbolContext sc;
+  Address address;
+  lldb::addr_t load_address;
+  lldb::DisassemblerSP disassembler;
+  lldb::InstructionSP instruction;
+  lldb_private::ExecutionContext exe_ctx;
+};
+
+// This custom LineEntry validator is neded because some line_entries have
+// 0 as line, which is meaningless. Notice that LineEntry::IsValid only
+// checks that line is not LLDB_INVALID_LINE_NUMBER, i.e. UINT32_MAX.
+static bool IsLineEntryValid(const LineEntry &line_entry) {
+  return line_entry.IsValid() && line_entry.line > 0;
+}
+
+/// \return
+///     \b true if the provided line entries match line, column and source file.
+///     This function assumes that the line entries are valid.
+static bool FileLineAndColumnMatches(const LineEntry &a, const LineEntry &b) {
+  if (a.line != b.line)
+    return false;
+  if (a.column != b.column)
+    return false;
+  return a.file == b.file;
+}
+
+/// Compare the symbol contexts of the provided \a InstructionSymbolInfo
+/// objects.
+///
+/// \return
+///     \a true if both instructions belong to the same scope level analized
+///     in the following order:
+///       - module
+///       - symbol
+///       - function
+///       - line
+static bool
+IsSameInstructionSymbolContext(const InstructionSymbolInfo &prev_insn,
+                               const InstructionSymbolInfo &insn) {
+  // module checks
+  if (insn.sc.module_sp != prev_insn.sc.module_sp)
+    return false;
+
+  // symbol checks
+  if (insn.sc.symbol != prev_insn.sc.symbol)
+    return false;
+
+  // function checks
+  if (!insn.sc.function && !prev_insn.sc.function)
+    return true;
+  else if (insn.sc.function != prev_insn.sc.function)
+    return false;
+
+  // line entry checks
+  const bool curr_line_valid = IsLineEntryValid(insn.sc.line_entry);
+  const bool prev_line_valid = IsLineEntryValid(prev_insn.sc.line_entry);
+  if (curr_line_valid && prev_line_valid)
+    return FileLineAndColumnMatches(insn.sc.line_entry,
+                                    prev_insn.sc.line_entry);
+  return curr_line_valid == prev_line_valid;
+}
+
+/// Dump the symbol context of the given instruction address if it's different
+/// from the symbol context of the previous instruction in the trace.
+///
+/// \param[in] prev_sc
+///     The symbol context of the previous instruction in the trace.
+///
+/// \param[in] address
+///     The address whose symbol information will be dumped.
+///
+/// \return
+///     The symbol context of the current address, which might differ from the
+///     previous one.
+static void
+DumpInstructionSymbolContext(Stream &s,
+                             Optional<InstructionSymbolInfo> prev_insn,
+                             InstructionSymbolInfo &insn) {
+  if (prev_insn && IsSameInstructionSymbolContext(*prev_insn, insn))
+    return;
+
+  s.Printf("  ");
+
+  if (!insn.sc.module_sp)
+    s.Printf("(none)");
+  else if (!insn.sc.function && !insn.sc.symbol)
+    s.Printf("%s`(none)",
+             insn.sc.module_sp->GetFileSpec().GetFilename().AsCString());
+  else
+    insn.sc.DumpStopContext(&s, insn.exe_ctx.GetTargetPtr(), insn.address,
+                            /*show_fullpath=*/false,
+                            /*show_module=*/true, /*show_inlined_frames=*/false,
+                            /*show_function_arguments=*/true,
+                            /*show_function_name=*/true);
+  s.Printf("\n");
+}
+
+static void DumpInstructionDisassembly(Stream &s, InstructionSymbolInfo &insn) {
+  if (!insn.instruction)
+    return;
+  s.Printf("    ");
+  insn.instruction->Dump(&s, /*show_address=*/false, /*show_bytes=*/false,
+                         /*max_opcode_byte_size=*/0, &insn.exe_ctx, &insn.sc,
+                         /*prev_sym_ctx=*/nullptr,
+                         /*disassembly_addr_format=*/nullptr,
+                         /*max_address_text_size=*/0);
+}
+
+void TraceInstructionDumper::DumpInstructions(Stream &s, size_t count) {
+  ThreadSP thread_sp = m_cursor_up->GetExecutionContextRef().GetThreadSP();
+  if (!thread_sp) {
+    s.Printf("invalid thread");
+    return;
+  }
+
+  s.Printf("thread #%u: tid = %" PRIu64 "\n", thread_sp->GetIndexID(),
+           thread_sp->GetID());
+
+  int digits_count =
+      GetNumberOfDigits(m_forwards ? m_index + count - 1 : m_index - count + 1);
+  bool was_prev_instruction_an_error = false;
+
+  auto printMissingInstructionsMessage = [&]() {
+    s.Printf("    ...missing instructions\n");
+  };
+
+  auto printInstructionIndex = [&]() {
+    s.Printf("    [%*d] ", digits_count, m_index);
+  };
+
+  InstructionSymbolInfo prev_insn_info;
+
+  Target &target = thread_sp->GetProcess()->GetTarget();
+  ExecutionContext exe_ctx;
+  target.CalculateExecutionContext(exe_ctx);
+  const ArchSpec &arch = target.GetArchitecture();
+
+  // Find the symbol context for the given address reusing the previous
+  // instruction's symbol context when possible.
+  auto calculateSymbolContext = [&](const Address &address) {
+    AddressRange range;
+    if (prev_insn_info.sc.GetAddressRange(eSymbolContextEverything, 0,
+                                          /*inline_block_range*/ false,
+                                          range) &&
+        range.Contains(address))
+      return prev_insn_info.sc;
+
+    SymbolContext sc;
+    address.CalculateSymbolContext(&sc, eSymbolContextEverything);
+    return sc;
+  };
+
+  // Find the disassembler for the given address reusing the previous
+  // instruction's disassembler when possible.
+  auto calculateDisass = [&](const Address &address, const SymbolContext &sc) {
+    if (prev_insn_info.disassembler) {
+      if (InstructionSP instruction =
+              prev_insn_info.disassembler->GetInstructionList()
+                  .GetInstructionAtAddress(address))
+        return std::make_tuple(prev_insn_info.disassembler, instruction);
+    }
+
+    if (sc.function) {
+      if (DisassemblerSP disassembler =
+              sc.function->GetInstructions(exe_ctx, nullptr)) {
+        if (InstructionSP instruction =
+                disassembler->GetInstructionList().GetInstructionAtAddress(
+                    address))
+          return std::make_tuple(disassembler, instruction);
+      }
+    }
+    // We fallback to a single instruction disassembler
+    AddressRange range(address, arch.GetMaximumOpcodeByteSize());
+    DisassemblerSP disassembler =
+        Disassembler::DisassembleRange(arch, /*plugin_name*/ nullptr,
+                                       /*flavor*/ nullptr, target, range);
+    return std::make_tuple(disassembler,
+                           disassembler ? disassembler->GetInstructionList()
+                                              .GetInstructionAtAddress(address)
+                                        : InstructionSP());
+  };
+
+  for (size_t i = 0; i < count; i++) {
+    if (m_no_more_data) {
+      s.Printf("    no more data\n");
+      break;
+    }
+
+    if (Error err = m_cursor_up->GetError()) {
+      if (!m_forwards && !was_prev_instruction_an_error)
+        printMissingInstructionsMessage();
+
+      was_prev_instruction_an_error = true;
+
+      printInstructionIndex();
+      s << toString(std::move(err));
+    } else {
+      if (m_forwards && was_prev_instruction_an_error)
+        printMissingInstructionsMessage();
+
+      was_prev_instruction_an_error = false;
+
+      InstructionSymbolInfo insn_info;
+
+      if (!m_raw) {
+        insn_info.load_address = m_cursor_up->GetLoadAddress();
+        insn_info.exe_ctx = exe_ctx;
+        insn_info.address.SetLoadAddress(insn_info.load_address, &target);
+        insn_info.sc = calculateSymbolContext(insn_info.address);
+        std::tie(insn_info.disassembler, insn_info.instruction) =
+            calculateDisass(insn_info.address, insn_info.sc);
+
+        DumpInstructionSymbolContext(s, prev_insn_info, insn_info);
+      }
+
+      printInstructionIndex();
+      s.Printf("0x%016" PRIx64, m_cursor_up->GetLoadAddress());
+
+      if (!m_raw)
+        DumpInstructionDisassembly(s, insn_info);
+
+      prev_insn_info = insn_info;
+    }
+
+    s.Printf("\n");
+    TryMoveOneInstruction();
+  }
+}
Index: lldb/source/Target/TraceCursor.cpp
===================================================================
--- lldb/source/Target/TraceCursor.cpp
+++ lldb/source/Target/TraceCursor.cpp
@@ -8,8 +8,34 @@
 
 #include "lldb/Target/TraceCursor.h"
 
-#include "lldb/Target/Trace.h"
+#include "lldb/Target/ExecutionContext.h"
 
+using namespace lldb;
 using namespace lldb_private;
+using namespace llvm;
 
-bool TraceCursor::IsStale() { return m_stop_id != m_trace_sp->GetStopID(); }
+TraceCursor::TraceCursor(lldb::ThreadSP thread_sp)
+    : m_exe_ctx_ref(ExecutionContext(thread_sp)) {}
+
+ExecutionContextRef &TraceCursor::GetExecutionContextRef() {
+  return m_exe_ctx_ref;
+}
+
+static size_t Move(TraceCursor &cursor, bool forwards, size_t count) {
+  size_t visited = 0;
+  for (size_t i = 0; i < count; i++) {
+    bool did_move = forwards ? cursor.Next() : cursor.Prev();
+    if (!did_move)
+      break;
+    visited++;
+  }
+  return visited;
+}
+
+size_t TraceCursor::Next(size_t count) {
+  return Move(*this, /*forwards=*/true, count);
+}
+
+size_t TraceCursor::Prev(size_t count) {
+  return Move(*this, /*forwards=*/false, count);
+}
Index: lldb/source/Target/Trace.cpp
===================================================================
--- lldb/source/Target/Trace.cpp
+++ lldb/source/Target/Trace.cpp
@@ -98,264 +98,6 @@
   return createInvalidPlugInError(name);
 }
 
-static int GetNumberOfDigits(size_t num) {
-  return num == 0 ? 1 : static_cast<int>(log10(num)) + 1;
-}
-
-/// \return
-///     \b true if the provided line entries match line, column and source file.
-///     This function assumes that the line entries are valid.
-static bool FileLineAndColumnMatches(const LineEntry &a, const LineEntry &b) {
-  if (a.line != b.line)
-    return false;
-  if (a.column != b.column)
-    return false;
-  return a.file == b.file;
-}
-
-// This custom LineEntry validator is neded because some line_entries have
-// 0 as line, which is meaningless. Notice that LineEntry::IsValid only
-// checks that line is not LLDB_INVALID_LINE_NUMBER, i.e. UINT32_MAX.
-static bool IsLineEntryValid(const LineEntry &line_entry) {
-  return line_entry.IsValid() && line_entry.line > 0;
-}
-
-/// Helper structure for \a TraverseInstructionsWithSymbolInfo.
-struct InstructionSymbolInfo {
-  SymbolContext sc;
-  Address address;
-  lldb::addr_t load_address;
-  lldb::DisassemblerSP disassembler;
-  lldb::InstructionSP instruction;
-  lldb_private::ExecutionContext exe_ctx;
-};
-
-/// InstructionSymbolInfo object with symbol information for the given
-/// instruction, calculated efficiently.
-///
-/// \param[in] symbol_scope
-///     If not \b 0, then the \a InstructionSymbolInfo will have its
-///     SymbolContext calculated up to that level.
-///
-/// \param[in] include_disassembler
-///     If \b true, then the \a InstructionSymbolInfo will have the
-///     \a disassembler and \a instruction objects calculated.
-static void TraverseInstructionsWithSymbolInfo(
-    Trace &trace, Thread &thread, size_t position,
-    Trace::TraceDirection direction, SymbolContextItem symbol_scope,
-    bool include_disassembler,
-    std::function<bool(size_t index, Expected<InstructionSymbolInfo> insn)>
-        callback) {
-  InstructionSymbolInfo prev_insn;
-
-  Target &target = thread.GetProcess()->GetTarget();
-  ExecutionContext exe_ctx;
-  target.CalculateExecutionContext(exe_ctx);
-  const ArchSpec &arch = target.GetArchitecture();
-
-  // Find the symbol context for the given address reusing the previous
-  // instruction's symbol context when possible.
-  auto calculate_symbol_context = [&](const Address &address) {
-    AddressRange range;
-    if (prev_insn.sc.GetAddressRange(symbol_scope, 0,
-                                     /*inline_block_range*/ false, range) &&
-        range.Contains(address))
-      return prev_insn.sc;
-
-    SymbolContext sc;
-    address.CalculateSymbolContext(&sc, symbol_scope);
-    return sc;
-  };
-
-  // Find the disassembler for the given address reusing the previous
-  // instruction's disassembler when possible.
-  auto calculate_disass = [&](const Address &address, const SymbolContext &sc) {
-    if (prev_insn.disassembler) {
-      if (InstructionSP instruction =
-              prev_insn.disassembler->GetInstructionList()
-                  .GetInstructionAtAddress(address))
-        return std::make_tuple(prev_insn.disassembler, instruction);
-    }
-
-    if (sc.function) {
-      if (DisassemblerSP disassembler =
-              sc.function->GetInstructions(exe_ctx, nullptr)) {
-        if (InstructionSP instruction =
-                disassembler->GetInstructionList().GetInstructionAtAddress(
-                    address))
-          return std::make_tuple(disassembler, instruction);
-      }
-    }
-    // We fallback to a single instruction disassembler
-    AddressRange range(address, arch.GetMaximumOpcodeByteSize());
-    DisassemblerSP disassembler =
-        Disassembler::DisassembleRange(arch, /*plugin_name*/ nullptr,
-                                       /*flavor*/ nullptr, target, range);
-    return std::make_tuple(disassembler,
-                           disassembler ? disassembler->GetInstructionList()
-                                              .GetInstructionAtAddress(address)
-                                        : InstructionSP());
-  };
-
-  trace.TraverseInstructions(
-      thread, position, direction,
-      [&](size_t index, Expected<lldb::addr_t> load_address) -> bool {
-        if (!load_address)
-          return callback(index, load_address.takeError());
-
-        InstructionSymbolInfo insn;
-        insn.load_address = *load_address;
-        insn.exe_ctx = exe_ctx;
-        insn.address.SetLoadAddress(*load_address, &target);
-        if (symbol_scope != 0)
-          insn.sc = calculate_symbol_context(insn.address);
-        if (include_disassembler)
-          std::tie(insn.disassembler, insn.instruction) =
-              calculate_disass(insn.address, insn.sc);
-        prev_insn = insn;
-        return callback(index, insn);
-      });
-}
-
-/// Compare the symbol contexts of the provided \a InstructionSymbolInfo
-/// objects.
-///
-/// \return
-///     \a true if both instructions belong to the same scope level analized
-///     in the following order:
-///       - module
-///       - symbol
-///       - function
-///       - line
-static bool
-IsSameInstructionSymbolContext(const InstructionSymbolInfo &prev_insn,
-                               const InstructionSymbolInfo &insn) {
-  // module checks
-  if (insn.sc.module_sp != prev_insn.sc.module_sp)
-    return false;
-
-  // symbol checks
-  if (insn.sc.symbol != prev_insn.sc.symbol)
-    return false;
-
-  // function checks
-  if (!insn.sc.function && !prev_insn.sc.function)
-    return true;
-  else if (insn.sc.function != prev_insn.sc.function)
-    return false;
-
-  // line entry checks
-  const bool curr_line_valid = IsLineEntryValid(insn.sc.line_entry);
-  const bool prev_line_valid = IsLineEntryValid(prev_insn.sc.line_entry);
-  if (curr_line_valid && prev_line_valid)
-    return FileLineAndColumnMatches(insn.sc.line_entry,
-                                    prev_insn.sc.line_entry);
-  return curr_line_valid == prev_line_valid;
-}
-
-/// Dump the symbol context of the given instruction address if it's different
-/// from the symbol context of the previous instruction in the trace.
-///
-/// \param[in] prev_sc
-///     The symbol context of the previous instruction in the trace.
-///
-/// \param[in] address
-///     The address whose symbol information will be dumped.
-///
-/// \return
-///     The symbol context of the current address, which might differ from the
-///     previous one.
-static void
-DumpInstructionSymbolContext(Stream &s,
-                             Optional<InstructionSymbolInfo> prev_insn,
-                             InstructionSymbolInfo &insn) {
-  if (prev_insn && IsSameInstructionSymbolContext(*prev_insn, insn))
-    return;
-
-  s.Printf("  ");
-
-  if (!insn.sc.module_sp)
-    s.Printf("(none)");
-  else if (!insn.sc.function && !insn.sc.symbol)
-    s.Printf("%s`(none)",
-             insn.sc.module_sp->GetFileSpec().GetFilename().AsCString());
-  else
-    insn.sc.DumpStopContext(&s, insn.exe_ctx.GetTargetPtr(), insn.address,
-                            /*show_fullpath*/ false,
-                            /*show_module*/ true, /*show_inlined_frames*/ false,
-                            /*show_function_arguments*/ true,
-                            /*show_function_name*/ true);
-  s.Printf("\n");
-}
-
-static void DumpInstructionDisassembly(Stream &s, InstructionSymbolInfo &insn) {
-  if (!insn.instruction)
-    return;
-  s.Printf("    ");
-  insn.instruction->Dump(&s, /*show_address*/ false, /*show_bytes*/ false,
-                         /*max_opcode_byte_size*/ 0, &insn.exe_ctx, &insn.sc,
-                         /*prev_sym_ctx*/ nullptr,
-                         /*disassembly_addr_format*/ nullptr,
-                         /*max_address_text_size*/ 0);
-}
-
-void Trace::DumpTraceInstructions(Thread &thread, Stream &s, size_t count,
-                                  size_t end_position, bool raw) {
-  Optional<size_t> instructions_count = GetInstructionCount(thread);
-  if (!instructions_count) {
-    s.Printf("thread #%u: tid = %" PRIu64 ", not traced\n", thread.GetIndexID(),
-             thread.GetID());
-    return;
-  }
-
-  s.Printf("thread #%u: tid = %" PRIu64 ", total instructions = %zu\n",
-           thread.GetIndexID(), thread.GetID(), *instructions_count);
-
-  if (count == 0 || end_position >= *instructions_count)
-    return;
-
-  int digits_count = GetNumberOfDigits(end_position);
-  size_t start_position =
-      end_position + 1 < count ? 0 : end_position + 1 - count;
-  auto printInstructionIndex = [&](size_t index) {
-    s.Printf("    [%*zu] ", digits_count, index);
-  };
-
-  bool was_prev_instruction_an_error = false;
-  Optional<InstructionSymbolInfo> prev_insn;
-
-  TraverseInstructionsWithSymbolInfo(
-      *this, thread, start_position, TraceDirection::Forwards,
-      eSymbolContextEverything, /*disassembler*/ true,
-      [&](size_t index, Expected<InstructionSymbolInfo> insn) -> bool {
-        if (!insn) {
-          printInstructionIndex(index);
-          s << toString(insn.takeError());
-
-          prev_insn = None;
-          was_prev_instruction_an_error = true;
-        } else {
-          if (was_prev_instruction_an_error)
-            s.Printf("    ...missing instructions\n");
-
-          if (!raw)
-            DumpInstructionSymbolContext(s, prev_insn, *insn);
-
-          printInstructionIndex(index);
-          s.Printf("0x%016" PRIx64, insn->load_address);
-
-          if (!raw)
-            DumpInstructionDisassembly(s, *insn);
-
-          prev_insn = *insn;
-          was_prev_instruction_an_error = false;
-        }
-
-        s.Printf("\n");
-        return index < end_position;
-      });
-}
-
 Error Trace::Start(const llvm::json::Value &request) {
   if (!m_live_process)
     return createStringError(inconvertibleErrorCode(),
Index: lldb/source/Target/CMakeLists.txt
===================================================================
--- lldb/source/Target/CMakeLists.txt
+++ lldb/source/Target/CMakeLists.txt
@@ -69,6 +69,7 @@
   ThreadPostMortemTrace.cpp
   Trace.cpp
   TraceCursor.cpp
+  TraceInstructionDumper.cpp
   TraceSessionFileParser.cpp
   UnixSignals.cpp
   UnwindAssembly.cpp
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
@@ -65,13 +65,6 @@
 
   llvm::StringRef GetSchema() override;
 
-  void TraverseInstructions(
-      Thread &thread, size_t position, TraceDirection direction,
-      std::function<bool(size_t index, llvm::Expected<lldb::addr_t> load_addr)>
-          callback) override;
-
-  llvm::Optional<size_t> GetInstructionCount(Thread &thread) override;
-
   lldb::TraceCursorUP GetCursor(Thread &thread) override;
 
   void DoRefreshLiveProcessState(
@@ -145,24 +138,23 @@
       : Trace(live_process), m_thread_decoders(){};
 
   /// Decode the trace of the given thread that, i.e. recontruct the traced
-  /// instructions. That trace must be managed by this class.
+  /// instructions.
   ///
   /// \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 a \b
-  ///     nullptr if the thread's trace is not managed by this class.
-  const DecodedThread *Decode(Thread &thread);
+  ///     A \a DecodedThread shared pointer with the decoded instructions. Any
+  ///     errors are embedded in the instruction list.
+  DecodedThreadSP Decode(Thread &thread);
 
   /// It is provided by either a session file or a live process' "cpuInfo"
   /// binary data.
   llvm::Optional<pt_cpu> m_cpu_info;
   std::map<const Thread *, std::unique_ptr<ThreadDecoder>> m_thread_decoders;
-  /// Dummy DecodedThread used when decoding threads after there were errors
-  /// when refreshing the live process state.
-  llvm::Optional<DecodedThread> m_failed_live_threads_decoder;
+  /// Error gotten after a failed live process update, if any.
+  llvm::Optional<std::string> m_live_refresh_error;
 };
 
 } // 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
@@ -9,6 +9,7 @@
 #include "TraceIntelPT.h"
 
 #include "CommandObjectTraceStartIntelPT.h"
+#include "DecodedThread.h"
 #include "TraceIntelPTConstants.h"
 #include "TraceIntelPTSessionFileParser.h"
 #include "lldb/Core/PluginManager.h"
@@ -88,44 +89,23 @@
         thread.get(), std::make_unique<PostMortemThreadDecoder>(thread, *this));
 }
 
-const DecodedThread *TraceIntelPT::Decode(Thread &thread) {
+DecodedThreadSP TraceIntelPT::Decode(Thread &thread) {
   RefreshLiveProcessState();
-  if (m_failed_live_threads_decoder.hasValue())
-    return &*m_failed_live_threads_decoder;
+  if (m_live_refresh_error.hasValue())
+    return std::make_shared<DecodedThread>(
+        thread.shared_from_this(),
+        createStringError(inconvertibleErrorCode(), *m_live_refresh_error));
 
   auto it = m_thread_decoders.find(&thread);
   if (it == m_thread_decoders.end())
-    return nullptr;
-  return &it->second->Decode();
+    return std::make_shared<DecodedThread>(
+        thread.shared_from_this(),
+        createStringError(inconvertibleErrorCode(), "thread not traced"));
+  return it->second->Decode();
 }
 
 lldb::TraceCursorUP TraceIntelPT::GetCursor(Thread &thread) {
-  // TODO: to implement
-  return nullptr;
-}
-
-void TraceIntelPT::TraverseInstructions(
-    Thread &thread, size_t position, TraceDirection direction,
-    std::function<bool(size_t index, Expected<lldb::addr_t> load_addr)>
-        callback) {
-  const DecodedThread *decoded_thread = Decode(thread);
-  if (!decoded_thread)
-    return;
-
-  ArrayRef<IntelPTInstruction> instructions = decoded_thread->GetInstructions();
-
-  ssize_t delta = direction == TraceDirection::Forwards ? 1 : -1;
-  for (ssize_t i = position; i < (ssize_t)instructions.size() && i >= 0;
-       i += delta)
-    if (!callback(i, instructions[i].GetLoadAddress()))
-      break;
-}
-
-Optional<size_t> TraceIntelPT::GetInstructionCount(Thread &thread) {
-  if (const DecodedThread *decoded_thread = Decode(thread))
-    return decoded_thread->GetInstructions().size();
-  else
-    return None;
+  return Decode(thread)->GetCursor();
 }
 
 Expected<pt_cpu> TraceIntelPT::GetCPUInfoForLiveProcess() {
@@ -195,7 +175,7 @@
   m_thread_decoders.clear();
 
   if (!state) {
-    m_failed_live_threads_decoder = DecodedThread(state.takeError());
+    m_live_refresh_error = toString(state.takeError());
     return;
   }
 
Index: lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
@@ -0,0 +1,52 @@
+//===-- TraceCursorIntelPT.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_TRACECURSORINTELPT_H
+#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACECURSORINTELPT_H
+
+#include "IntelPTDecoder.h"
+#include "TraceIntelPTSessionFileParser.h"
+
+namespace lldb_private {
+namespace trace_intel_pt {
+
+class TraceCursorIntelPT : public TraceCursor {
+public:
+  TraceCursorIntelPT(lldb::ThreadSP thread_sp,
+                     DecodedThreadSP decoded_thread_sp);
+
+  bool Next(lldb::TraceInstructionControlFlowType granularity,
+            bool ignore_errors) override;
+
+  bool Prev(lldb::TraceInstructionControlFlowType granularity,
+            bool ignore_errors) override;
+
+  void SeekToEnd() override;
+
+  void SeekToBegin() override;
+
+  llvm::Error GetError() override;
+
+  lldb::addr_t GetLoadAddress() override;
+
+  lldb::TraceInstructionControlFlowType
+  GetInstructionControlFlowType() override;
+
+  bool IsError() override;
+
+private:
+  /// Storage of the actual instructions
+  DecodedThreadSP m_decoded_thread_sp;
+  /// Internal instruction index currently pointing at.
+  size_t m_pos;
+};
+
+} // namespace trace_intel_pt
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACECURSORINTELPT_H
Index: lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
@@ -0,0 +1,74 @@
+//===-- TraceCursorIntelPT.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 "TraceCursorIntelPT.h"
+#include "DecodedThread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::trace_intel_pt;
+using namespace llvm;
+
+TraceCursorIntelPT::TraceCursorIntelPT(ThreadSP thread_sp,
+                                       DecodedThreadSP decoded_thread_sp)
+    : TraceCursor(thread_sp), m_decoded_thread_sp(decoded_thread_sp) {
+  assert(!m_decoded_thread_sp->GetInstructions().empty() &&
+         "a trace should have at least one instruction or error");
+  m_pos = m_decoded_thread_sp->GetInstructions().size() - 1;
+}
+
+bool TraceCursorIntelPT::Next(TraceInstructionControlFlowType granularity,
+                              bool ignore_errors) {
+  size_t size = m_decoded_thread_sp->GetInstructions().size();
+  while (m_pos + 1 < size) {
+    m_pos++;
+    if ((GetInstructionControlFlowType() & granularity) ||
+        (!ignore_errors && IsError()))
+      return true;
+  }
+  return false;
+}
+
+bool TraceCursorIntelPT::Prev(TraceInstructionControlFlowType granularity,
+                              bool ignore_errors) {
+  while (m_pos > 0) {
+    m_pos--;
+    if ((GetInstructionControlFlowType() & granularity) ||
+        (!ignore_errors && IsError()))
+      return true;
+  }
+  return false;
+}
+
+void TraceCursorIntelPT::SeekToEnd() {
+  m_pos = m_decoded_thread_sp->GetInstructions().size() - 1;
+}
+
+void TraceCursorIntelPT::SeekToBegin() { m_pos = 0; }
+
+bool TraceCursorIntelPT::IsError() {
+  return m_decoded_thread_sp->GetInstructions()[m_pos].IsError();
+}
+
+Error TraceCursorIntelPT::GetError() {
+  return m_decoded_thread_sp->GetInstructions()[m_pos].ToError();
+}
+
+lldb::addr_t TraceCursorIntelPT::GetLoadAddress() {
+  return m_decoded_thread_sp->GetInstructions()[m_pos].GetLoadAddress();
+}
+
+TraceInstructionControlFlowType
+TraceCursorIntelPT::GetInstructionControlFlowType() {
+  lldb::addr_t next_load_address =
+      m_pos + 1 < m_decoded_thread_sp->GetInstructions().size()
+          ? m_decoded_thread_sp->GetInstructions()[m_pos + 1].GetLoadAddress()
+          : LLDB_INVALID_ADDRESS;
+  return m_decoded_thread_sp->GetInstructions()[m_pos].GetControlFlowType(
+      next_load_address);
+}
Index: lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
+++ lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.h
@@ -31,7 +31,7 @@
   ///
   /// \return
   ///     A \a DecodedThread instance.
-  const DecodedThread &Decode();
+  DecodedThreadSP Decode();
 
   ThreadDecoder(const ThreadDecoder &other) = delete;
   ThreadDecoder &operator=(const ThreadDecoder &other) = delete;
@@ -41,9 +41,9 @@
   ///
   /// \return
   ///     A \a DecodedThread instance.
-  virtual DecodedThread DoDecode() = 0;
+  virtual DecodedThreadSP DoDecode() = 0;
 
-  llvm::Optional<DecodedThread> m_decoded_thread;
+  llvm::Optional<DecodedThreadSP> m_decoded_thread;
 };
 
 /// Decoder implementation for \a lldb_private::ThreadPostMortemTrace, which are
@@ -59,7 +59,7 @@
                           TraceIntelPT &trace);
 
 private:
-  DecodedThread DoDecode() override;
+  DecodedThreadSP DoDecode() override;
 
   lldb::ThreadPostMortemTraceSP m_trace_thread;
   TraceIntelPT &m_trace;
@@ -75,7 +75,7 @@
   LiveThreadDecoder(Thread &thread, TraceIntelPT &trace);
 
 private:
-  DecodedThread DoDecode() override;
+  DecodedThreadSP DoDecode() override;
 
   lldb::ThreadSP m_thread_sp;
   TraceIntelPT &m_trace;
Index: lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
+++ lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
@@ -222,7 +222,7 @@
     return cpu_info.takeError();
 }
 
-const DecodedThread &ThreadDecoder::Decode() {
+DecodedThreadSP ThreadDecoder::Decode() {
   if (!m_decoded_thread.hasValue())
     m_decoded_thread = DoDecode();
   return *m_decoded_thread;
@@ -232,22 +232,26 @@
     const lldb::ThreadPostMortemTraceSP &trace_thread, TraceIntelPT &trace)
     : m_trace_thread(trace_thread), m_trace(trace) {}
 
-DecodedThread PostMortemThreadDecoder::DoDecode() {
+DecodedThreadSP PostMortemThreadDecoder::DoDecode() {
   if (Expected<std::vector<IntelPTInstruction>> instructions =
           DecodeTraceFile(*m_trace_thread->GetProcess(), m_trace,
                           m_trace_thread->GetTraceFile()))
-    return DecodedThread(std::move(*instructions));
+    return std::make_shared<DecodedThread>(m_trace_thread->shared_from_this(),
+                                           std::move(*instructions));
   else
-    return DecodedThread(instructions.takeError());
+    return std::make_shared<DecodedThread>(m_trace_thread->shared_from_this(),
+                                           instructions.takeError());
 }
 
 LiveThreadDecoder::LiveThreadDecoder(Thread &thread, TraceIntelPT &trace)
     : m_thread_sp(thread.shared_from_this()), m_trace(trace) {}
 
-DecodedThread LiveThreadDecoder::DoDecode() {
+DecodedThreadSP LiveThreadDecoder::DoDecode() {
   if (Expected<std::vector<IntelPTInstruction>> instructions =
           DecodeLiveThread(*m_thread_sp, m_trace))
-    return DecodedThread(std::move(*instructions));
+    return std::make_shared<DecodedThread>(m_thread_sp,
+                                           std::move(*instructions));
   else
-    return DecodedThread(instructions.takeError());
+    return std::make_shared<DecodedThread>(m_thread_sp,
+                                           instructions.takeError());
 }
Index: lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
+++ lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
@@ -66,12 +66,7 @@
   /// Error constructor
   ///
   /// libipt errors should use the underlying \a IntelPTError class.
-  IntelPTInstruction(llvm::Error err) {
-    llvm::handleAllErrors(std::move(err),
-                          [&](std::unique_ptr<llvm::ErrorInfoBase> info) {
-                            m_error = std::move(info);
-                          });
-  }
+  IntelPTInstruction(llvm::Error err);
 
   /// Check if this object represents an error (i.e. a gap).
   ///
@@ -80,15 +75,27 @@
   bool IsError() const;
 
   /// \return
-  ///     The instruction pointer address, or an \a llvm::Error if it is an
-  ///     error.
-  llvm::Expected<lldb::addr_t> GetLoadAddress() const;
+  ///     The instruction pointer address, or \a LLDB_INVALID_ADDRESS if it is
+  ///     an error.
+  lldb::addr_t GetLoadAddress() const;
 
   /// \return
   ///     An \a llvm::Error object if this class corresponds to an Error, or an
   ///     \a llvm::Error::success otherwise.
   llvm::Error ToError() const;
 
+  /// Get the \a lldb::TraceInstructionControlFlowType categories of the
+  /// instruction.
+  ///
+  /// \param[in] next_load_address
+  ///     The address of the next instruction in the trace or \b
+  ///     LLDB_INVALID_ADDRESS if not available.
+  ///
+  /// \return
+  ///     The control flow categories, or \b 0 if the instruction is an error.
+  lldb::TraceInstructionControlFlowType
+  GetControlFlowType(lldb::addr_t next_load_address) const;
+
   IntelPTInstruction(IntelPTInstruction &&other) = default;
 
 private:
@@ -106,15 +113,14 @@
 ///
 /// Each decoded thread contains a cursor to the current position the user is
 /// stopped at. See \a Trace::GetCursorPosition for more information.
-class DecodedThread {
+class DecodedThread : public std::enable_shared_from_this<DecodedThread> {
 public:
-  DecodedThread(std::vector<IntelPTInstruction> &&instructions)
-      : m_instructions(std::move(instructions)), m_position(GetLastPosition()) {
-  }
+  DecodedThread(lldb::ThreadSP thread_sp,
+                std::vector<IntelPTInstruction> &&instructions);
 
   /// Constructor with a single error signaling a complete failure of the
   /// decoding process.
-  DecodedThread(llvm::Error error);
+  DecodedThread(lldb::ThreadSP thread_sp, llvm::Error error);
 
   /// Get the instructions from the decoded trace. Some of them might indicate
   /// errors (i.e. gaps) in the trace.
@@ -123,28 +129,16 @@
   ///   The instructions of the trace.
   llvm::ArrayRef<IntelPTInstruction> GetInstructions() const;
 
-  /// \return
-  ///   The current position of the cursor of this trace, or 0 if there are no
-  ///   instructions.
-  size_t GetCursorPosition() const;
-
-  /// Change the position of the cursor of this trace. If this value is to high,
-  /// the new position will be set as the last instruction of the trace.
-  ///
-  /// \return
-  ///     The effective new position.
-  size_t SetCursorPosition(size_t new_position);
-  /// \}
+  /// Get a new cursor for the decoded thread.
+  lldb::TraceCursorUP GetCursor();
 
 private:
-  /// \return
-  ///     The index of the last element of the trace, or 0 if empty.
-  size_t GetLastPosition() const;
-
+  lldb::ThreadSP m_thread_sp;
   std::vector<IntelPTInstruction> m_instructions;
-  size_t m_position;
 };
 
+using DecodedThreadSP = std::shared_ptr<DecodedThread>;
+
 } // namespace trace_intel_pt
 } // namespace lldb_private
 
Index: lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
+++ lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
@@ -8,8 +8,13 @@
 
 #include "DecodedThread.h"
 
+#include <intel-pt.h>
+#include <memory>
+
+#include "TraceCursorIntelPT.h"
 #include "lldb/Utility/StreamString.h"
 
+using namespace lldb;
 using namespace lldb_private;
 using namespace lldb_private::trace_intel_pt;
 using namespace llvm;
@@ -30,13 +35,18 @@
   OS << "error: " << libipt_error_message;
 }
 
+IntelPTInstruction::IntelPTInstruction(llvm::Error err) {
+  llvm::handleAllErrors(std::move(err),
+                        [&](std::unique_ptr<llvm::ErrorInfoBase> info) {
+                          m_error = std::move(info);
+                        });
+  m_pt_insn.ip = LLDB_INVALID_ADDRESS;
+  m_pt_insn.iclass = ptic_error;
+}
+
 bool IntelPTInstruction::IsError() const { return (bool)m_error; }
 
-Expected<lldb::addr_t> IntelPTInstruction::GetLoadAddress() const {
-  if (IsError())
-    return ToError();
-  return m_pt_insn.ip;
-}
+lldb::addr_t IntelPTInstruction::GetLoadAddress() const { return m_pt_insn.ip; }
 
 Error IntelPTInstruction::ToError() const {
   if (!IsError())
@@ -48,22 +58,54 @@
                                  m_error->convertToErrorCode());
 }
 
-size_t DecodedThread::GetLastPosition() const {
-  return m_instructions.empty() ? 0 : m_instructions.size() - 1;
+TraceInstructionControlFlowType
+IntelPTInstruction::GetControlFlowType(lldb::addr_t next_load_address) const {
+  if (IsError())
+    return (TraceInstructionControlFlowType)0;
+
+  TraceInstructionControlFlowType mask =
+      eTraceInstructionControlFlowTypeInstruction;
+
+  switch (m_pt_insn.iclass) {
+  case ptic_cond_jump:
+  case ptic_jump:
+  case ptic_far_jump:
+    mask |= eTraceInstructionControlFlowTypeBranch;
+    if (m_pt_insn.ip + m_pt_insn.size != next_load_address)
+      mask |= eTraceInstructionControlFlowTypeTakenBranch;
+    break;
+  case ptic_return:
+  case ptic_far_return:
+    mask |= eTraceInstructionControlFlowTypeReturn;
+    break;
+  case ptic_call:
+  case ptic_far_call:
+    mask |= eTraceInstructionControlFlowTypeCall;
+    break;
+  default:
+    break;
+  }
+
+  return mask;
 }
 
 ArrayRef<IntelPTInstruction> DecodedThread::GetInstructions() const {
   return makeArrayRef(m_instructions);
 }
 
-size_t DecodedThread::GetCursorPosition() const { return m_position; }
+DecodedThread::DecodedThread(ThreadSP thread_sp, Error error)
+    : m_thread_sp(thread_sp) {
+  m_instructions.emplace_back(std::move(error));
+}
 
-size_t DecodedThread::SetCursorPosition(size_t new_position) {
-  m_position = std::min(new_position, GetLastPosition());
-  return m_position;
+DecodedThread::DecodedThread(ThreadSP thread_sp,
+                             std::vector<IntelPTInstruction> &&instructions)
+    : m_thread_sp(thread_sp), m_instructions(std::move(instructions)) {
+  if (m_instructions.empty())
+    m_instructions.emplace_back(
+        createStringError(inconvertibleErrorCode(), "empty trace"));
 }
 
-DecodedThread::DecodedThread(Error error) {
-  m_instructions.emplace_back(std::move(error));
-  m_position = GetLastPosition();
+lldb::TraceCursorUP DecodedThread::GetCursor() {
+  return std::make_unique<TraceCursorIntelPT>(m_thread_sp, shared_from_this());
 }
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
@@ -17,6 +17,7 @@
   CommandObjectTraceStartIntelPT.cpp
   DecodedThread.cpp
   IntelPTDecoder.cpp
+  TraceCursorIntelPT.cpp
   TraceIntelPT.cpp
   TraceIntelPTSessionFileParser.cpp
 
Index: lldb/source/Commands/Options.td
===================================================================
--- lldb/source/Commands/Options.td
+++ lldb/source/Commands/Options.td
@@ -1047,13 +1047,19 @@
 }
 
 let Command = "thread trace dump instructions" in {
+  def thread_trace_dump_instructions_forwards: Option<"forwards", "f">, Group<1>,
+    Desc<"If specified, the trace is traversed forwards chronologically "
+    "starting at the oldest instruction. Otherwise, it starts at the most "
+    "recent one and the traversal is backwards.">;
   def thread_trace_dump_instructions_count : Option<"count", "c">, Group<1>,
     Arg<"Count">,
-    Desc<"The number of instructions to display ending at the current position.">;
-  def thread_trace_dump_instructions_position : Option<"position", "p">,
+    Desc<"The number of instructions to display starting at the most recent "
+    "instruction, or the oldest if --forwards is provided.">;
+  def thread_trace_dump_instructions_skip: Option<"skip", "s">,
     Group<1>,
     Arg<"Index">,
-    Desc<"The position to use instead of the current position of the trace.">;
+    Desc<"How many instruction to skip from the end of the trace to start "
+    "dumping instructions, or from the beginning if --forwards is provided">;
   def thread_trace_dump_instructions_raw : Option<"raw", "r">,
     Group<1>,
     Desc<"Dump only instruction address without disassembly nor symbol information.">;
Index: lldb/source/Commands/CommandObjectThread.cpp
===================================================================
--- lldb/source/Commands/CommandObjectThread.cpp
+++ lldb/source/Commands/CommandObjectThread.cpp
@@ -8,6 +8,7 @@
 
 #include "CommandObjectThread.h"
 
+#include <memory>
 #include <sstream>
 
 #include "CommandObjectThreadUtil.h"
@@ -32,6 +33,7 @@
 #include "lldb/Target/ThreadPlan.h"
 #include "lldb/Target/ThreadPlanStepInRange.h"
 #include "lldb/Target/Trace.h"
+#include "lldb/Target/TraceInstructionDumper.h"
 #include "lldb/Utility/State.h"
 
 using namespace lldb;
@@ -2004,21 +2006,24 @@
           m_count = count;
         break;
       }
-      case 'p': {
-        int32_t position;
-        if (option_arg.empty() || option_arg.getAsInteger(0, position) ||
-            position < 0)
+      case 's': {
+        int32_t skip;
+        if (option_arg.empty() || option_arg.getAsInteger(0, skip) || skip < 0)
           error.SetErrorStringWithFormat(
               "invalid integer value for option '%s'",
               option_arg.str().c_str());
         else
-          m_position = position;
+          m_skip = skip;
         break;
       }
       case 'r': {
         m_raw = true;
         break;
       }
+      case 'f': {
+        m_forwards = true;
+        break;
+      }
       default:
         llvm_unreachable("Unimplemented option");
       }
@@ -2027,8 +2032,9 @@
 
     void OptionParsingStarting(ExecutionContext *execution_context) override {
       m_count = kDefaultCount;
-      m_position = llvm::None;
+      m_skip = 0;
       m_raw = false;
+      m_forwards = false;
     }
 
     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
@@ -2039,14 +2045,15 @@
 
     // Instance variables to hold the values for command options.
     size_t m_count;
-    llvm::Optional<ssize_t> m_position;
+    size_t m_skip;
     bool m_raw;
+    bool m_forwards;
   };
 
   CommandObjectTraceDumpInstructions(CommandInterpreter &interpreter)
       : CommandObjectIterateOverThreads(
             interpreter, "thread trace dump instructions",
-            "Dump the traced instructions for one or more threads.  If no "
+            "Dump the traced instructions for one or more threads. If no "
             "threads are specified, show the current thread.  Use the "
             "thread-index \"all\" to see all threads.",
             nullptr,
@@ -2063,14 +2070,14 @@
                                uint32_t index) override {
     current_command_args.GetCommandString(m_repeat_command);
     m_create_repeat_command_just_invoked = true;
-    m_consecutive_repetitions = 0;
     return m_repeat_command.c_str();
   }
 
 protected:
   bool DoExecute(Args &args, CommandReturnObject &result) override {
-    if (IsRepeatCommand())
-      m_consecutive_repetitions++;
+    if (!IsRepeatCommand())
+      m_dumpers.clear();
+
     bool status = CommandObjectIterateOverThreads::DoExecute(args, result);
 
     m_create_repeat_command_just_invoked = false;
@@ -2086,20 +2093,20 @@
     ThreadSP thread_sp =
         m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid);
 
-    if (llvm::Optional<size_t> insn_count =
-            trace_sp->GetInstructionCount(*thread_sp)) {
-      size_t count = m_options.m_count;
-      ssize_t position =
-          m_options.m_position.getValueOr((ssize_t)*insn_count - 1) -
-          m_consecutive_repetitions * count;
-      if (position < 0)
-        result.AppendError("error: no more data");
-      else
-        trace_sp->DumpTraceInstructions(*thread_sp, result.GetOutputStream(),
-                                        count, position, m_options.m_raw);
-    } else {
-      result.AppendError("error: not traced");
+    if (!m_dumpers.count(thread_sp->GetID())) {
+      lldb::TraceCursorUP cursor_up = trace_sp->GetCursor(*thread_sp);
+      if (m_options.m_forwards)
+        cursor_up->SeekToBegin();
+
+      auto dumper = std::make_unique<TraceInstructionDumper>(
+          std::move(cursor_up), m_options.m_forwards, m_options.m_raw);
+      dumper->Skip(m_options.m_skip);
+
+      m_dumpers[thread_sp->GetID()] = std::move(dumper);
     }
+
+    m_dumpers[thread_sp->GetID()]->DumpInstructions(result.GetOutputStream(),
+                                                    m_options.m_count);
     return true;
   }
 
@@ -2108,7 +2115,7 @@
   // Repeat command helpers
   std::string m_repeat_command;
   bool m_create_repeat_command_just_invoked;
-  size_t m_consecutive_repetitions = 0;
+  std::map<lldb::tid_t, std::unique_ptr<TraceInstructionDumper>> m_dumpers;
 };
 
 // CommandObjectMultiwordTraceDump
Index: lldb/include/lldb/Target/TraceInstructionDumper.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Target/TraceInstructionDumper.h
@@ -0,0 +1,65 @@
+//===-- TraceInstructionDumper.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/TraceCursor.h"
+
+#ifndef LLDB_TARGET_TRACE_INSTRUCTION_DUMPER_H
+#define LLDB_TARGET_TRACE_INSTRUCTION_DUMPER_H
+
+namespace lldb_private {
+
+class TraceInstructionDumper {
+public:
+  /// Create a instruction dumper for the cursor.
+  ///
+  /// \param[in] cursor
+  ///     The cursor whose instructions will be dumped.
+  ///
+  /// \param[in] forwards
+  ///     If \b true, the instructions are traversed forwards chronologically
+  ///     from the current position of the cursor. Otherwise, backwards.
+  ///
+  /// \param[in] raw
+  ///     Dump only instruction addresses without disassembly nor symbol
+  ///     information.
+  TraceInstructionDumper(lldb::TraceCursorUP &&cursor_up, bool forwards,
+                         bool raw);
+
+  /// Dump \a count instructions of the thread trace starting at the current
+  /// cursor position.
+  ///
+  /// This effectively moves the cursor to the next unvisited position, so that
+  /// a subsequent call to this method continues where it left off.
+  ///
+  /// \param[in] s
+  ///     The stream object where the instructions are printed.
+  ///
+  /// \param[in] count
+  ///     The number of instructions to print.
+  void DumpInstructions(Stream &s, size_t count);
+
+  /// Skip \p count instructions in the direction specified by \p m_forwards.
+  void Skip(size_t count);
+
+private:
+  bool TryMoveOneInstruction();
+
+  lldb::TraceCursorUP m_cursor_up;
+  /// Whether to traverse the trace forwards of backwards.
+  bool m_forwards;
+  bool m_raw;
+  /// If \b true, all the instructions have been traversed.
+  bool m_no_more_data = false;
+  /// Instruction index for presentation purposes. It increases if the traversal
+  /// is done forwards, and decreases otherwise.
+  int m_index = 0;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_TARGET_TRACE_INSTRUCTION_DUMPER_H
Index: lldb/include/lldb/Target/TraceCursor.h
===================================================================
--- lldb/include/lldb/Target/TraceCursor.h
+++ lldb/include/lldb/Target/TraceCursor.h
@@ -11,6 +11,8 @@
 
 #include "lldb/lldb-private.h"
 
+#include "lldb/Target/ExecutionContext.h"
+
 namespace lldb_private {
 
 /// Class used for iterating over the instructions of a thread's trace.
@@ -59,6 +61,8 @@
 ///  } while(cursor->Prev(granularity));
 class TraceCursor {
 public:
+  TraceCursor(lldb::ThreadSP thread_sp);
+
   virtual ~TraceCursor() = default;
 
   /// Move the cursor to the next instruction more recent chronologically in the
@@ -87,6 +91,19 @@
                         lldb::eTraceInstructionControlFlowTypeInstruction,
                     bool ignore_errors = false) = 0;
 
+  /// Move the cursor \p count positions forwards chronologically.
+  ///
+  /// \param[in] count
+  ///     How many positions to move.
+  ///
+  /// \return
+  ///     The number of instructions or failures in the trace traversed.
+  size_t Next(size_t count);
+
+  /// Similar to \a TraceCursor::Next(int count), but moves backwards
+  /// chronologically.
+  size_t Prev(size_t count);
+
   /// Force the cursor to point to the end of the trace, i.e. the most recent
   /// item.
   virtual void SeekToEnd() = 0;
@@ -96,14 +113,17 @@
   virtual void SeekToBegin() = 0;
 
   /// \return
-  ///   \b true if the trace corresponds to a live process who has resumed after
-  ///   the trace cursor was created. Otherwise, including the case in which the
-  ///   process is a post-mortem one, return \b false.
-  bool IsStale();
+  ///   The \a ExecutionContextRef of the backing thread from the creation time
+  ///   of this cursor.
+  ExecutionContextRef &GetExecutionContextRef();
 
   /// Instruction or error information
   /// \{
 
+  /// \return
+  ///     Whether the cursor points to an error or not.
+  virtual bool IsError() = 0;
+
   /// Get the corresponding error message if the cursor points to an error in
   /// the trace.
   ///
@@ -124,14 +144,10 @@
   ///     to an error in the trace, return \b 0.
   virtual lldb::TraceInstructionControlFlowType
   GetInstructionControlFlowType() = 0;
-
   /// \}
 
-private:
-  /// The stop ID when the cursor was created.
-  uint32_t m_stop_id = 0;
-  /// The trace that owns this cursor.
-  lldb::TraceSP m_trace_sp;
+protected:
+  ExecutionContextRef m_exe_ctx_ref;
 };
 
 } // namespace lldb_private
Index: lldb/include/lldb/Target/Trace.h
===================================================================
--- lldb/include/lldb/Target/Trace.h
+++ lldb/include/lldb/Target/Trace.h
@@ -45,11 +45,6 @@
 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
@@ -137,62 +132,6 @@
   ///     The JSON schema of this Trace plug-in.
   virtual llvm::StringRef GetSchema() = 0;
 
-  /// Dump \a count instructions of the given thread's trace ending at the
-  /// given \a end_position position.
-  ///
-  /// The instructions are printed along with their indices or positions, which
-  /// are increasing chronologically. This means that the \a index 0 represents
-  /// the oldest instruction of the trace chronologically.
-  ///
-  /// \param[in] thread
-  ///     The thread whose trace will be dumped.
-  ///
-  /// \param[in] s
-  ///     The stream object where the instructions are printed.
-  ///
-  /// \param[in] count
-  ///     The number of instructions to print.
-  ///
-  /// \param[in] end_position
-  ///     The position of the last instruction to print.
-  ///
-  /// \param[in] raw
-  ///     Dump only instruction addresses without disassembly nor symbol
-  ///     information.
-  void DumpTraceInstructions(Thread &thread, Stream &s, size_t count,
-                             size_t end_position, bool raw);
-
-  /// Run the provided callback on the instructions of the trace of the given
-  /// thread.
-  ///
-  /// The instructions will be traversed starting at the given \a 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 incrementing indices. If
-  ///     \b TraceDirection::Backwards, the traversal is done backwards
-  ///     chronologically, i.e. with decrementing indices.
-  ///
-  /// \param[in] callback
-  ///     The callback to execute on each instruction. If it returns \b false,
-  ///     the iteration stops.
-  virtual void TraverseInstructions(
-      Thread &thread, size_t position, TraceDirection direction,
-      std::function<bool(size_t index, llvm::Expected<lldb::addr_t> load_addr)>
-          callback) = 0;
-
   /// Get a \a TraceCursor for the given thread's trace.
   ///
   /// \return
@@ -201,16 +140,6 @@
   ///     trace.
   virtual lldb::TraceCursorUP GetCursor(Thread &thread) = 0;
 
-  /// Get the number of available instructions in the trace of the given thread.
-  ///
-  /// \param[in] thread
-  ///     The thread whose trace will be inspected.
-  ///
-  /// \return
-  ///     The total number of instructions in the trace, or \a llvm::None if the
-  ///     thread is not being traced.
-  virtual llvm::Optional<size_t> GetInstructionCount(Thread &thread) = 0;
-
   /// Check if a thread is currently traced by this object.
   ///
   /// \param[in] thread
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to