public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Tom Tromey <tromey@adacore.com>
To: gdb-patches@sourceware.org
Subject: [PATCH 8/8] Handle array- and string-like values in no-op pretty printers
Date: Tue, 22 Aug 2023 09:25:14 -0600	[thread overview]
Message-ID: <20230822-array-and-string-like-v1-8-2dcea29b0567@adacore.com> (raw)
In-Reply-To: <20230822-array-and-string-like-v1-0-2dcea29b0567@adacore.com>

This changes the no-op pretty printers -- used by DAP -- to handle
array- and string-like objects known by the gdb core.  Two new tests
are added, one for Ada and one for Rust.
---
 gdb/python/lib/gdb/printing.py            |  26 ++++++-
 gdb/testsuite/gdb.dap/ada-arrays.exp      | 123 ++++++++++++++++++++++++++++++
 gdb/testsuite/gdb.dap/ada-arrays/cstuff.c |  22 ++++++
 gdb/testsuite/gdb.dap/ada-arrays/main.adb |  24 ++++++
 gdb/testsuite/gdb.dap/ada-arrays/pck.adb  |  21 +++++
 gdb/testsuite/gdb.dap/ada-arrays/pck.ads  |  51 +++++++++++++
 gdb/testsuite/gdb.dap/rust-slices.exp     | 119 +++++++++++++++++++++++++++++
 gdb/testsuite/gdb.dap/rust-slices.rs      |  29 +++++++
 8 files changed, 412 insertions(+), 3 deletions(-)

diff --git a/gdb/python/lib/gdb/printing.py b/gdb/python/lib/gdb/printing.py
index a668bd0e3fc..1a761a6aa57 100644
--- a/gdb/python/lib/gdb/printing.py
+++ b/gdb/python/lib/gdb/printing.py
@@ -18,6 +18,7 @@
 
 import gdb
 import gdb.types
+import itertools
 import re
 
 
@@ -285,11 +286,24 @@ class NoOpArrayPrinter:
     def __init__(self, ty, value):
         self.value = value
         (low, high) = ty.range()
-        self.low = low
-        self.high = high
+        # In Ada, an array can have an index type that is a
+        # non-contiguous enum.  In this case the indexing must be done
+        # by using the indices into the enum type, not the underlying
+        # integer values.
+        range_type = ty.fields()[0].type
+        if range_type.target().code == gdb.TYPE_CODE_ENUM:
+            e_values = range_type.target().fields()
+            # Drop any values before LOW.
+            e_values = itertools.dropwhile(lambda x: x.enumval < low, e_values)
+            # Drop any values after HIGH.
+            e_values = itertools.takewhile(lambda x: x.enumval <= high, e_values)
+            low = 0
+            high = len(list(e_values)) - 1
         # This is a convenience to the DAP code and perhaps other
         # users.
         self.num_children = high - low + 1
+        self.low = low
+        self.high = high
 
     def to_string(self):
         return ""
@@ -330,7 +344,13 @@ def make_visualizer(value):
         pass
     else:
         ty = value.type.strip_typedefs()
-        if ty.code == gdb.TYPE_CODE_ARRAY:
+        if ty.is_string_like:
+            result = gdb.printing.NoOpScalarPrinter(value)
+        elif ty.code == gdb.TYPE_CODE_ARRAY:
+            result = gdb.printing.NoOpArrayPrinter(ty, value)
+        elif ty.is_array_like:
+            value = value.to_array()
+            ty = value.type.strip_typedefs()
             result = gdb.printing.NoOpArrayPrinter(ty, value)
         elif ty.code in (gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION):
             result = gdb.printing.NoOpStructPrinter(ty, value)
