xref: /llvm-project/lldb/test/API/commands/trace/TestTraceDumpInstructions.py (revision 2238dcc39358353cac21df75c3c3286ab20b8f53)
1from intelpt_testcase import *
2from lldbsuite.test.lldbtest import *
3from lldbsuite.test.decorators import *
4
5
6class TestTraceDumpInstructions(TraceIntelPTTestCaseBase):
7    def testErrorMessages(self):
8        # We first check the output when there are no targets
9        self.expect(
10            "thread trace dump instructions",
11            substrs=[
12                "error: invalid target, create a target using the 'target create' command"
13            ],
14            error=True,
15        )
16
17        # We now check the output when there's a non-running target
18        self.expect(
19            "target create "
20            + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")
21        )
22
23        self.expect(
24            "thread trace dump instructions",
25            substrs=["error: Command requires a current process."],
26            error=True,
27        )
28
29        # Now we check the output when there's a running target without a trace
30        self.expect("b main")
31        self.expect("run")
32
33        self.expect(
34            "thread trace dump instructions",
35            substrs=["error: Process is not being traced"],
36            error=True,
37        )
38
39    def testRawDumpInstructionsInJSON(self):
40        self.expect(
41            "trace load -v "
42            + os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"),
43            substrs=["intel-pt"],
44        )
45
46        self.expect(
47            "thread trace dump instructions --raw --count 5 --forwards --json",
48            substrs=[
49                """[{"id":3,"loadAddress":"0x400511"}""",
50                """{"id":7,"loadAddress":"0x400518"}""",
51                """{"id":8,"loadAddress":"0x40051f"}""",
52                """{"id":9,"loadAddress":"0x400529"}""",
53                """{"id":10,"loadAddress":"0x40052d"}""",
54            ],
55        )
56
57        self.expect(
58            "thread trace dump instructions --raw --count 5 --forwards --pretty-json",
59            substrs=[
60                """[
61  {
62    "id": 3,
63    "loadAddress": "0x400511"
64  },
65  {
66    "id": 7,
67    "loadAddress": "0x400518"
68  },
69  {
70    "id": 8,
71    "loadAddress": "0x40051f"
72  },
73  {
74    "id": 9,
75    "loadAddress": "0x400529"
76  },
77  {
78    "id": 10,
79    "loadAddress": "0x40052d"
80  }
81]"""
82            ],
83        )
84
85    def testRawDumpInstructionsInJSONToFile(self):
86        self.expect(
87            "trace load -v "
88            + os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"),
89            substrs=["intel-pt"],
90        )
91
92        outfile = os.path.join(self.getBuildDir(), "output.json")
93
94        self.expect(
95            "thread trace dump instructions --raw --count 5 --forwards --pretty-json --file "
96            + outfile
97        )
98
99        with open(outfile, "r") as out:
100            self.assertEqual(
101                out.read(),
102                """[
103  {
104    "id": 3,
105    "loadAddress": "0x400511"
106  },
107  {
108    "id": 7,
109    "loadAddress": "0x400518"
110  },
111  {
112    "id": 8,
113    "loadAddress": "0x40051f"
114  },
115  {
116    "id": 9,
117    "loadAddress": "0x400529"
118  },
119  {
120    "id": 10,
121    "loadAddress": "0x40052d"
122  }
123]""",
124            )
125
126    def testRawDumpInstructions(self):
127        self.expect(
128            "trace load -v "
129            + os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"),
130            substrs=["intel-pt"],
131        )
132
133        self.expect(
134            "thread trace dump instructions --raw --count 21 --forwards",
135            substrs=[
136                """thread #1: tid = 3842849
137    3: 0x0000000000400511
138    7: 0x0000000000400518
139    8: 0x000000000040051f
140    9: 0x0000000000400529
141    10: 0x000000000040052d
142    11: 0x0000000000400521
143    12: 0x0000000000400525
144    13: 0x0000000000400529
145    14: 0x000000000040052d
146    15: 0x0000000000400521
147    16: 0x0000000000400525
148    17: 0x0000000000400529
149    18: 0x000000000040052d
150    19: 0x0000000000400521
151    20: 0x0000000000400525
152    21: 0x0000000000400529
153    22: 0x000000000040052d
154    23: 0x0000000000400521
155    24: 0x0000000000400525
156    25: 0x0000000000400529
157    26: 0x000000000040052"""
158            ],
159        )
160
161        # We check if we can pass count and skip
162        self.expect(
163            "thread trace dump instructions --count 5 --skip 6 --raw --forwards",
164            substrs=[
165                """thread #1: tid = 3842849
166    7: 0x0000000000400518
167    8: 0x000000000040051f
168    9: 0x0000000000400529
169    10: 0x000000000040052d
170    11: 0x0000000000400521"""
171            ],
172        )
173
174        self.expect(
175            "thread trace dump instructions --count 5 --skip 6 --raw",
176            substrs=[
177                """thread #1: tid = 3842849
178    21: 0x0000000000400529
179    20: 0x0000000000400525
180    19: 0x0000000000400521
181    18: 0x000000000040052d
182    17: 0x0000000000400529"""
183            ],
184        )
185
186        # We check if we can pass count and skip and instruction id in hex
187        self.expect(
188            "thread trace dump instructions --count 5 --skip 6 --raw --id 0xE",
189            substrs=[
190                """thread #1: tid = 3842849
191    8: 0x000000000040051f
192    7: 0x0000000000400518
193    3: 0x0000000000400511
194    no more data"""
195            ],
196        )
197
198        # We check if we can pass count and skip and instruction id in decimal
199        self.expect(
200            "thread trace dump instructions --count 5 --skip 6 --raw --id 14",
201            substrs=[
202                """thread #1: tid = 3842849
203    8: 0x000000000040051f
204    7: 0x0000000000400518
205    3: 0x0000000000400511
206    no more data"""
207            ],
208        )
209
210        # We check if we can access the thread by index id
211        self.expect(
212            "thread trace dump instructions 1 --raw",
213            substrs=[
214                """thread #1: tid = 3842849
215    26: 0x000000000040052d"""
216            ],
217        )
218
219        # We check that we get an error when using an invalid thread index id
220        self.expect(
221            "thread trace dump instructions 10",
222            error=True,
223            substrs=['error: no thread with index: "10"'],
224        )
225
226    def testDumpFullInstructionsWithMultipleThreads(self):
227        # We load a trace with two threads
228        self.expect(
229            "trace load -v "
230            + os.path.join(self.getSourceDir(), "intelpt-trace", "trace_2threads.json")
231        )
232
233        # We print the instructions of a specific thread
234        self.expect(
235            "thread trace dump instructions 2 --count 2",
236            substrs=[
237                """thread #2: tid = 3842850
238  a.out`main + 32 at main.cpp:4
239    26: 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5
240    25: 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)"""
241            ],
242        )
243
244        # We use custom --count and --skip, saving the command to history for later
245        self.expect(
246            "thread trace dump instructions 2 --count 2 --skip 2",
247            inHistory=True,
248            substrs=[
249                """thread #2: tid = 3842850
250  a.out`main + 28 at main.cpp:4
251    25: 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)
252    24: 0x0000000000400525    addl   $0x1, -0x8(%rbp)"""
253            ],
254        )
255
256        # We use a repeat command twice and ensure the previous count is used and the
257        # start position moves with each command.
258        self.expect(
259            "",
260            inHistory=True,
261            substrs=[
262                """thread #2: tid = 3842850
263  a.out`main + 20 at main.cpp:5
264    23: 0x0000000000400521    xorl   $0x1, -0x4(%rbp)
265  a.out`main + 32 at main.cpp:4
266    22: 0x000000000040052d    jle    0x400521                  ; <+20> at main.cpp:5"""
267            ],
268        )
269
270        self.expect(
271            "",
272            inHistory=True,
273            substrs=[
274                """thread #2: tid = 3842850
275  a.out`main + 28 at main.cpp:4
276    21: 0x0000000000400529    cmpl   $0x3, -0x8(%rbp)
277    20: 0x0000000000400525    addl   $0x1, -0x8(%rbp"""
278            ],
279        )
280
281    def testInvalidBounds(self):
282        self.expect(
283            "trace load -v "
284            + os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json")
285        )
286
287        # The output should be work when too many instructions are asked
288        self.expect(
289            "thread trace dump instructions --count 20 --forwards",
290            substrs=[
291                """thread #1: tid = 3842849
292  a.out`main + 4 at main.cpp:2
293    3: 0x0000000000400511    movl   $0x0, -0x4(%rbp)
294  a.out`main + 11 at main.cpp:4
295    7: 0x0000000000400518    movl   $0x0, -0x8(%rbp)
296    8: 0x000000000040051f    jmp    0x400529                  ; <+28> at main.cpp:4"""
297            ],
298        )
299
300        # Should print no instructions if the position is out of bounds
301        self.expect("thread trace dump instructions --skip 23", endstr="no more data\n")
302
303        # Should fail with negative bounds
304        self.expect("thread trace dump instructions --skip -1", error=True)
305        self.expect("thread trace dump instructions --count -1", error=True)
306
307    def testWrongImage(self):
308        self.expect(
309            "trace load "
310            + os.path.join(self.getSourceDir(), "intelpt-trace", "trace_bad_image.json")
311        )
312        self.expect(
313            "thread trace dump instructions --forwards",
314            substrs=[
315                """thread #1: tid = 3842849
316    ...missing instructions
317    3: (error) no memory mapped at this address: 0x0000000000400511"""
318            ],
319        )
320
321    def testWrongCPU(self):
322        self.expect(
323            "trace load "
324            + os.path.join(self.getSourceDir(), "intelpt-trace", "trace_wrong_cpu.json")
325        )
326        self.expect(
327            "thread trace dump instructions --forwards",
328            substrs=["error: unknown cpu"],
329            error=True,
330        )
331
332    def testMultiFileTraceWithMissingModuleInJSON(self):
333        self.expect(
334            "trace load "
335            + os.path.join(
336                self.getSourceDir(), "intelpt-trace-multi-file", "multi-file-no-ld.json"
337            )
338        )
339
340        self.expect(
341            "thread trace dump instructions --count 4 --id 9 --forwards --pretty-json",
342            substrs=[
343                """[
344  {
345    "id": 9,
346    "loadAddress": "0x40054b",
347    "module": "a.out",
348    "symbol": "foo()",
349    "mnemonic": "jmp"
350  },
351  {
352    "id": 10,
353    "loadAddress": "0x400510",
354    "module": "a.out",
355    "symbol": null,
356    "mnemonic": "pushq"
357  },
358  {
359    "id": 11,
360    "loadAddress": "0x400516",
361    "module": "a.out",
362    "symbol": null,
363    "mnemonic": "jmpq"
364  },
365  {
366    "id": 12,
367    "error": "no memory mapped at this address: 0x00007ffff7df1950"
368  },
369  {
370    "id": 16,
371    "loadAddress": "0x400674",
372    "module": "a.out",
373    "symbol": "main",
374    "mnemonic": "movl",
375    "source": "/home/wallace/llvm-sand/external/llvm-project/lldb/test/API/commands/trace/intelpt-trace-multi-file/main.cpp",
376    "line": 10,
377    "column": 0
378  }
379]"""
380            ],
381        )
382
383        self.expect(
384            "thread trace dump instructions --count 4 --id 20 --forwards --pretty-json",
385            substrs=[
386                """[
387  {
388    "id": 20,
389    "loadAddress": "0x400677",
390    "module": "a.out",
391    "symbol": "main",
392    "mnemonic": "movl",
393    "source": "/home/wallace/llvm-sand/external/llvm-project/lldb/test/API/commands/trace/intelpt-trace-multi-file/main.cpp",
394    "line": 12,
395    "column": 0
396  },
397  {
398    "id": 21,
399    "loadAddress": "0x40067a",
400    "module": "a.out",
401    "symbol": "main",
402    "mnemonic": "addl",
403    "source": "/home/wallace/llvm-sand/external/llvm-project/lldb/test/API/commands/trace/intelpt-trace-multi-file/main.cpp",
404    "line": 12,
405    "column": 0
406  },
407  {
408    "id": 22,
409    "loadAddress": "0x40067f",
410    "module": "a.out",
411    "symbol": "main",
412    "mnemonic": "movl",
413    "source": "/home/wallace/llvm-sand/external/llvm-project/lldb/test/API/commands/trace/intelpt-trace-multi-file/main.cpp",
414    "line": 12,
415    "column": 0
416  },
417  {
418    "id": 26,
419    "loadAddress": "0x400682",
420    "module": "a.out",
421    "symbol": "inline_function()",
422    "mnemonic": "movl",
423    "source": "/home/wallace/llvm-sand/external/llvm-project/lldb/test/API/commands/trace/intelpt-trace-multi-file/main.cpp",
424    "line": 4,
425    "column": 0
426  }
427]"""
428            ],
429        )
430
431    def testMultiFileTraceWithMissingModule(self):
432        self.expect(
433            "trace load "
434            + os.path.join(
435                self.getSourceDir(), "intelpt-trace-multi-file", "multi-file-no-ld.json"
436            )
437        )
438
439        # This instructions in this test covers the following flow:
440        #
441        # - The trace starts with a call to libfoo, which triggers the dynamic
442        #   linker, but the dynamic linker is not included in the JSON file,
443        #   thus the trace reports a set of missing instructions after
444        #   instruction [6].
445        # - Then, the dump continues in the next synchronization point showing
446        #   a call to an inlined function, which is displayed as [inlined].
447        # - Finally, a call to libfoo is performed, which invokes libbar inside.
448        #
449        # Whenever there's a line or symbol change, including the inline case, a
450        # line is printed showing the symbol context change.
451        #
452        # Finally, the instruction disassembly is included in the dump.
453        self.expect(
454            "thread trace dump instructions --count 50 --forwards",
455            substrs=[
456                """thread #1: tid = 815455
457  a.out`main + 15 at main.cpp:10
458    3: 0x000000000040066f    callq  0x400540                  ; symbol stub for: foo()
459  a.out`symbol stub for: foo()
460    7: 0x0000000000400540    jmpq   *0x200ae2(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 40
461    8: 0x0000000000400546    pushq  $0x2
462    9: 0x000000000040054b    jmp    0x400510
463  a.out`(none)
464    10: 0x0000000000400510    pushq  0x200af2(%rip)            ; _GLOBAL_OFFSET_TABLE_ + 8
465    11: 0x0000000000400516    jmpq   *0x200af4(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 16
466    ...missing instructions
467    12: (error) no memory mapped at this address: 0x00007ffff7df1950
468  a.out`main + 20 at main.cpp:10
469    16: 0x0000000000400674    movl   %eax, -0xc(%rbp)
470  a.out`main + 23 at main.cpp:12
471    20: 0x0000000000400677    movl   -0xc(%rbp), %eax
472    21: 0x000000000040067a    addl   $0x1, %eax
473    22: 0x000000000040067f    movl   %eax, -0xc(%rbp)
474  a.out`main + 34 [inlined] inline_function() at main.cpp:4
475    26: 0x0000000000400682    movl   $0x0, -0x4(%rbp)
476  a.out`main + 41 [inlined] inline_function() + 7 at main.cpp:5
477    27: 0x0000000000400689    movl   -0x4(%rbp), %eax
478    28: 0x000000000040068c    addl   $0x1, %eax
479    29: 0x0000000000400691    movl   %eax, -0x4(%rbp)
480  a.out`main + 52 [inlined] inline_function() + 18 at main.cpp:6
481    30: 0x0000000000400694    movl   -0x4(%rbp), %eax
482  a.out`main + 55 at main.cpp:14
483    31: 0x0000000000400697    movl   -0xc(%rbp), %ecx
484    32: 0x000000000040069a    addl   %eax, %ecx
485    33: 0x000000000040069c    movl   %ecx, -0xc(%rbp)
486  a.out`main + 63 at main.cpp:16
487    37: 0x000000000040069f    callq  0x400540                  ; symbol stub for: foo()
488  a.out`symbol stub for: foo()
489    38: 0x0000000000400540    jmpq   *0x200ae2(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 40
490  libfoo.so`foo() at foo.cpp:3
491    39: 0x00007ffff7bd96e0    pushq  %rbp
492    40: 0x00007ffff7bd96e1    movq   %rsp, %rbp
493  libfoo.so`foo() + 4 at foo.cpp:4
494    41: 0x00007ffff7bd96e4    subq   $0x10, %rsp
495    42: 0x00007ffff7bd96e8    callq  0x7ffff7bd95d0            ; symbol stub for: bar()
496  libfoo.so`symbol stub for: bar()
497    43: 0x00007ffff7bd95d0    jmpq   *0x200a4a(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 32
498  libbar.so`bar() at bar.cpp:1
499    44: 0x00007ffff79d7690    pushq  %rbp
500    45: 0x00007ffff79d7691    movq   %rsp, %rbp
501  libbar.so`bar() + 4 at bar.cpp:2
502    46: 0x00007ffff79d7694    movl   $0x1, -0x4(%rbp)
503  libbar.so`bar() + 11 at bar.cpp:3
504    47: 0x00007ffff79d769b    movl   -0x4(%rbp), %eax
505    48: 0x00007ffff79d769e    addl   $0x1, %eax
506    49: 0x00007ffff79d76a3    movl   %eax, -0x4(%rbp)
507  libbar.so`bar() + 22 at bar.cpp:4
508    50: 0x00007ffff79d76a6    movl   -0x4(%rbp), %eax
509    51: 0x00007ffff79d76a9    popq   %rbp
510    52: 0x00007ffff79d76aa    retq""",
511                """libfoo.so`foo() + 13 at foo.cpp:4
512    53: 0x00007ffff7bd96ed    movl   %eax, -0x4(%rbp)
513  libfoo.so`foo() + 16 at foo.cpp:5
514    54: 0x00007ffff7bd96f0    movl   -0x4(%rbp), %eax
515    55: 0x00007ffff7bd96f3    addl   $0x1, %eax
516    56: 0x00007ffff7bd96f8    movl   %eax, -0x4(%rbp)
517  libfoo.so`foo() + 27 at foo.cpp:6
518    57: 0x00007ffff7bd96fb    movl   -0x4(%rbp), %eax
519    58: 0x00007ffff7bd96fe    addq   $0x10, %rsp
520    59: 0x00007ffff7bd9702    popq   %rbp
521    60: 0x00007ffff7bd9703    retq""",
522                """a.out`main + 68 at main.cpp:16
523    61: 0x00000000004006a4    movl   -0xc(%rbp), %ecx
524    62: 0x00000000004006a7    addl   %eax, %ecx
525    63: 0x00000000004006a9    movl   %ecx, -0xc(%rbp)
526    no more data""",
527            ],
528        )
529
530        self.expect(
531            "thread trace dump instructions --count 50",
532            substrs=[
533                """thread #1: tid = 815455
534  a.out`main + 73 at main.cpp:16
535    63: 0x00000000004006a9    movl   %ecx, -0xc(%rbp)
536    62: 0x00000000004006a7    addl   %eax, %ecx
537    61: 0x00000000004006a4    movl   -0xc(%rbp), %ecx
538  libfoo.so`foo() + 35 at foo.cpp:6
539    60: 0x00007ffff7bd9703    retq""",
540                """59: 0x00007ffff7bd9702    popq   %rbp
541    58: 0x00007ffff7bd96fe    addq   $0x10, %rsp
542    57: 0x00007ffff7bd96fb    movl   -0x4(%rbp), %eax
543  libfoo.so`foo() + 24 at foo.cpp:5
544    56: 0x00007ffff7bd96f8    movl   %eax, -0x4(%rbp)
545    55: 0x00007ffff7bd96f3    addl   $0x1, %eax
546    54: 0x00007ffff7bd96f0    movl   -0x4(%rbp), %eax
547  libfoo.so`foo() + 13 at foo.cpp:4
548    53: 0x00007ffff7bd96ed    movl   %eax, -0x4(%rbp)
549  libbar.so`bar() + 26 at bar.cpp:4
550    52: 0x00007ffff79d76aa    retq""",
551                """51: 0x00007ffff79d76a9    popq   %rbp
552    50: 0x00007ffff79d76a6    movl   -0x4(%rbp), %eax
553  libbar.so`bar() + 19 at bar.cpp:3
554    49: 0x00007ffff79d76a3    movl   %eax, -0x4(%rbp)
555    48: 0x00007ffff79d769e    addl   $0x1, %eax
556    47: 0x00007ffff79d769b    movl   -0x4(%rbp), %eax
557  libbar.so`bar() + 4 at bar.cpp:2
558    46: 0x00007ffff79d7694    movl   $0x1, -0x4(%rbp)
559  libbar.so`bar() + 1 at bar.cpp:1
560    45: 0x00007ffff79d7691    movq   %rsp, %rbp
561    44: 0x00007ffff79d7690    pushq  %rbp
562  libfoo.so`symbol stub for: bar()
563    43: 0x00007ffff7bd95d0    jmpq   *0x200a4a(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 32
564  libfoo.so`foo() + 8 at foo.cpp:4
565    42: 0x00007ffff7bd96e8    callq  0x7ffff7bd95d0            ; symbol stub for: bar()
566    41: 0x00007ffff7bd96e4    subq   $0x10, %rsp
567  libfoo.so`foo() + 1 at foo.cpp:3
568    40: 0x00007ffff7bd96e1    movq   %rsp, %rbp
569    39: 0x00007ffff7bd96e0    pushq  %rbp
570  a.out`symbol stub for: foo()
571    38: 0x0000000000400540    jmpq   *0x200ae2(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 40
572  a.out`main + 63 at main.cpp:16
573    37: 0x000000000040069f    callq  0x400540                  ; symbol stub for: foo()
574  a.out`main + 60 at main.cpp:14
575    33: 0x000000000040069c    movl   %ecx, -0xc(%rbp)
576    32: 0x000000000040069a    addl   %eax, %ecx
577    31: 0x0000000000400697    movl   -0xc(%rbp), %ecx
578  a.out`main + 52 [inlined] inline_function() + 18 at main.cpp:6
579    30: 0x0000000000400694    movl   -0x4(%rbp), %eax
580  a.out`main + 49 [inlined] inline_function() + 15 at main.cpp:5
581    29: 0x0000000000400691    movl   %eax, -0x4(%rbp)
582    28: 0x000000000040068c    addl   $0x1, %eax
583    27: 0x0000000000400689    movl   -0x4(%rbp), %eax
584  a.out`main + 34 [inlined] inline_function() at main.cpp:4
585    26: 0x0000000000400682    movl   $0x0, -0x4(%rbp)
586  a.out`main + 31 at main.cpp:12
587    22: 0x000000000040067f    movl   %eax, -0xc(%rbp)
588    21: 0x000000000040067a    addl   $0x1, %eax
589    20: 0x0000000000400677    movl   -0xc(%rbp), %eax
590  a.out`main + 20 at main.cpp:10
591    16: 0x0000000000400674    movl   %eax, -0xc(%rbp)
592    ...missing instructions
593    12: (error) no memory mapped at this address: 0x00007ffff7df1950
594  a.out`(none)
595    11: 0x0000000000400516    jmpq   *0x200af4(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 16
596    10: 0x0000000000400510    pushq  0x200af2(%rip)            ; _GLOBAL_OFFSET_TABLE_ + 8
597  a.out`symbol stub for: foo() + 11
598    9: 0x000000000040054b    jmp    0x400510
599    8: 0x0000000000400546    pushq  $0x2
600    7: 0x0000000000400540    jmpq   *0x200ae2(%rip)           ; _GLOBAL_OFFSET_TABLE_ + 40
601  a.out`main + 15 at main.cpp:10
602    3: 0x000000000040066f    callq  0x400540                  ; symbol stub for: foo()
603    no more data""",
604            ],
605        )
606
607        self.expect(
608            "thread trace dump instructions --skip 100 --forwards",
609            inHistory=True,
610            substrs=[
611                """thread #1: tid = 815455
612    no more data"""
613            ],
614        )
615
616        self.expect(
617            "",
618            substrs=[
619                """thread #1: tid = 815455
620    no more data"""
621            ],
622        )
623
624        self.expect(
625            "thread trace dump instructions --raw --all --forwards",
626            substrs=[
627                """thread #1: tid = 815455
628    3: 0x000000000040066f
629    7: 0x0000000000400540""",
630                """11: 0x0000000000400516
631    ...missing instructions
632    12: (error) no memory mapped at this address: 0x00007ffff7df1950
633    16: 0x0000000000400674""",
634                """61: 0x00000000004006a4
635    62: 0x00000000004006a7
636    63: 0x00000000004006a9
637    no more data""",
638            ],
639        )
640