diff options
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/python/lib/gdb/dap/disassemble.py | 59 | ||||
-rw-r--r-- | gdb/testsuite/gdb.dap/disassem.c | 52 | ||||
-rw-r--r-- | gdb/testsuite/gdb.dap/disassem.exp | 105 |
3 files changed, 209 insertions, 7 deletions
diff --git a/gdb/python/lib/gdb/dap/disassemble.py b/gdb/python/lib/gdb/dap/disassemble.py index 65bf3d4..d65790a 100644 --- a/gdb/python/lib/gdb/dap/disassemble.py +++ b/gdb/python/lib/gdb/dap/disassemble.py @@ -16,6 +16,50 @@ import gdb from .server import capability, request +from .sources import make_source + + +# This tracks labels associated with a disassembly request and helps +# with updating individual instructions. +class _BlockTracker: + def __init__(self): + # Map from PC to symbol names. A given PC is assumed to have + # just one label -- DAP wouldn't let us return multiple labels + # anyway. + self.labels = {} + # List of blocks that have already been handled. Note that + # blocks aren't hashable so a set is not used. + self.blocks = [] + + # Add a gdb.Block and its superblocks, ignoring the static and + # global block. BLOCK can also be None, which is ignored. + def add_block(self, block): + while block is not None: + if block.is_static or block.is_global or block in self.blocks: + return + self.blocks.append(block) + if block.function is not None: + self.labels[block.start] = block.function.name + for sym in block: + if sym.addr_class == gdb.SYMBOL_LOC_LABEL: + self.labels[int(sym.value())] = sym.name + block = block.superblock + + # Add PC to this tracker. Update RESULT as appropriate with + # information about the source and any label. + def add_pc(self, pc, result): + self.add_block(gdb.block_for_pc(pc)) + if pc in self.labels: + result["symbol"] = self.labels[pc] + sal = gdb.find_pc_line(pc) + if sal.symtab is not None: + if sal.line != 0: + result["line"] = sal.line + if sal.symtab.filename is not None: + # The spec says this can be omitted in some + # situations, but it's a little simpler to just always + # supply it. + result["location"] = make_source(sal.symtab.filename) @request("disassemble") @@ -35,17 +79,18 @@ def disassemble( except gdb.error: # Maybe there was no frame. arch = inf.architecture() + tracker = _BlockTracker() result = [] total_count = instructionOffset + instructionCount for elt in arch.disassemble(pc, count=total_count)[instructionOffset:]: mem = inf.read_memory(elt["addr"], elt["length"]) - result.append( - { - "address": hex(elt["addr"]), - "instruction": elt["asm"], - "instructionBytes": mem.hex(), - } - ) + insn = { + "address": hex(elt["addr"]), + "instruction": elt["asm"], + "instructionBytes": mem.hex(), + } + tracker.add_pc(elt["addr"], insn) + result.append(insn) return { "instructions": result, } diff --git a/gdb/testsuite/gdb.dap/disassem.c b/gdb/testsuite/gdb.dap/disassem.c new file mode 100644 index 0000000..c0f5128 --- /dev/null +++ b/gdb/testsuite/gdb.dap/disassem.c @@ -0,0 +1,52 @@ +/* Copyright 2024 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +__attribute__((__noinline__)) static int +callee (int x) +{ + return x * 2; +} + +static inline int __attribute__((__always_inline__)) +compute (int x) +{ + return callee (x); +} + +static int +return_value (int x) +{ + int accum = 0; + + for (int i = 0; i < x; ++i) + { + int value = compute (i); + if (value < 0) + goto out_label; + } + + out_label: + + return accum; +} + +int +main () +{ + int value = return_value (23); + return value > 0 ? 0 : 1; +} diff --git a/gdb/testsuite/gdb.dap/disassem.exp b/gdb/testsuite/gdb.dap/disassem.exp new file mode 100644 index 0000000..87fb516 --- /dev/null +++ b/gdb/testsuite/gdb.dap/disassem.exp @@ -0,0 +1,105 @@ +# Copyright 2024 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Test DAP disassembly. + +require allow_dap_tests + +load_lib dap-support.exp + +standard_testfile + +if {[build_executable ${testfile}.exp $testfile] == -1} { + return +} + +if {[dap_initialize] == ""} { + return +} + +set obj [dap_check_request_and_response "set breakpoint" \ + setFunctionBreakpoints \ + {o breakpoints [a [o name [s main]]]}] +set fn_bpno [dap_get_breakpoint_number $obj] + +dap_check_request_and_response "configurationDone" configurationDone + +if {[dap_launch $testfile] == ""} { + return +} +dap_wait_for_event_and_check "inferior started" thread "body reason" started + +dap_wait_for_event_and_check "stopped at line breakpoint" stopped \ + "body reason" breakpoint \ + "body hitBreakpointIds" $fn_bpno + +# Find out how many lines of disassembly we should request. This is +# kind of lame but DAP doesn't really provide tools to do this, and +# gdb's DAP implementation doesn't try to figure out what memory might +# not really be part of a function. +set obj [dap_check_request_and_response "disassemble using CLI" \ + evaluate {o expression [s {disassemble &return_value}] \ + context [s repl]}] +set output [dict get [lindex $obj 0] body result] +# The result will have literal "\" "n" sequences, turn these into +# newlines. +set with_nl [string map [list "\\n" "\n"] $output] +# The value we want is the number of lines starting with an address. +set insn_count 0 +foreach line [split $with_nl "\n"] { + if {[regexp "^ *0x" $line]} { + incr insn_count + } +} + +set obj [dap_check_request_and_response "find function address" \ + evaluate {o expression [s "&return_value"]}] +set pc [dict get [lindex $obj 0] body memoryReference] + +set obj [dap_check_request_and_response "disassemble the function" \ + disassemble \ + [format {o memoryReference [s %s] instructionCount [i %d]} \ + $pc $insn_count]] +set response [lindex $obj 0] + +set seen_labels(_) _ +set insn_no 1 +foreach insn [dict get $response body instructions] { + with_test_prefix $insn_no { + gdb_assert {[dict exists $insn line]} \ + "line in disassemble output" + gdb_assert {[dict exists $insn location]} \ + "location in disassemble output" + if {[dict exists $insn symbol]} { + set seen_labels([dict get $insn symbol]) 1 + } + } + incr insn_no +} + +proc require_label {name} { + global seen_labels + if {[info exists seen_labels($name)]} { + pass "saw label $name" + } else { + fail "saw label $name" + } +} + +require_label return_value +require_label compute +require_label out_label + +dap_shutdown |