From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-oi1-x233.google.com (mail-oi1-x233.google.com [IPv6:2607:f8b0:4864:20::233]) by sourceware.org (Postfix) with ESMTPS id C10AC3858C53 for ; Fri, 20 Jan 2023 02:09:15 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org C10AC3858C53 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oi1-x233.google.com with SMTP id r132so3285259oif.10 for ; Thu, 19 Jan 2023 18:09:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=WW2r/9X8ehyo//ZYbMAqmu2I6jiTwvwGsiERBLMspTA=; b=WZn0+nyzOz7n7GqEy0fjAT3bRQNrswLJF9gr2+658LrQLlb9uIqovootg/4Tat724/ I83bmTY6Ee146SXB7/wbKImIybKUN4xnwomoPUtwt1eko5V/5LROLEt4T4I1iTJ+HIjX b0xCi9Xi23Zqjem6rQXX0pDyplAKySx5AWsB6+hw7m8e7aWiu4Xlij+ypXENm6KggkTN dAX3jlwVRmmqFr/YaLAW0puO5a4P8BLzkb93jJdBxCKsm0Fu85qKQ4D8q5PZnuO/UtPx KZnBLMdcH127t1EfsOA/BeOy5DQ+dIAVXaz2f5xskQkardoSvUriQEFeQLk5ZuAOkiJF 7f/A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=WW2r/9X8ehyo//ZYbMAqmu2I6jiTwvwGsiERBLMspTA=; b=y83Hu2P7NgpXtBG5qGgTT/6uVWP60Cgc4WjxspiY9xsbRyPbgq4T8ChMWZCK3i/KmX pQtEAp3eC4P0QqvXRrG6FYmwtHAKpa9y6LTNXC4GzDIkAhCG+g1KBAKB+ee4skXy19De qaZfe9c6RuYvVni2FCIALgjcZIroY2IpeEfqM67Lm6J+M1CcTYE2dW0V+LkVzGVnc12i gg3HDbEhcS/teYSVIrrxZMhbpDuR9Y/5tYjqpWe9g8oZc38yVM5m/EQz8Mym9bLuWDe9 1QiIiQEBg1m9YEhDih/MUNBWkTrLgwKAVSn8pqBxeV3y4A6w69k5XUAtKnu9BlXWK/m6 iCDA== X-Gm-Message-State: AFqh2kpoaLnaAPYqT+yNMTrrSROozesb4v/tht5JR8s7nx/OaUVR/42t rgBlz7/oMI4eFvoOiOqAW3W2aYpMC+oIzQ== X-Google-Smtp-Source: AMrXdXuYSNWJyX6c/YRY0nKMuRprTa7EZBSzgi4+/hrxZweh/ynmjFZYW8oHTaZNLCrBCWvCvXSUag== X-Received: by 2002:a05:6808:2a06:b0:35e:728d:6de4 with SMTP id ez6-20020a0568082a0600b0035e728d6de4mr5253027oib.2.1674180554506; Thu, 19 Jan 2023 18:09:14 -0800 (PST) Received: from localhost.localdomain ([2804:431:e7c7:50e6:38f8:6585:d1eb:60ba]) by smtp.gmail.com with ESMTPSA id u11-20020a54438b000000b0036eb408a81fsm1085833oiv.24.2023.01.19.18.09.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 19 Jan 2023 18:09:14 -0800 (PST) From: Matheus Branco Borella To: gdb-patches@sourceware.org Cc: Matheus Branco Borella Subject: [PATCH] Add __repr__() implementation to a few Python types Date: Thu, 19 Jan 2023 22:43:37 -0300 Message-Id: <20230120014336.377-1-dark.ryu.550@gmail.com> X-Mailer: git-send-email 2.37.3.windows.1 In-Reply-To: <87wn5jen5t.fsf@redhat.com> References: <87wn5jen5t.fsf@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-11.9 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_ENVFROM_END_DIGIT,FREEMAIL_FROM,GIT_PATCH_0,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: Only a few types in the Python API currently have __repr__() implementations. This patch adds a few more of them. specifically: it adds __repr__() implementations to gdb.Symbol, gdb.Architecture, gdb.Block, gdb.Breakpoint, and gdb.Type. This makes it easier to play around the GDB Python API in the Python interpreter session invoked with the 'pi' command in GDB, giving more easily accessible tipe information to users. An example of how this would look like: ``` (gdb) pi >> gdb.lookup_type("char") >> gdb.lookup_global_symbol("main") ``` > Sorry for being a little slow. What does this actually mean? When you > say "makes use of u8 string literals" - does this mean you have string > literals in this patch containing non ASCII characters? > > I've trying to understand why this is different to any other part of GDB > that prints stuff via Python. I forgot to take that out of the commit message, my bad. Originally, I'd intended for the string literals in the patch that get handed to Python to be all u8 literals so that I could guarantee it wouldn't break in an environment that doesn't output regular string literals in an ASCII-compatible encoding, as Python expects all strings handed to it to be encoded in UTF-8. But seeing as all of the rest of the Python interface code uses regular string literals, I figured it wouldn't make much of difference having them in anyway. > I guess I was surprised that so many of the new tests included an > explicit call to repr, given the premise of the change was that simply > 'print(empty)' would now print something useful. > > I guess maybe it doesn't hurt to _also_ include some explicit repr > calls, but I was expecting most tests to just be printing the object > directly. As blarsen@ also pointed out, `print`-ing an object directly that has an implmentation of __str__() will print whatever its __str__() functions returns, regardless of whether it implements __repr__() or not, which is not what we want here. __repr__() is always preferred in the REPL, though, so it's understandable it might not be clear at first why I'm calling `repr()` explicitly. > Over long line, please wrap a little. There's other long lines in your > patch, I'll not point out each one. Should be all fixed now (hopefully I didn't miss any), with the exception of the `repr_pattern` strings in `py-breakpoint.exp`, which I couldn't for the life of me get to match properly with the output were they not on a single line. --- gdb/python/py-arch.c | 18 +++++- gdb/python/py-block.c | 27 ++++++++- gdb/python/py-breakpoint.c | 68 +++++++++++++++++++++- gdb/python/py-symbol.c | 16 ++++- gdb/python/py-type.c | 30 +++++++++- gdb/testsuite/gdb.python/py-arch.exp | 6 ++ gdb/testsuite/gdb.python/py-block.exp | 4 +- gdb/testsuite/gdb.python/py-breakpoint.exp | 24 ++++---- gdb/testsuite/gdb.python/py-symbol.exp | 2 + gdb/testsuite/gdb.python/py-type.exp | 4 ++ 10 files changed, 181 insertions(+), 18 deletions(-) diff --git a/gdb/python/py-arch.c b/gdb/python/py-arch.c index cf0978560f9..5384a0d0d0c 100644 --- a/gdb/python/py-arch.c +++ b/gdb/python/py-arch.c @@ -319,6 +319,22 @@ archpy_integer_type (PyObject *self, PyObject *args, PyObject *kw) return type_to_type_object (type); } +/* __repr__ implementation for gdb.Architecture. */ + +static PyObject * +archpy_repr (PyObject *self) +{ + const auto gdbarch = arch_object_to_gdbarch (self); + if (gdbarch == nullptr) + return PyUnicode_FromFormat + (""); + + return PyUnicode_FromFormat + ("", + gdbarch_bfd_arch_info (gdbarch)->arch_name, + gdbarch_bfd_arch_info (gdbarch)->printable_name); +} + /* Implementation of gdb.architecture_names(). Return a list of all the BFD architecture names that GDB understands. */ @@ -391,7 +407,7 @@ PyTypeObject arch_object_type = { 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ - 0, /* tp_repr */ + archpy_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ diff --git a/gdb/python/py-block.c b/gdb/python/py-block.c index b9aea3aca69..b4c55add765 100644 --- a/gdb/python/py-block.c +++ b/gdb/python/py-block.c @@ -424,6 +424,31 @@ blpy_iter_is_valid (PyObject *self, PyObject *args) Py_RETURN_TRUE; } +/* __repr__ implementation for gdb.Block. */ + +static PyObject * +blpy_repr (PyObject *self) +{ + const auto block = block_object_to_block (self); + if (block == nullptr) + return PyUnicode_FromFormat (""); + + const auto name = block->function () ? + block->function ()->print_name () : ""; + + block_iterator iter; + block_iterator_first (block, &iter); + + std::string str; + const struct symbol *symbol; + while ((symbol = block_iterator_next (&iter)) != nullptr) + str = (str + "\n") + symbol->print_name () + ","; + if(!str.empty ()) + str += "\n"; + + return PyUnicode_FromFormat ("", name, str.c_str ()); +} + int gdbpy_initialize_blocks (void) { @@ -486,7 +511,7 @@ PyTypeObject block_object_type = { 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ - 0, /*tp_repr*/ + blpy_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ &block_object_as_mapping, /*tp_as_mapping*/ diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c index de7b9f4266b..d68a205330c 100644 --- a/gdb/python/py-breakpoint.c +++ b/gdb/python/py-breakpoint.c @@ -33,6 +33,7 @@ #include "location.h" #include "py-event.h" #include "linespec.h" +#include "gdbsupport/common-utils.h" extern PyTypeObject breakpoint_location_object_type CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("breakpoint_location_object"); @@ -967,6 +968,31 @@ bppy_init (PyObject *self, PyObject *args, PyObject *kwargs) return 0; } +/* __repr__ implementation for gdb.Breakpoint. */ + +static PyObject * +bppy_repr (PyObject *self) +{ + const auto bp = (struct gdbpy_breakpoint_object*) self; + if (bp->bp == nullptr) + return PyUnicode_FromFormat (""); + + std::string str = " "; + if (bp->bp->thread != -1) + str += string_printf ("thread=%d ", bp->bp->thread); + if (bp->bp->task > 0) + str += string_printf ("task=%d ", bp->bp->task); + if (bp->bp->enable_count > 0) + str += string_printf ("enable_count=%d ", bp->bp->enable_count); + str.pop_back (); + + return PyUnicode_FromFormat + ("", + bp->bp->number, + bp->bp->hit_count, + str.c_str ()); +} + /* Append to LIST the breakpoint Python object associated to B. Return true on success. Return false on failure, with the Python error @@ -1389,7 +1415,7 @@ PyTypeObject breakpoint_object_type = 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ - 0, /*tp_repr*/ + bppy_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ @@ -1604,6 +1630,44 @@ bplocpy_dealloc (PyObject *py_self) Py_TYPE (py_self)->tp_free (py_self); } +/* __repr__ implementation for gdb.BreakpointLocation. */ + +static PyObject * +bplocpy_repr (PyObject *py_self) +{ + const auto self = (gdbpy_breakpoint_location_object *) py_self; + if (self->owner == nullptr || self->owner->bp == nullptr + || self->owner->bp != self->bp_loc->owner) + return PyUnicode_FromFormat (""); + + const auto enabled = self->bp_loc->enabled ? "enabled" : "disabled"; + + std::string str(enabled); + + str += " requested_address=0x"; + str += string_printf ("%lx", self->bp_loc->requested_address); + + str += " address=0x"; + str += string_printf ("%lx", self->bp_loc->address); + + if (self->bp_loc->symtab != nullptr) + { + str += " source="; + str += self->bp_loc->symtab->filename; + str += ":"; + str += string_printf ("%d", self->bp_loc->line_number); + } + + const auto fn_name = self->bp_loc->function_name.get (); + if (fn_name != nullptr) + { + str += " in "; + str += fn_name; + } + + return PyUnicode_FromFormat ("", str.c_str ()); +} + /* Attribute get/set Python definitions. */ static gdb_PyGetSetDef bp_location_object_getset[] = { @@ -1635,7 +1699,7 @@ PyTypeObject breakpoint_location_object_type = 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ - 0, /*tp_repr*/ + bplocpy_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ diff --git a/gdb/python/py-symbol.c b/gdb/python/py-symbol.c index 93c86964f3e..5a8149bbe66 100644 --- a/gdb/python/py-symbol.c +++ b/gdb/python/py-symbol.c @@ -375,6 +375,20 @@ sympy_dealloc (PyObject *obj) Py_TYPE (obj)->tp_free (obj); } +/* __repr__ implementation for gdb.Symbol. */ + +static PyObject * +sympy_repr (PyObject *self) +{ + const auto symbol = symbol_object_to_symbol (self); + if (symbol == nullptr) + return PyUnicode_FromFormat (""); + + return PyUnicode_FromFormat + ("", + symbol->print_name ()); +} + /* Implementation of gdb.lookup_symbol (name [, block] [, domain]) -> (symbol, is_field_of_this) A tuple with 2 elements is always returned. The first is the symbol @@ -732,7 +746,7 @@ PyTypeObject symbol_object_type = { 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ - 0, /*tp_repr*/ + sympy_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c index 928efacfe8a..eb11ef029ca 100644 --- a/gdb/python/py-type.c +++ b/gdb/python/py-type.c @@ -1026,6 +1026,34 @@ typy_template_argument (PyObject *self, PyObject *args) return value_to_value_object (val); } +/* __repr__ implementation for gdb.Type. */ + +static PyObject * +typy_repr (PyObject *self) +{ + const auto type = type_object_to_type (self); + if (type == nullptr) + return PyUnicode_FromFormat (""); + + const char *code = pyty_codes[type->code ()].name; + string_file type_name; + try + { + current_language->print_type (type, "", + &type_name, -1, 0, + &type_print_raw_options); + } + catch (const gdb_exception &except) + { + GDB_PY_HANDLE_EXCEPTION (except); + } + auto py_typename = PyUnicode_Decode + (type_name.c_str (), type_name.size (), + host_charset (), NULL); + + return PyUnicode_FromFormat ("", code, py_typename); +} + static PyObject * typy_str (PyObject *self) { @@ -1612,7 +1640,7 @@ PyTypeObject type_object_type = 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ - 0, /*tp_repr*/ + typy_repr, /*tp_repr*/ &type_object_as_number, /*tp_as_number*/ 0, /*tp_as_sequence*/ &typy_mapping, /*tp_as_mapping*/ diff --git a/gdb/testsuite/gdb.python/py-arch.exp b/gdb/testsuite/gdb.python/py-arch.exp index 1fbbc47c872..d436c957e25 100644 --- a/gdb/testsuite/gdb.python/py-arch.exp +++ b/gdb/testsuite/gdb.python/py-arch.exp @@ -29,6 +29,8 @@ if ![runto_main] { # Test python/15461. Invalid architectures should not trigger an # internal GDB assert. gdb_py_test_silent_cmd "python empty = gdb.Architecture()" "get empty arch" 0 +gdb_test "python print(repr (empty))" "" \ + "Test empty achitecture __repr__ does not trigger an assert" gdb_test "python print(empty.name())" ".*Architecture is invalid.*" \ "Test empty architecture.name does not trigger an assert" gdb_test "python print(empty.disassemble())" ".*Architecture is invalid.*" \ @@ -46,6 +48,10 @@ gdb_py_test_silent_cmd "python insn_list3 = arch.disassemble(pc, count=1)" \ gdb_py_test_silent_cmd "python insn_list4 = arch.disassemble(gdb.Value(pc))" \ "disassemble no end no count" 0 +gdb_test "python print (repr (arch))" \ + "" \ + "test __repr__ for architecture" + gdb_test "python print (len(insn_list1))" "1" "test number of instructions 1" gdb_test "python print (len(insn_list2))" "1" "test number of instructions 2" gdb_test "python print (len(insn_list3))" "1" "test number of instructions 3" diff --git a/gdb/testsuite/gdb.python/py-block.exp b/gdb/testsuite/gdb.python/py-block.exp index 0a88aec56a0..5e3d1c72d5e 100644 --- a/gdb/testsuite/gdb.python/py-block.exp +++ b/gdb/testsuite/gdb.python/py-block.exp @@ -39,7 +39,7 @@ gdb_continue_to_breakpoint "Block break here." gdb_py_test_silent_cmd "python frame = gdb.selected_frame()" "Get Frame" 0 gdb_py_test_silent_cmd "python block = frame.block()" \ "Get block, initial innermost block" 0 -gdb_test "python print (block)" "" "check block not None" +gdb_test "python print (block)" "" "check block not None" gdb_test "python print (block.function)" "None" "first anonymous block" gdb_test "python print (block.start)" "${decimal}" "check start not None" gdb_test "python print (block.end)" "${decimal}" "check end not None" @@ -73,7 +73,7 @@ gdb_test "python print (block.function)" "block_func" \ gdb_test "up" ".*" gdb_py_test_silent_cmd "python frame = gdb.selected_frame()" "Get Frame 2" 0 gdb_py_test_silent_cmd "python block = frame.block()" "Get Frame 2's block" 0 -gdb_test "python print (block)" "" \ +gdb_test "python print (repr (block))" "" \ "Check Frame 2's block not None" gdb_test "python print (block.function)" "main" "main block" diff --git a/gdb/testsuite/gdb.python/py-breakpoint.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp index e36e87dc291..4da46431a3a 100644 --- a/gdb/testsuite/gdb.python/py-breakpoint.exp +++ b/gdb/testsuite/gdb.python/py-breakpoint.exp @@ -50,11 +50,13 @@ proc_with_prefix test_bkpt_basic { } { return 0 } + set repr_pattern "" + # Now there should be one breakpoint: main. gdb_py_test_silent_cmd "python blist = gdb.breakpoints()" \ "Get Breakpoint List" 0 - gdb_test "python print (blist\[0\])" \ - "" "Check obj exists @main" + gdb_test "python print (repr (blist\[0\]))" \ + "$repr_pattern" "Check obj exists @main" gdb_test "python print (blist\[0\].location)" \ "main." "Check breakpoint location @main" gdb_test "python print (blist\[0\].pending)" "False" \ @@ -71,12 +73,12 @@ proc_with_prefix test_bkpt_basic { } { "Get Breakpoint List" 0 gdb_test "python print (len(blist))" \ "2" "Check for two breakpoints" - gdb_test "python print (blist\[0\])" \ - "" "Check obj exists @main 2" + gdb_test "python print (repr (blist\[0\]))" \ + "$repr_pattern" "Check obj exists @main 2" gdb_test "python print (blist\[0\].location)" \ "main." "Check breakpoint location @main 2" - gdb_test "python print (blist\[1\])" \ - "" "Check obj exists @mult_line" + gdb_test "python print (repr (blist\[1\]))" \ + "$repr_pattern" "Check obj exists @mult_line" gdb_test "python print (blist\[1\].location)" \ "py-breakpoint\.c:${mult_line}*" \ @@ -224,14 +226,16 @@ proc_with_prefix test_bkpt_invisible { } { return 0 } + set repr_pattern "" + delete_breakpoints set ibp_location [gdb_get_line_number "Break at multiply."] gdb_py_test_silent_cmd "python ibp = gdb.Breakpoint(\"$ibp_location\", internal=False)" \ "Set invisible breakpoint" 0 gdb_py_test_silent_cmd "python ilist = gdb.breakpoints()" \ "Get Breakpoint List" 0 - gdb_test "python print (ilist\[0\])" \ - "" "Check invisible bp obj exists 1" + gdb_test "python print (repr (ilist\[0\]))" \ + "$repr_pattern" "Check invisible bp obj exists 1" gdb_test "python print (ilist\[0\].location)" \ "py-breakpoint\.c:$ibp_location*" "Check breakpoint location 1" gdb_test "python print (ilist\[0\].visible)" \ @@ -243,8 +247,8 @@ proc_with_prefix test_bkpt_invisible { } { "Set invisible breakpoint" 0 gdb_py_test_silent_cmd "python ilist = gdb.breakpoints()" \ "Get Breakpoint List" 0 - gdb_test "python print (ilist\[0\])" \ - "" "Check invisible bp obj exists 2" + gdb_test "python print (repr (ilist\[0\]))" \ + "$repr_pattern" "Check invisible bp obj exists 2" gdb_test "python print (ilist\[0\].location)" \ "py-breakpoint\.c:$ibp_location*" "Check breakpoint location 2" gdb_test "python print (ilist\[0\].visible)" \ diff --git a/gdb/testsuite/gdb.python/py-symbol.exp b/gdb/testsuite/gdb.python/py-symbol.exp index ad06b07c2c6..979b7dfb8fb 100644 --- a/gdb/testsuite/gdb.python/py-symbol.exp +++ b/gdb/testsuite/gdb.python/py-symbol.exp @@ -44,6 +44,8 @@ clean_restart ${binfile} # point where we don't have a current frame, and we don't want to # require one. gdb_py_test_silent_cmd "python main_func = gdb.lookup_global_symbol(\"main\")" "Lookup main" 1 +gdb_test "python print (repr (main_func))" "" \ + "test main_func.__repr__" gdb_test "python print (main_func.is_function)" "True" "test main_func.is_function" gdb_test "python print (gdb.lookup_global_symbol(\"junk\"))" "None" "test lookup_global_symbol(\"junk\")" diff --git a/gdb/testsuite/gdb.python/py-type.exp b/gdb/testsuite/gdb.python/py-type.exp index 594c9749d8e..95cdfa54a6e 100644 --- a/gdb/testsuite/gdb.python/py-type.exp +++ b/gdb/testsuite/gdb.python/py-type.exp @@ -393,3 +393,7 @@ if { [build_inferior "${binfile}-cxx" "c++"] == 0 } { test_type_equality } } + +# Test __repr__() +gdb_test "python print (repr (gdb.lookup_type ('char')))" \ + "" "test __repr__()" -- 2.37.3.windows.1