From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id KPQCJ819WWgt1hoAWB0awg (envelope-from ) for ; Mon, 23 Jun 2025 12:16:13 -0400 Received: by simark.ca (Postfix, from userid 112) id 9ADE31E11C; Mon, 23 Jun 2025 12:16:13 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-9.0 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED, RCVD_IN_VALIDITY_RPBL,RCVD_IN_VALIDITY_SAFE autolearn=ham autolearn_force=no version=4.0.1 Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id B863E1E0C2 for ; Mon, 23 Jun 2025 12:16:12 -0400 (EDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 611AC3846711 for ; Mon, 23 Jun 2025 16:16:12 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 611AC3846711 Received: from us-smtp-delivery-114.mimecast.com (us-smtp-delivery-114.mimecast.com [170.10.129.114]) by sourceware.org (Postfix) with ESMTP id B03823846718 for ; Mon, 23 Jun 2025 16:11:23 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org B03823846718 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=labware.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=labware.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org B03823846718 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.114 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1750695083; cv=none; b=Gz4NKcCzrpfMoiUttO6CWOsvlwV+BVlBtvJcXaSCRGYRnpAkkI4NMdTC8lG1fqpIktH+LGI1MDShPGJYdLElYq8KZ0NIuO2rQ6XWwbFx9yUd2QOF4Vm6najzIRRExGBrV3UafJZlrRw24iLAzD3EP4FslR4Byyov9mk8owTQjLI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1750695083; c=relaxed/simple; bh=B/M7cKvjo0BptrFOTlgEAQZF/BmBqASiyOA67IWJ8Oo=; h=From:To:Subject:Date:Message-ID:MIME-Version; b=eGcmCiMgINyeVRosmg3VTEkQj1F3aDWGrESWEs/j5d2j6LF+8mlWyfARLVoHsKqpIAr2yds0gHkdDxkVBjCcJDkzPlV3fIbd6liSNToT/G6ilaLvkLwg4WJROOdTzOpkKOC5KFyXG/tAZSqEGghSDTI2MxH4meSiO120HCl2Yg0= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B03823846718 Received: from NAM12-MW2-obe.outbound.protection.outlook.com (mail-mw2nam12on2136.outbound.protection.outlook.com [40.107.244.136]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-353-wH00uMQnOBSWzX7-Z80U_A-1; Mon, 23 Jun 2025 12:11:21 -0400 X-MC-Unique: wH00uMQnOBSWzX7-Z80U_A-1 X-Mimecast-MFC-AGG-ID: wH00uMQnOBSWzX7-Z80U_A_1750695080 Received: from SA1PR17MB5365.namprd17.prod.outlook.com (2603:10b6:806:1d8::11) by PH0PR17MB4637.namprd17.prod.outlook.com (2603:10b6:510:81::10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8857.29; Mon, 23 Jun 2025 16:11:17 +0000 Received: from SA1PR17MB5365.namprd17.prod.outlook.com ([fe80::6398:7dc0:7d88:5a4d]) by SA1PR17MB5365.namprd17.prod.outlook.com ([fe80::6398:7dc0:7d88:5a4d%5]) with mapi id 15.20.8857.026; Mon, 23 Jun 2025 16:11:17 +0000 From: Jan Vrany To: gdb-patches@sourceware.org CC: Jan Vrany , Eli Zaretskii Subject: [RFC v5 12/18] gdb/python: allow instantiation of gdb.Block from Python Date: Mon, 23 Jun 2025 17:10:07 +0100 Message-ID: <20250623161013.650814-13-jan.vrany@labware.com> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250623161013.650814-1-jan.vrany@labware.com> References: <20250623161013.650814-1-jan.vrany@labware.com> X-ClientProxiedBy: LO2P123CA0067.GBRP123.PROD.OUTLOOK.COM (2603:10a6:600:1::31) To SA1PR17MB5365.namprd17.prod.outlook.com (2603:10b6:806:1d8::11) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: SA1PR17MB5365:EE_|PH0PR17MB4637:EE_ X-MS-Office365-Filtering-Correlation-Id: 05520fa3-5fec-42b1-c744-08ddb27095e9 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|376014|1800799024 X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?bh3Qqxo/tcWRUtFNgKXB/0qkKY8o/EYy8jV2SZaboFV+39Ti4Zc8dYPUZofP?= =?us-ascii?Q?cMGdoQ762U9UTQsrglvdX5BfK8Pdsigok/383RxkRT9YJvKMuoy6WkOcEtwG?= =?us-ascii?Q?aNHNe7IY+XaghWf3uMHGsoxgjOU2tfw4HzfwdJbEJ6bgryUZT1bRNDqFVjUv?= =?us-ascii?Q?V/FYO+AGXYDbsQzXo5e8IlMZg61mr0riYElByInwChDjOmJxkIEEt5SLUyx2?= =?us-ascii?Q?YqQenxoh2LA3ANVktvstnOv/X/W6/COXymhueaw5646dabnGH7y8/KMivU8J?= =?us-ascii?Q?FgJW/OJF7yKXC7xuic1GDC8oNSgyCMVh2HhB33wtjaaVneIrGcXLdX8dTaXd?= =?us-ascii?Q?KyH1ir+WdPc2ZSrVTm5161SPkUo2VQmculzlFPk9bZX8DTfkvUxgZyWdyH75?= =?us-ascii?Q?Cr27IsTCgP/XeoU6zVFpxc6xyzGmHe7C4dNVxttt0uEBZ7JJ7yTn0TUa425b?= =?us-ascii?Q?LEZ4LRdanib/NkPGgRBzqIuGXaTSj1RdQ/H3Ttsw9MM1ovwBS7exxPL8QM1G?= =?us-ascii?Q?XXQ2T4hkE+K5dq7oaPWMy6xXcmrd0E2OESi9WY3nfpGAhl0eVc4Qpt/noSeE?= =?us-ascii?Q?o9h/g3Tg+JFiUt4qSmjLtoFfavMy58JwHOEQzPpcEOBoJPrabCGwYtgsNn4H?= =?us-ascii?Q?I46kGsQ8tsOVxp3lGSdAVbexQd02ZneJLcYXlO2h3an86dhACq8jD0h69ogc?= =?us-ascii?Q?yUwQLrmaGxnjLV9B6tfhSM7DnNC/kqhXJsdobQkn9u5GOcmJj+jKeUu9xtfL?= =?us-ascii?Q?ZdJk8DZV8eRxBOR3dF51bouksYWZ2LyybtSXZioUybNClsqwmlRpHDYP3Qfw?= =?us-ascii?Q?dNgSxCdzfZTSh5cn+WNIh77CkTQjfW66brkKEe1YC5UZs5WxGwPVpVfNc6Wm?= =?us-ascii?Q?8MnObZ9CBj6oOhFmnC6gJFKsfUxiR0N/Pu97ytsvLQOVZ5UwJT4mFVw+OEEQ?= =?us-ascii?Q?SDcIo4rYhxy+LXFpUWsVaIuehD4oM5OPIzHJsUTnaWsAq6q0Hocw7iKI4KCj?= =?us-ascii?Q?TX+jLBzod25nuoaJlKbMCjmrr4XSuvKz1VyGvredOe1MPCnPW6+ng/BGA8+W?= =?us-ascii?Q?QwFZlulhz0i/O1MtNLF5fMtlB50aHi4ClD6GWLyj5z2wReu+gYkX8u9JjNub?= =?us-ascii?Q?2Cl9O/nOD3y2eJ8mbopwglSHvjuCn6FIe474X35nx9fvn/l5nxWS9/EtRTIz?= =?us-ascii?Q?D7h8gem6sIQrSyxWF2k+/wTooKJcLRcRFyDNyQ+ScDv0wooz6JKeNL6ea/tf?= =?us-ascii?Q?N6dN2S8U4p9fSnpDY/1BEdDDXTplhQWydiFh/qK7ENQiZrBW0Pwc+f26JW2Y?= =?us-ascii?Q?v9JSdsayAXz/+fzRkmCPoP+RHS8WGiKEYQhFNVRR+qH0/fd5BADD2jPiMEHr?= =?us-ascii?Q?0OylcYpSfk0NnarRfBAaDjsBZh6CnTnAXdNn6B7Alrh14BIUtcw8Z5CIoXbG?= =?us-ascii?Q?Jauj3w8GUKE=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:SA1PR17MB5365.namprd17.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230040)(366016)(376014)(1800799024); DIR:OUT; SFP:1102 X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?m3hHFh/VIilT7vBagO8wmGLrmYYOTypED6oKpa2VR7kQ3/fkB64gZ2haIfDA?= =?us-ascii?Q?vbX72LkMMgT/WhPpEhFZYq+lXf8TapSpiAZ2lPnhzqkf+QQYmoKkosEggd2k?= =?us-ascii?Q?xblfW56pbvcR/VisvG5eM0RYnwj0k5tGG+5NpDSMPKn59Q7QWhGi9VVIW5VK?= =?us-ascii?Q?4ErpovG2RKvCVGHsodtLE2Gah6bsErhYVFIguZwFgudcVkFJaqu45chpIdpE?= =?us-ascii?Q?wNdypfe1Gj8qAC3wR7can1cjd1CiHcft7AfwzbsEfMQyc2/fmkDYWt7UUp8R?= =?us-ascii?Q?TYeaUafz7fxbSz5uR9OLj/nc/xW6H5vktjDAEq0UXoG0i4Ae7vKKJlx1qR7k?= =?us-ascii?Q?3oTImTHVUOC1UXW/f8VlBw3madBPacMAzA0Yl6iWPWTTuJi6ZYvSycfI1Ijv?= =?us-ascii?Q?P+7t68nBV9jZadSOhdZRIhVett/CCgQgfhsHYvgTkwOoY2fclowM69sOxlcO?= =?us-ascii?Q?qJK+gkyranwb2pHt2ry1OzKcfzSJE87tEdLSHIDTgFAN2NQW1elJbi5NQuP8?= =?us-ascii?Q?yf5RsjLGOYCMfNiocCQTO3NE6qtmm18A+Nsj2Da+DqofxFjmnc3OSna0J6e5?= =?us-ascii?Q?7SnBB9WtmV/C7R258OX7Avm6LoLkwc3aFbpOFPkvyG3Aug5ktVxdw5VDTSux?= =?us-ascii?Q?prZ0ficPdMSsCZsanK//2DrOpR9BWot0a6upqvbDPmgemI3b1f73BzsNshq2?= =?us-ascii?Q?HMtYVdABgntLrDo3L7E/2v67QR7atYXI5heRmtAaTjIAD//7viZ+uAkBgDYD?= =?us-ascii?Q?f1gshp/7uV5M8hN3ET3g7fxDsoFA2kXDnBwCxw8z9vDOd3w22pXvb2LoTLp9?= =?us-ascii?Q?m6svNP6uEjOkU7S8Rk7gc+x2JFoF/ISay3s4rGBn5Bzs0GlZAPYsHhSEPvHZ?= =?us-ascii?Q?NkxDDtTaHDS9gD+rklTBsfFFKtr4HzqAwIhI/OOj+8358OU2ZzC6183jpJ47?= =?us-ascii?Q?Pw5qxYybv0o60Se74CZYXH/ys7t01giozoasJmgA8czzZH2ExZQOpvq0CRZP?= =?us-ascii?Q?PhYVq/vWz6lC5y937/IeExFEpLZyEh73zJuq575SAthyFn1ARjzpxOC5/Oz5?= =?us-ascii?Q?Uh1eqk8N55HDwKfBGr5uveDV34Hx5H9l/j0kIDoHNNQHLG50e6GvzJTH/ada?= =?us-ascii?Q?tpxRSlZGsqTJSdHLUZnXDZh5CdUN2zoqd59p8fUON2txNOIsxrMZ1AGJQT6C?= =?us-ascii?Q?IkLiNgRPRGuw4makqxdyDjNEta+++eBtjVgzuMEYPRlC5lTH96RJQxbobC02?= =?us-ascii?Q?crFYgBzbiP8dJd/Uzto+UxZaodEakNzEd6tQBK7tPmQIw3rUZZ43nB71hmiU?= =?us-ascii?Q?kJgyhy+NmBdOd3KZowSXs5P0RaPnczGtZ1VjjvByobmWsruu9THmh1NK9Z+q?= =?us-ascii?Q?tvGyrKTN0qRh44h+ewrwtvbiiHx1I3bvOsCTW85xsEFw/lDGIYkTynHLziUh?= =?us-ascii?Q?qpkpXSkeHL03khxXlDbXEW2FyeUrgZ4FrElmER7qxohVIaIohyeOym4KC7lU?= =?us-ascii?Q?uVLUeEfKC5eDDMaMMkS9JI9dIY8qn6jAO+6ZYqcCXku3+W1FEJMFYf8WlYWN?= =?us-ascii?Q?1LHYNIPXbhqCC7X5z5rAwSy8nhbWCJE2EvCFLzNU?= X-OriginatorOrg: labware.com X-MS-Exchange-CrossTenant-Network-Message-Id: 05520fa3-5fec-42b1-c744-08ddb27095e9 X-MS-Exchange-CrossTenant-AuthSource: SA1PR17MB5365.namprd17.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 23 Jun 2025 16:11:17.8477 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: b5db0322-1aa0-4c0a-859c-ad0f96966f4c X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: Ewja6Q63WH58uvjNR0zz4TbNtDahUselNeckPd73m5m8I7n6KMkgCpMnubPwGrpaZQGKMVHmgrc5A5DVxms+gw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH0PR17MB4637 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: 4SSF16Jn9_cLidrbHABoS0sDz20IDivrbv8PPD0Nvag_1750695080 X-Mimecast-Originator: labware.com Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=WINDOWS-1252 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~public-inbox=simark.ca@sourceware.org This commit adds code to allow user extension to instantiate gdb.Block. This is a step towards a Python support for dynamically generated code (JIT) in GDB. Reviewed-By: Eli Zaretskii --- gdb/doc/python.texi | 6 ++ gdb/python/py-block.c | 111 +++++++++++++++++++++++++- gdb/testsuite/gdb.python/py-block.exp | 23 ++++++ 3 files changed, 138 insertions(+), 2 deletions(-) diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 65e243074ff..7f50615d151 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -6174,6 +6174,12 @@ historical compatibility. =20 A @code{gdb.Block} object has the following methods: =20 +@defun Block.__init__ (superblock, start, end) +Create new block in @var{superblock} spanning from @var{start} to @var{end= }. +The new block's @var{start}--@var{end} range must be within superblock's +range and must not overlap with any block already contained in superblock. +@end defun + @defun Block.is_valid () Returns @code{True} if the @code{gdb.Block} object is valid, @code{False} if not. A block object can become invalid if the block it diff --git a/gdb/python/py-block.c b/gdb/python/py-block.c index 89315ebf866..43b15b6013e 100644 --- a/gdb/python/py-block.c +++ b/gdb/python/py-block.c @@ -344,6 +344,106 @@ blpy_dealloc (PyObject *obj) Py_TYPE (obj)->tp_free (obj); } =20 +/* Object initializer; creates new block. + + Use: __init__(SUPERBLOCK, START, END). */ + +static int +blpy_init (PyObject *zelf, PyObject *args, PyObject *kw) +{ + struct block_object *self =3D (struct block_object*) zelf; + + if (self->block) + { + PyErr_Format (PyExc_RuntimeError, +=09=09 _("Block object already initialized.")); + return -1; + } + + static const char *keywords[] =3D { "superblock", "start", "end", NULL }= ; + PyObject *superblock_obj; + uint64_t start; + uint64_t end; + + if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "OKK", keywords, +=09=09=09=09=09 &superblock_obj, &start, &end)) + return -1; + + + auto superblock =3D block_object_to_block (superblock_obj); + if (superblock =3D=3D nullptr) + { + PyErr_Format (PyExc_TypeError, +=09=09 _("The superblock argument is not valid gdb.Block " +=09=09 "object")); + return -1; + } + + /* Check that start-end range is valid. */ + if (! (start <=3D end)) + { + PyErr_Format (PyExc_ValueError, +=09=09 _("The start argument must be less or equal to the end " +=09=09 "argument")); + return -1; + + } + + /* Check that start-end range is within superblocks' range. */ + if (! (superblock-> start() <=3D start && end <=3D superblock->end ())) + { + PyErr_Format (PyExc_ValueError, +=09=09 _("The start-end range must be within superblocks' " +=09=09 "range")); + return -1; + } + + /* Check that start-end range does not overlap with any + "sibling" blocks' range. */ + auto cu =3D superblock->global_block ()->compunit (); + + for (auto each : cu->blockvector ()->blocks ()) + { + if (each->superblock () =3D=3D superblock) +=09{ +=09 /* each is a "sibling" block. */ +=09 if (each->overlaps (start, end)) +=09 { +=09 PyErr_Format (PyExc_ValueError, +=09=09 _("The start-end range overlaps with one of the " +=09=09 "sibling blocks")); +=09 return -1; +=09 } +=09} + } + + auto obstack =3D &(cu->objfile ()->objfile_obstack); + auto blk =3D new (obstack) block (); + + blk->set_superblock (superblock); + blk->set_multidict (mdict_create_linear (obstack, NULL)); + blk->set_start ((CORE_ADDR) start); + blk->set_end ((CORE_ADDR) end); + + cu->blockvector ()->add_block (blk); + + self->block =3D blk; + self->objfile =3D cu->objfile (); + + htab_t table =3D blpy_objfile_data_key.get (self->objfile); + if (table =3D=3D nullptr) + { + table =3D htab_create_alloc (10, block_object_hash, block_object_eq, +=09=09=09=09 block_object_del, xcalloc, xfree); + blpy_objfile_data_key.set (self->objfile, table); + } + hashval_t hash =3D htab_hash_pointer (blk); + void **slot =3D htab_find_slot_with_hash (table, blk, hash, INSERT); + *slot =3D self; + + return 0; +} + /* Create a new block object (gdb.Block) that encapsulates the struct block object from GDB. */ PyObject * @@ -535,7 +635,6 @@ blpy_richcompare (PyObject *self, PyObject *other, int = op) static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION gdbpy_initialize_blocks (void) { - block_object_type.tp_new =3D PyType_GenericNew; if (gdbpy_type_ready (&block_object_type) < 0) return -1; =20 @@ -615,7 +714,15 @@ PyTypeObject block_object_type =3D { 0,=09=09=09=09 /* tp_iternext */ block_object_methods,=09=09 /* tp_methods */ 0,=09=09=09=09 /* tp_members */ - block_object_getset=09=09 /* tp_getset */ + block_object_getset,=09=09 /* tp_getset */ + 0,=09=09=09=09 /* tp_base */ + 0,=09=09=09=09 /* tp_dict */ + 0,=09=09=09=09 /* tp_descr_get */ + 0,=09=09=09=09 /* tp_descr_set */ + 0, /* tp_dictoffset */ + blpy_init, /* tp_init */ + 0,=09=09=09=09 /* tp_alloc */ + PyType_GenericNew=09=09 /* tp_new */ }; =20 static PyMethodDef block_iterator_object_methods[] =3D { diff --git a/gdb/testsuite/gdb.python/py-block.exp b/gdb/testsuite/gdb.pyth= on/py-block.exp index 11c2de8fc6f..afa306be00f 100644 --- a/gdb/testsuite/gdb.python/py-block.exp +++ b/gdb/testsuite/gdb.python/py-block.exp @@ -115,6 +115,29 @@ gdb_test "python print (repr (block))" "" \ "Check Frame 2's block not None" gdb_test "python print (block.function)" "main" "main block" =20 +# Test creation of blocks. For that we create a new compunit to make sure +# there's space for new blocks to fit in. +gdb_py_test_silent_cmd "python cu =3D gdb.Compunit(\"dynamic\", gdb.curren= t_progspace().objfiles()\[0\], 100, 200)" \ +=09=09 "Create new compunit" 1 +gdb_test "python print ( gdb.Block(cu.static_block(), 100, 150))" \ +=09 " \{.*\}>" \ +=09 "Create new block" +gdb_test "python print ( gdb.Block(\"xxx\", 160, 170))" \ +=09 "TypeError.*:.*" \ +=09 "Try create new block with non-block superblock" +gdb_test "python print ( gdb.Block(cu.static_block(), 170, 160))" \ +=09 "ValueError.*:.*" \ +=09 "Try create new block with start > end" +gdb_test "python print ( gdb.Block(cu.static_block(), 70, 160))" \ +=09 "ValueError.*:.*" \ +=09 "Try create new block with outside superblock" +gdb_test "python print ( gdb.Block(cu.static_block(), 140, 160))" \ +=09 "ValueError.*:.*" \ +=09 "Try create new block overlaping with sibling" +gdb_test "python print ( gdb.Block(cu.static_block(), 160, 170))" \ +=09 " \{.*\}>" \ +=09 "Create sibling block" + # Test Block is_valid. This must always be the last test in this # testcase as it unloads the object file. delete_breakpoints --=20 2.47.2