diff --git a/gdb/testsuite/gdb.dap/ada-arrays.exp b/gdb/testsuite/gdb.dap/ada-arrays.exp
new file mode 100644
index 00000000000..13b5425ded4
--- /dev/null
+++ b/gdb/testsuite/gdb.dap/ada-arrays.exp
@@ -0,0 +1,123 @@
+# Copyright 2023 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/>.
+
+load_lib "ada.exp"
+load_lib dap-support.exp
+
+require allow_dap_tests
+require allow_ada_tests
+
+standard_ada_testfile main
+set cfile "cstuff"
+set csrcfile ${srcdir}/${subdir}/${testdir}/${cfile}.c
+set cobject [standard_output_file ${cfile}.o]
+
+gdb_compile "${csrcfile}" "${cobject}" object [list debug]
+if {[gdb_compile_ada "${srcfile}" "${binfile}" executable debug] != ""} {
+  return -1
+}
+
+if {[dap_launch $testfile] == ""} {
+    return
+}
+
+# Stop in a C frame, but examine values in an Ada frame, to make sure
+# cross-language scenarios work correctly.
+set line [gdb_get_line_number "STOP" $testdir/cstuff.c]
+set obj [dap_check_request_and_response "set breakpoint by line number" \
+	     setBreakpoints \
+	     [format {o source [o path [%s]] breakpoints [a [o line [i %d]]]} \
+		  [list s cstuff.c] $line]]
+set line_bpno [dap_get_breakpoint_number $obj]
+
+dap_check_request_and_response "start inferior" configurationDone
+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" $line_bpno
+
+set bt [lindex [dap_check_request_and_response "backtrace" stackTrace \
+		    {o threadId [i 1]}] \
+	    0]
+# The Ada frame is frame 1.
+set frame_id [dict get [lindex [dict get $bt body stackFrames] 1] id]
+
+set scopes [dap_check_request_and_response "get scopes" scopes \
+		[format {o frameId [i %d]} $frame_id]]
+set scopes [dict get [lindex $scopes 0] body scopes]
+
+gdb_assert {[llength $scopes] == 2} "two scopes"
+
+lassign $scopes scope ignore
+gdb_assert {[dict get $scope name] == "Arguments"} "scope is arguments"
+gdb_assert {[dict get $scope presentationHint] == "arguments"} \
+    "arguments presentation hint"
+gdb_assert {[dict get $scope namedVariables] == 3} "three vars in scope"
+
+set num [dict get $scope variablesReference]
+set refs [lindex [dap_check_request_and_response "fetch arguments" \
+		      "variables" \
+		      [format {o variablesReference [i %d]} $num]] \
+	      0]
+
+# Helper to check the contents of a single array-like object.  VAR is
+# the variable entry.  NAME is the name of the variable, pulled out
+# for convenience.# ARGS are the expected child values.
+proc check_array_contents {var name args} {
+    set len [llength $args]
+    gdb_assert {[dict get $var indexedVariables] == $len} \
+	"check length of $name variable"
+
+    set num [dict get $var variablesReference]
+    set refs [lindex [dap_check_request_and_response \
+			  "fetch contents of $name" \
+			  "variables" \
+			  [format {o variablesReference [i %d]} $num]] \
+		  0]
+
+    foreach subvar [dict get $refs body variables] subvalue $args {
+	set subname [dict get $subvar name]
+	gdb_assert {[dict get $subvar value] == $subvalue} \
+	    "check value of $name entry $subname"
+    }
+}
+
+foreach var [dict get $refs body variables] {
+    set name [dict get $var name]
+    switch $name {
+	"the_buffer" {
+	    check_array_contents $var $name 1 2 3 4
+	}
+
+	"the_ar" {
+	    check_array_contents $var $name 5 6 7 8 9
+	}
+
+	"hello" {
+	    # Note that the expected value looks strange here -- there
+	    # are too many backslashes.  This is a TON issue, as the
+	    # JSON looks ok: "value": "\"hello\"".
+	    gdb_assert {[dict get $var value] == "\\\"hello\\\""} \
+		"value of hello variable"
+	}
+
+	default {
+	    fail "unknown variable $name"
+	}
+    }
+}
+
+dap_shutdown
diff --git a/gdb/testsuite/gdb.dap/ada-arrays/cstuff.c b/gdb/testsuite/gdb.dap/ada-arrays/cstuff.c
new file mode 100644
index 00000000000..af87082401c
--- /dev/null
+++ b/gdb/testsuite/gdb.dap/ada-arrays/cstuff.c
@@ -0,0 +1,22 @@
+/* Copyright 2023 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/>.  */
+
+void
+c_procedure (int x)
+{
+  /* STOP */
+}
diff --git a/gdb/testsuite/gdb.dap/ada-arrays/main.adb b/gdb/testsuite/gdb.dap/ada-arrays/main.adb
new file mode 100644
index 00000000000..c9e98c4a249
--- /dev/null
+++ b/gdb/testsuite/gdb.dap/ada-arrays/main.adb
@@ -0,0 +1,24 @@
+--  Copyright 2023 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/>.
+
+with Pck; use Pck;
+
+procedure Main is
+   Value : Buffer (1 .. 4) := (1, 2, 3, 4);
+   Another_Value : AR := (5, 6, 7, 8, 9);
+   Hello: String := "hello";
+begin
+   Do_Nothing (Value, Another_Value, Hello);
+end Main;
diff --git a/gdb/testsuite/gdb.dap/ada-arrays/pck.adb b/gdb/testsuite/gdb.dap/ada-arrays/pck.adb
new file mode 100644
index 00000000000..7efa8930af8
--- /dev/null
+++ b/gdb/testsuite/gdb.dap/ada-arrays/pck.adb
@@ -0,0 +1,21 @@
+--  Copyright 2023 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/>.
+
+package body Pck is
+   procedure Do_Nothing (The_Buffer : in out Buffer; The_AR : in out AR; Hello: in out String) is
+   begin
+      C_Procedure (23);
+   end Do_Nothing;
+end Pck;
diff --git a/gdb/testsuite/gdb.dap/ada-arrays/pck.ads b/gdb/testsuite/gdb.dap/ada-arrays/pck.ads
new file mode 100644
index 00000000000..475bb7bf1e9
--- /dev/null
+++ b/gdb/testsuite/gdb.dap/ada-arrays/pck.ads
@@ -0,0 +1,51 @@
+--  Copyright 2023 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/>.
+
+package Pck is
+   pragma Linker_Options ("cstuff.o");
+
+   procedure C_Procedure (Value : Integer);
+   pragma Import(C, C_Procedure, "c_procedure");
+
+   type Small is new Integer range 0 .. 2 ** 6 - 1;
+
+   type Buffer is array (Integer range <>) of Small;
+   pragma Pack (Buffer);
+
+   type Enum_With_Gaps is
+     (
+      LIT0,
+      LIT1,
+      LIT2,
+      LIT3,
+      LIT4
+     );
+
+   for Enum_With_Gaps use
+     (
+      LIT0 => 3,
+      LIT1 => 5,
+      LIT2 => 8,
+      LIT3 => 13,
+      LIT4 => 21
+     );
+   for Enum_With_Gaps'size use 16;
+
+   type Enum_Subrange is new Enum_With_Gaps range Lit1 .. Lit3;
+
+   type AR is array (Enum_With_Gaps range <>) of Integer;
+
+   procedure Do_Nothing (The_Buffer : in out Buffer; The_AR : in out AR; Hello: in out String);
+end Pck;
diff --git a/gdb/testsuite/gdb.dap/rust-slices.exp b/gdb/testsuite/gdb.dap/rust-slices.exp
new file mode 100644
index 00000000000..96ed5dac8d3
--- /dev/null
+++ b/gdb/testsuite/gdb.dap/rust-slices.exp
@@ -0,0 +1,119 @@
+# Copyright 2023 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 "scopes" and "variables".
+
+load_lib rust-support.exp
+load_lib dap-support.exp
+
+require allow_dap_tests
+require allow_rust_tests
+require {can_compile rust}
+
+standard_testfile .rs
+
+if {[build_executable ${testfile}.exp $testfile $srcfile {debug rust}] == -1} {
+    return
+}
+
+if {[dap_launch $testfile] == ""} {
+    return
+}
+
+set line [gdb_get_line_number "STOP"]
+set obj [dap_check_request_and_response "set breakpoint by line number" \
+	     setBreakpoints \
+	     [format {o source [o path [%s]] breakpoints [a [o line [i %d]]]} \
+		  [list s $srcfile] $line]]
+set line_bpno [dap_get_breakpoint_number $obj]
+
+dap_check_request_and_response "start inferior" configurationDone
+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" $line_bpno
+
+set bt [lindex [dap_check_request_and_response "backtrace" stackTrace \
+		    {o threadId [i 1]}] \
+	    0]
+set frame_id [dict get [lindex [dict get $bt body stackFrames] 0] id]
+
+set scopes [dap_check_request_and_response "get scopes" scopes \
+		[format {o frameId [i %d]} $frame_id]]
+set scopes [dict get [lindex $scopes 0] body scopes]
+
+gdb_assert {[llength $scopes] == 2} "two scopes"
+
+lassign $scopes scope ignore
+gdb_assert {[dict get $scope name] == "Locals"} "scope is locals"
+gdb_assert {[dict get $scope presentationHint] == "locals"} \
+    "locals presentation hint"
+gdb_assert {[dict get $scope namedVariables] == 3} "three vars in scope"
+
+set num [dict get $scope variablesReference]
+set refs [lindex [dap_check_request_and_response "fetch variables" \
+		      "variables" \
+		      [format {o variablesReference [i %d]} $num]] \
+	      0]
+
+# Helper to check the contents of a single array-like object.  VAR is
+# the variable entry.  NAME is the name of the variable, pulled out
+# for convenience.  ARGS are the expected child values.
+proc check_array_contents {var name args} {
+    set len [llength $args]
+    gdb_assert {[dict get $var indexedVariables] == $len} \
+	"check length of $name variable"
+
+    set num [dict get $var variablesReference]
+    set refs [lindex [dap_check_request_and_response \
+			  "fetch contents of $name" \
+			  "variables" \
+			  [format {o variablesReference [i %d]} $num]] \
+		  0]
+
+    foreach subvar [dict get $refs body variables] subvalue $args {
+	set subname [dict get $subvar name]
+	gdb_assert {[dict get $subvar value] == $subvalue} \
+	    "check value of $name entry $subname"
+    }
+}
+
+foreach var [dict get $refs body variables] {
+    set name [dict get $var name]
+    switch $name {
+	"array" {
+	    check_array_contents $var $name 1 2 3 4
+	}
+
+	"slice" {
+	    check_array_contents $var $name 2
+	}
+
+	"hello" {
+	    # Note that the expected value looks strange here -- there
+	    # are too many backslashes.  This is a TON issue, as the
+	    # JSON looks ok: "value": "\"hello\"".
+	    gdb_assert {[dict get $var value] == "\\\"hello\\\""} \
+		"value of hello variable"
+	}
+
+	default {
+	    fail "unknown variable $name"
+	}
+    }
+}
+
+dap_shutdown
diff --git a/gdb/testsuite/gdb.dap/rust-slices.rs b/gdb/testsuite/gdb.dap/rust-slices.rs
new file mode 100644
index 00000000000..0f06a75cfa7
--- /dev/null
+++ b/gdb/testsuite/gdb.dap/rust-slices.rs
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 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/>.
+
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+
+fn empty () {
+}
+
+fn main () {
+    let array = [1,2,3,4];
+    let slice = &array[1..2];
+    let hello = "hello";
+
+    empty();			// STOP
+}

