From patchwork Wed Oct 18 14:57:15 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 78094 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 35F2B385841B for ; Wed, 18 Oct 2023 14:57:52 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 3BFA43858D33 for ; Wed, 18 Oct 2023 14:57:37 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 3BFA43858D33 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 3BFA43858D33 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1697641059; cv=none; b=tjpgIaZR9Gm/ewDjNq3NdjAy7vmLV4tIzWzpZgD80n4bXONBjM1PEFKUeLMQusIKEfhKbdj00Qn66XvAOiiSPrf7h/8M3nQWwFPgue4q2taaoFuwVkxoWtguqXnZitJ4h67K91XtWdRwC+wuBji5yG6vrR+ii/gprXGaWPkOxp4= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1697641059; c=relaxed/simple; bh=Z7njHRMbWmUKVgyYYE9anif6QFBwGkcCf/1jlNHXObk=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=P72/PWHDSr+yfDDSLtr0NxoIV2DREuUw55p8AXdkS6kaJYoN28d4Kj5tOkDvQt7lv5M8kkR8u5GNHo9oBc7hPrfkkUZQieg5s2pBt9LN7LGQ8GOnXj4ImAKmuCoiIL/27gRgas8QyYPSMRDa4eL3V3xs5XY/yobIL9OcDOlDFvA= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1697641056; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=y2eWL2Sihvdt1w9OXKXlXcHcwkCxEKHaBC9/C1hSaRQ=; b=hBMYULNVugxMPfMXxNdlFOexbm7U6sLnvv5eUIjP/i9kICRTQeIBZmbm2i4O2HZ2x0Nded p9plVQS/a1orEQ9cOdDZCNk9xjXFe1UrmezL9xwvNW/mfrbWx3q6itMirzUWyuWv1OVvX0 RSkDTB9i9RMfNJjSeRVb7f1h5yz5dVI= Received: from mail-yb1-f199.google.com (mail-yb1-f199.google.com [209.85.219.199]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-418-jZiCg1SvNbm73UbmORoPrA-1; Wed, 18 Oct 2023 10:57:20 -0400 X-MC-Unique: jZiCg1SvNbm73UbmORoPrA-1 Received: by mail-yb1-f199.google.com with SMTP id 3f1490d57ef6-d9b9f56489fso4991198276.1 for ; Wed, 18 Oct 2023 07:57:20 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697641040; x=1698245840; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=y2eWL2Sihvdt1w9OXKXlXcHcwkCxEKHaBC9/C1hSaRQ=; b=jgKnefPdGxAE8SdNznU7F2h+dNoUtUHdMJ60HxbP/6Xbyhuh94rnT6aQug8jN4Q5JV wjeVhT1hQ21n5Hq41q0GVpSaqZKShQ+0HPeuFQM9BDFHkveI0dn/oZHM7F33e2e+iRb4 uW4JgRjeq8HPyuJUDBFaHOomPW2yXyBcZ54AJrsP57htSFQ9YyXglfHgDM/EqxAem7gs U93lnNJko+5LMftXEKKUS4QAr7WBnPxzEQ22u6cCJ8P474EkOUqe5pi1VfSltIl6xx9r EzF0VJY/ZB4B8eJ4MNM45BiCWg9JJY4DrHno/5mtZJN4XMg2oh43b38Nm8OSsF6hI8sA ST0Q== X-Gm-Message-State: AOJu0Yx7d0ys85P0tbbCf/EVObCO483EE2Ttsfdji9eQ5+/F0Iemmdm0 M8PqZS8ZQm1KoIKZZMA65datKNnNaTNW82mlrFUqPEnUvtWWxLCJsuVzDl5z4ioBD8V9Sg+4GIj VpkGrtidkeuxFRoPVVt7zMRscXQ0oi/480cNYfB4GmBTDePABnQrfBZTG+Y+3Kp1w0n1ca1416m ggLm2YLw== X-Received: by 2002:a25:bccc:0:b0:d9b:9f55:b62d with SMTP id l12-20020a25bccc000000b00d9b9f55b62dmr5303032ybm.61.1697641039705; Wed, 18 Oct 2023 07:57:19 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFf3my6QCu2RQ8IsxFhUgQqEvRzIyy5TxVgKQwhPzYdWAyIXDOOppQr5l3fooUM/EBZZlg5Kw== X-Received: by 2002:a25:bccc:0:b0:d9b:9f55:b62d with SMTP id l12-20020a25bccc000000b00d9b9f55b62dmr5303012ybm.61.1697641039254; Wed, 18 Oct 2023 07:57:19 -0700 (PDT) Received: from localhost ([31.111.84.209]) by smtp.gmail.com with ESMTPSA id f6-20020ad45586000000b0066d20f29e5fsm12080qvx.35.2023.10.18.07.57.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 07:57:18 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH] gdb/python: Add new gdb.Value.bytes attribute Date: Wed, 18 Oct 2023 15:57:15 +0100 Message-Id: <9cefc82f3c5d6619bca1f04a783e48ea5639a841.1697641024.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.7 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, 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 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org Add a gdb.Value.bytes attribute. This attribute contains the bytes of the value (assuming the complete bytes of the value are available). If the bytes of the gdb.Value are not available then accessing this attribute raises an exception. The bytes object returned from gdb.Value.bytes is cached within GDB so that the same bytes object is returned each time. The bytes object is created on-demand though to reduce unnecessary work. For some values we can of course obtain the same information by reading inferior memory based on gdb.Value.address and gdb.Value.type.sizeof, however, not every value is in memory, so we don't always have an address. The gdb.Value.bytes attribute will convert any value to a bytes object, so long as the contents are available. The value can be one created purely in Python code, the value could be in a register, or (of course) the value could be in memory. --- gdb/NEWS | 3 ++ gdb/doc/python.texi | 7 ++++ gdb/python/py-value.c | 35 +++++++++++++++++ gdb/testsuite/gdb.python/py-value.exp | 56 ++++++++++++++++++++++++++- 4 files changed, 100 insertions(+), 1 deletion(-) base-commit: 29736fc507c7a9c6e797b7f83e8df4be73d37767 diff --git a/gdb/NEWS b/gdb/NEWS index 08d779010f0..d89df5dbb45 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -11,6 +11,9 @@ ** New function gdb.notify_mi(NAME, DATA), that emits custom GDB/MI async notification. + ** New attribute gdb.Value.bytes that contains a bytes object + holding the contents of this value. + *** Changes in GDB 14 * GDB now supports the AArch64 Scalable Matrix Extension 2 (SME2), which diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 546b4d4b962..a2db6c1b863 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -916,6 +916,13 @@ method is invoked. @end defvar +@defvar Value.bytes +The value of this read-only attribute is a @code{bytes} object +containing the bytes that make up this Value's value. If the complete +contents of this value are not available then accessing this attribute +will raise an exception. +@end defvar + The following methods are provided: @defun Value.__init__ (val) diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c index 0bf1d6e0dae..87287cd9c7f 100644 --- a/gdb/python/py-value.c +++ b/gdb/python/py-value.c @@ -63,6 +63,7 @@ struct value_object { PyObject *address; PyObject *type; PyObject *dynamic_type; + PyObject *content_bytes; }; /* List of all values which are currently exposed to Python. It is @@ -86,6 +87,7 @@ valpy_clear_value (value_object *self) Py_CLEAR (self->address); Py_CLEAR (self->type); Py_CLEAR (self->dynamic_type); + Py_CLEAR (self->content_bytes); } /* Called by the Python interpreter when deallocating a value object. */ @@ -1304,6 +1306,36 @@ valpy_get_is_lazy (PyObject *self, void *closure) Py_RETURN_FALSE; } +/* Implements gdb.Value.bytes attribute. */ +static PyObject * +valpy_get_bytes (PyObject *self, void *closure) +{ + value_object *value_obj = (value_object *) self; + struct value *value = value_obj->value; + + if (value_obj->content_bytes != nullptr) + { + Py_INCREF (value_obj->content_bytes); + return value_obj->content_bytes; + } + + gdb::array_view contents; + try + { + contents = value->contents (); + } + catch (const gdb_exception &except) + { + GDB_PY_HANDLE_EXCEPTION (except); + } + + value_obj->content_bytes + = PyBytes_FromStringAndSize ((const char *) contents.data (), + contents.size ()); + Py_XINCREF (value_obj->content_bytes); + return value_obj->content_bytes; +} + /* Implements gdb.Value.fetch_lazy (). */ static PyObject * valpy_fetch_lazy (PyObject *self, PyObject *args) @@ -1865,6 +1897,7 @@ value_to_value_object (struct value *val) val_obj->address = NULL; val_obj->type = NULL; val_obj->dynamic_type = NULL; + val_obj->content_bytes = nullptr; note_value (val_obj); } @@ -2152,6 +2185,8 @@ static gdb_PyGetSetDef value_object_getset[] = { "Boolean telling whether the value is lazy (not fetched yet\n\ from the inferior). A lazy value is fetched when needed, or when\n\ the \"fetch_lazy()\" method is called.", NULL }, + { "bytes", valpy_get_bytes, nullptr, + "Return a bytearray containing the bytes of this value.", nullptr }, {NULL} /* Sentinel */ }; diff --git a/gdb/testsuite/gdb.python/py-value.exp b/gdb/testsuite/gdb.python/py-value.exp index cdfcd414cd4..89bfbfd8153 100644 --- a/gdb/testsuite/gdb.python/py-value.exp +++ b/gdb/testsuite/gdb.python/py-value.exp @@ -66,7 +66,14 @@ proc test_value_creation {} { gdb_test "python print ('result = %s' % i.address)" "= None" "test address attribute in non-addressable value" # Test creating / printing an optimized out value - gdb_test "python print(gdb.Value(gdb.Value(5).type.optimized_out()))" + gdb_test "python print(gdb.Value(gdb.Value(5).type.optimized_out()))" \ + "" + + # Test accessing the bytes of an optimised out value. + gdb_test "python print(gdb.Value(gdb.Value(5).type.optimized_out()).bytes)" \ + [multi_line \ + "gdb\\.error: value has been optimized out" \ + "Error while executing Python code\\."] } # Check that we can call gdb.Value.__init__ to change a value. @@ -485,6 +492,34 @@ proc test_subscript_regression {exefile lang} { gdb_py_test_silent_cmd "print {\"fu \",\"foo\",\"bar\"}" "Build array" 1 gdb_py_test_silent_cmd "python marray = gdb.history(0)" "fetch marray" 0 gdb_test "python print (marray\[1\]\[2\])" "o." "test multiple subscript" + + # A Python helper function. Fetch VAR_NAME from the inferior as a + # gdb.Value. Read the bytes of the value based on its address, and + # the size of its type. The compare these bytes to the value + # obtained from gdb.Value.bytes. Assert that the two bytes objects + # match. + gdb_test_multiline "Create a function to check Value.bytes" \ + "python" "" \ + "def check_value_bytes(var_name):" "" \ + " val = gdb.parse_and_eval(var_name)" "" \ + " addr = val.address" "" \ + " len = val.type.sizeof" "" \ + " mem = gdb.selected_inferior().read_memory(addr, len)" "" \ + " mem_bytes = mem.tobytes()" "" \ + " val_bytes = val.bytes" "" \ + " assert mem_bytes == val_bytes" "" \ + "end" "" + + gdb_test_no_output { python check_value_bytes("a") } + gdb_test_no_output { python check_value_bytes("p") } + gdb_test_no_output { python check_value_bytes("i") } + gdb_test_no_output { python check_value_bytes("ptr_i") } + gdb_test_no_output { python check_value_bytes("embed") } + gdb_test_no_output { python check_value_bytes("fp1") } + gdb_test_no_output { python check_value_bytes("nullst") } + gdb_test_no_output { python check_value_bytes("st") } + gdb_test_no_output { python check_value_bytes("s") } + gdb_test_no_output { python check_value_bytes("u") } } # A few tests of gdb.parse_and_eval. @@ -542,13 +577,30 @@ proc prepare_type_and_buffer {} { proc test_value_from_buffer {} { global gdb_prompt + # A Python helper function. Create a bytes object from inferior + # memory LEN bytes starting at ADDR, and compare this to the bytes + # obtained from VAL.bytes. Assert that the two bytes object match. + gdb_test_multiline "Create another function to check Value.bytes" \ + "python" "" \ + "def compare_value_bytes_to_mem(val, addr, len):" "" \ + " mem = gdb.selected_inferior().read_memory(addr, len)" "" \ + " mem_bytes = mem.tobytes()" "" \ + " val_bytes = val.bytes" "" \ + " assert mem_bytes == val_bytes" "" \ + "end" "" + prepare_type_and_buffer gdb_test "python v=gdb.Value(b,tp); print(v)" "1" \ "construct value from buffer" + gdb_test_no_output { python compare_value_bytes_to_mem(v, addr, size_a0) } gdb_test "python v=gdb.Value(b\[size_a0:\],tp); print(v)" "2" \ "convert 2nd elem of buffer to value" + gdb_test_no_output \ + { python compare_value_bytes_to_mem(v, (int(addr) + size_a0), size_a0) } gdb_test "python v=gdb.Value(b\[2*size_a0:\],tp); print(v)" "3" \ "convert 3rd elem of buffer to value" + gdb_test_no_output \ + { python compare_value_bytes_to_mem(v, (int(addr) + (2 * size_a0)), size_a0) } gdb_test "python v=gdb.Value(b\[2*size_a0+1:\],tp); print(v)" \ "ValueError: Size of type is larger than that of buffer object\..*" \ "attempt to convert smaller buffer than size of type" @@ -556,6 +608,8 @@ proc test_value_from_buffer {} { "make array type" 0 gdb_py_test_silent_cmd "python va=gdb.Value(b,atp)" \ "construct array value from buffer" 0 + gdb_test_no_output \ + { python compare_value_bytes_to_mem(va, addr, size_a0 * 3) } gdb_test "python print(va)" "\\{1, 2, 3\\}" "print array value" gdb_test "python print(va\[0\])" "1" "print first array element" gdb_test "python print(va\[1\])" "2" "print second array element"