-- 
2.40.1


  parent reply	other threads:[~2023-08-22 15:25 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-08-22 15:25 [PATCH 0/8] Handle array- and string-like types in DAP Tom Tromey
2023-08-22 15:25 ` [PATCH 1/8] Move rust_language::lookup_symbol_nonlocal Tom Tromey
2023-08-22 15:25 ` [PATCH 2/8] Refactor Rust code for slice-to-array operation Tom Tromey
2023-08-22 15:25 ` [PATCH 3/8] Introduce TYPE_SPECIFIC_RUST_STUFF Tom Tromey
2023-08-22 15:25 ` [PATCH 4/8] Use ada_value_subscript in valpy_getitem Tom Tromey
2023-08-22 15:25 ` [PATCH 5/8] Introduce type::is_array_like and value_to_array Tom Tromey
2023-08-22 15:25 ` [PATCH 6/8] Select frame when fetching a frame variable in DAP Tom Tromey
2023-08-22 15:25 ` [PATCH 7/8] Add new Python APIs to support DAP value display Tom Tromey
2023-08-22 15:46   ` Eli Zaretskii
2023-08-22 15:25 ` Tom Tromey [this message]
2023-09-05 17:22 ` [PATCH 0/8] Handle array- and string-like types in DAP Tom Tromey

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230822-array-and-string-like-v1-8-2dcea29b0567@adacore.com \
    --to=tromey@adacore.com \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).