From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from NAM10-DM6-obe.outbound.protection.outlook.com (mail-dm6nam10on2045.outbound.protection.outlook.com [40.107.93.45]) by sourceware.org (Postfix) with ESMTPS id A4D64385801E for ; Mon, 17 Jan 2022 12:44:49 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org A4D64385801E ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=PecSvxYVVzQmsl3PeJrS5sfi/XaT6DkjRsPNjjHuXDRrj6Rf4ox4H+jV+JB8uza1yovZvMzT0RvnUljJPh2E+6ox0IDod9C9fYm5+T6ZPIGGoVC3b7uzIdlSwoOthkwUkh702STTyfFH55GjkS0FJD8OtRwu/WYl/hlHBIS6sALliqINkXtsjLJWpTwVV62vBMQbVJn7uSEd8lOSTpuchSE2Cxjj7rQbpRWUicAev6hqUo/y0u0/qD72OXFSrbNpf1soClL80D1nhBpaGe8XEh+ethy4WWeZJHPTJzxYGuiBfRkCHAdUPfbneWPvrYPkbVqdpjHLbxtW/051KA70fQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=6YfNJqd/Cex32OxoH/gPSMTUbaE/eCdGuxDeFg004rY=; b=R1OqVnIUakcBvrzLetI/mjutlqI7NU5lkdcGab4W9K7pfFhOcnN8kkEhGf33PEGdAXVf0PtauhF6Z3xtckf3WgvslPjszWlhZxRbeZkksF1B6AY1UKx0cOXY9IZFc8BmPzlQ74OvC6LqBPiv3KEyxP7bOoTrk7SXEMiEXrF4xjx1hTxtrI773CNXgCJRujMN8L5Vtn97nQ0wzEigruHVtupz3nP5makFOE0odm/gi6WaHw7VniCj+Ng538qVQL6JXjQLJfGezavmvDfGB/CC1hReswBedkgRSz6RnwCW7I7M4VQXy2vDJs3AucVWfV8s+YpB5SXAWeviZvOdKikEMA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=labware.com; dmarc=pass action=none header.from=labware.com; dkim=pass header.d=labware.com; arc=none Received: from DM6PR17MB3113.namprd17.prod.outlook.com (2603:10b6:5:6::10) by DM5PR17MB1484.namprd17.prod.outlook.com (2603:10b6:3:145::13) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4888.12; Mon, 17 Jan 2022 12:44:48 +0000 Received: from DM6PR17MB3113.namprd17.prod.outlook.com ([fe80::7839:31f:2416:6ba5]) by DM6PR17MB3113.namprd17.prod.outlook.com ([fe80::7839:31f:2416:6ba5%7]) with mapi id 15.20.4888.014; Mon, 17 Jan 2022 12:44:48 +0000 From: Jan Vrany To: gdb-patches@sourceware.org Cc: Jan Vrany Subject: [PATCH 2/5] gdb/python: create GDB/MI commands using python. Date: Mon, 17 Jan 2022 12:44:22 +0000 Message-Id: <20220117124425.2658516-3-jan.vrany@labware.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20220117124425.2658516-1-jan.vrany@labware.com> References: <20220117124425.2658516-1-jan.vrany@labware.com> Content-Transfer-Encoding: 8bit Content-Type: text/plain X-ClientProxiedBy: LO4P123CA0043.GBRP123.PROD.OUTLOOK.COM (2603:10a6:600:152::12) To DM6PR17MB3113.namprd17.prod.outlook.com (2603:10b6:5:6::10) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: ee912ad4-3fc1-4313-27dc-08d9d9b72585 X-MS-TrafficTypeDiagnostic: DM5PR17MB1484:EE_ X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:2512; X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 2Yp8zTuGzBVhbZpzdvpK/1Ob37Isr7GUOJjRAZ9XXz3T5KdtWdCpYHBpk1eyiNT1I1OD0/kOUGy64GzWq/13+yRclKMzc/aFX9VZXeaOJrJwwDynD4gjQaDh4VDb+5uueB1fqA4Xq6iaFTEcNv3gUOTTqiCLM/hAg9BFaB5U7se0N/thRtTbzbOw0WAm3IK5ht+4Y2/l+Z3W5eeItPIjGoji+MYXry7AjZT2H1Pw9Noiz8abbChKg7ecJHyG1PM++PsD9vris+iUf95dBTnmt+58eEsHrsWyrQOI0fdBYn1Rn6GvyWVPiAvG7OxeTfovBhAosTrl4hTvUWHN8UU3UFnC6QuhvKocYcWj2TAl5K1tRxwIGDqlK9NZK5iNRA473gs6D1WgdWsHpFuHjawEHCX2I5uk0rQVTF0FxnoaaW6ZmD5Yof2WCHNLlCt9f19xeEEDdProgvMlHdUJPsarsxtVhvMxGG1G0LrEWQE/+1itfg9egdqQG/doDF143gO1D6YL/+KlKgyhHU0t6g0Rd5NHIRiXcY5LYVBL5yfcG/LKTD0gTyr+yXPnbijgMt3860ICknmDGOoI/rUjszET3vR1eoSo7Kbj7N5MOO+gHVYhyyXQrfaCryeRTDGIXMWWPmeFiFzOhdAVeSLDin7ZxZvQ8u52GjP6VXQf68xEhKOcRQ02s/ZxoIEh3Dj23yfREU+iGy46X+EfLHK7/mVDF/69QmB1JoZMKT1Lh7xNcp0QbisGe2yKS1CNkb2DnSFc9B8dghoqYsCXcZkqwe+hd1wqHyPrgRoVnD3mo3KOjp8qe31D6yKxvvauAhWDuhaG X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:DM6PR17MB3113.namprd17.prod.outlook.com; PTR:; CAT:NONE; SFS:(4636009)(366004)(4326008)(26005)(6916009)(1076003)(2906002)(316002)(6506007)(30864003)(36756003)(66946007)(6666004)(66476007)(66556008)(38100700002)(5660300002)(38350700002)(508600001)(186003)(107886003)(52116002)(86362001)(83380400001)(44832011)(6512007)(6486002)(8676002)(8936002)(2616005)(2004002); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?QSlwaq7VyxKK0KUxuipLbssIHoRIMPAvF+tOGsrZ35MNMBOEW0UL49ZBfwkw?= =?us-ascii?Q?RqdiQ5J4BESfeurZSvU9J8BAi4WHJm7CHPVwgE65L1rD3ONnS0AINJqSQrLp?= =?us-ascii?Q?gX6mpQQFlCK1rJf1OT6YeOmK7OvocR1wTqrAR6y2mD4GzgNzQoPVeqT+tbSB?= =?us-ascii?Q?tRB6L6jC9Irk+wH4L20wSKThYKqWGraQscwDw3PM9DY0DQ9UY6rAue/su/18?= =?us-ascii?Q?96O15IjtZg4rMEUXJs0BtAimGywp8+esT+zN+sy/1pxbxhm1mCYCI3sEisz/?= =?us-ascii?Q?moJS0u0XJ4yjiBxja0RqgdHlxSnLTRjBjj5j5k7KEfVKMyKxSlOqqkTdLhp3?= =?us-ascii?Q?aHA9ABpjrx8k9n55VSknnZsUk03oDJHQSK37tQ3RilPMuMS7EzsNoGG7YUlk?= =?us-ascii?Q?PXYH2q2F1qAqJRiafhpgtnekMz+q2yrYadXI+gHKFt/t6VwWmeFYFy1qbOen?= =?us-ascii?Q?qnHfzOlVVvplviHUFBr4HsF5U/zvExeAzsksbEL1WmcYV3g5H/90iAidtzdb?= =?us-ascii?Q?vFbKfXZy3tjH/ysRX1qcnf+D+SpSPCG5fLOhaK41rdybBnDBTXiz8WF2K4Ba?= =?us-ascii?Q?AteDRHMbCVY9f0BWUDbCBRWQgHuN0R4bos7OmLbVyEHZkc8IIIbWjIRghPym?= =?us-ascii?Q?bj/j2toyrobK+1V+TlMmhLduWsRJuecOpVd6FJfBCP7iy2u7LoPs4ODXgLnR?= =?us-ascii?Q?fkOESRpPjug7TkPki7r+Y4w7Ui9hv5q7UR1aU5+dXCwkYpCGOwJPGq/iukAd?= =?us-ascii?Q?xulOH0IdOXHs8eAdyBNbNLfmD63eB4rh/2WBhJFPVGnttEH0ZyYCn+cIYo2J?= =?us-ascii?Q?TEPckIzDMx14ospb40yWrX4n9lO0Tcznt9GpzgRvLbyl1uy5NSZ2+JpPmDDm?= =?us-ascii?Q?EWfjrEEfbrUTCPmw0IW1znJZaa66nKxxbYbjHqTLYnrHDNWEB34chir4SDm/?= =?us-ascii?Q?xh5PPLJ5ShfBotMHymZSfi17jDbuspNBA+BGuwbu976/He/zNyLj7nFR0fjb?= =?us-ascii?Q?CNjZfY50stK0htJgGfJDwM9zFH93wgHxXGC2akR+b5Hk9UUwKEJfs+eLPk17?= =?us-ascii?Q?J8a7AxGA6LFk3r9R4QVSpzZY2ZPxoPbubs6aeRk5qcXuLM7PysT4Oh3etVTe?= =?us-ascii?Q?GeyaMfWBdbZ1ca41CcJqFRyhdpIfBeobUhUxoO9GBdfWdbPmhouBqKiCjcNG?= =?us-ascii?Q?Vgcfzp0SVPh4rbne3wP/DIYdeNv2CLJPS8plsoxA1P4P8VpMMomXOl6ApuAK?= =?us-ascii?Q?ZWJDCez8JsGwX3RMRDYNQ47fLXgXQCxdMV2zC9MwAJ+KQb9STNLKQexlZMmR?= =?us-ascii?Q?rw4pacABJyrVNA7nzb7oMVJKaaCs2N1eye3ZeBesvnd67KfWubW6VPusQyHS?= =?us-ascii?Q?PvtNf2MeLQZkv7ugV+khq8WcuvwjfCxvrQ5EqSsb5mvUegj5IO9ewnU5+thX?= =?us-ascii?Q?SroM1jOMIrS89r7msILN/bBBEEJdagvE8hHGGQtWIrgCHXjMPCcSc688cvhE?= =?us-ascii?Q?Z4Hz3RoWv9mPOp2WdkkQVvOOuaZf6IAd0aua7UbT8rY+fiswVWtQyUVtZg5s?= =?us-ascii?Q?GI+KK8KBDnMOmB2d/9yaZQXy17KSAdffn3QtxECsyE5BiEWUq4RIusuhlh9L?= =?us-ascii?Q?1LnaL1rqAPCz6T21E8QSU/c=3D?= X-OriginatorOrg: labware.com X-MS-Exchange-CrossTenant-Network-Message-Id: ee912ad4-3fc1-4313-27dc-08d9d9b72585 X-MS-Exchange-CrossTenant-AuthSource: DM6PR17MB3113.namprd17.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 17 Jan 2022 12:44:48.2924 (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: bzNhYHLHT1q8eNKjhpwHApydJvhyFqJ3uavk0dWKZxqSl8Y/JTOlKPfSWeMHDzKO2BfAUEtbNOwOnX2VikHPMQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM5PR17MB1484 X-Spam-Status: No, score=-11.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 17 Jan 2022 12:44:52 -0000 This commit allows an user to create custom MI commands using Python similarly to what is possible for Python CLI commands. A new subclass of mi_command is defined for Python MI commands, mi_command_py. A new file, py-micmd.c contains the logic for Python MI commands. --- gdb/Makefile.in | 1 + gdb/mi/mi-cmds.c | 13 +- gdb/mi/mi-cmds.h | 14 ++ gdb/python/py-micmd.c | 300 +++++++++++++++++++++++++++++++++++ gdb/python/py-micmd.h | 63 ++++++++ gdb/python/python-internal.h | 2 + gdb/python/python.c | 13 +- 7 files changed, 396 insertions(+), 10 deletions(-) create mode 100644 gdb/python/py-micmd.c create mode 100644 gdb/python/py-micmd.h diff --git a/gdb/Makefile.in b/gdb/Makefile.in index d0db5fbdee1..5cb428459c3 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -409,6 +409,7 @@ SUBDIR_PYTHON_SRCS = \ python/py-lazy-string.c \ python/py-linetable.c \ python/py-membuf.c \ + python/py-micmd.c \ python/py-newobjfileevent.c \ python/py-objfile.c \ python/py-param.c \ diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c index 57fe32c1cc6..c82bc4810df 100644 --- a/gdb/mi/mi-cmds.c +++ b/gdb/mi/mi-cmds.c @@ -26,13 +26,11 @@ #include #include -/* A command held in the MI_CMD_TABLE. */ +/* See mi-cmds.h. */ -using mi_command_up = std::unique_ptr; +std::map mi_cmd_table; -/* MI command table (built at run time). */ -static std::map mi_cmd_table; /* The abstract base class for all built-in MI command types. */ @@ -134,12 +132,9 @@ struct mi_command_cli : public mi_command_builtin bool m_args_p; }; -/* Insert COMMAND into the global mi_cmd_table. Return false if - COMMAND->name already exists in mi_cmd_table, in which case COMMAND will - not have been added to mi_cmd_table. Otherwise, return true, and - COMMAND was added to mi_cmd_table. */ +/* See mi-cmds.h. */ -static bool +bool insert_mi_cmd_entry (mi_command_up command) { gdb_assert (command != nullptr); diff --git a/gdb/mi/mi-cmds.h b/gdb/mi/mi-cmds.h index 94ecb271d48..75c5927e3cb 100644 --- a/gdb/mi/mi-cmds.h +++ b/gdb/mi/mi-cmds.h @@ -22,6 +22,7 @@ #ifndef MI_MI_CMDS_H #define MI_MI_CMDS_H +#include #include "gdbsupport/gdb_optional.h" enum print_values { @@ -183,6 +184,14 @@ struct mi_command int *m_suppress_notification; }; +/* A command held in the MI_CMD_TABLE. */ + +using mi_command_up = std::unique_ptr; + +/* MI command table (built at run time). */ + +extern std::map mi_cmd_table; + /* Lookup a command in the MI command table, returns nullptr if COMMAND is not found. */ @@ -190,4 +199,9 @@ extern mi_command *mi_cmd_lookup (const char *command); extern void mi_execute_command (const char *cmd, int from_tty); +/* Insert a new mi-command into the command table. Return true if + insertion was successful. */ + +extern bool insert_mi_cmd_entry (mi_command_up command); + #endif /* MI_MI_CMDS_H */ diff --git a/gdb/python/py-micmd.c b/gdb/python/py-micmd.c new file mode 100644 index 00000000000..b94b748b25c --- /dev/null +++ b/gdb/python/py-micmd.c @@ -0,0 +1,300 @@ +/* MI Command Set for GDB, the GNU debugger. + + Copyright (C) 2019 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 . */ + +/* gdb MI commands implemented in Python */ + +#include "defs.h" +#include "python-internal.h" +#include "python/py-micmd.h" +#include "arch-utils.h" +#include "charset.h" +#include "language.h" + +#include + +static PyObject *invoke_cst; + +extern PyTypeObject + micmdpy_object_type CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("micmdpy_object"); + +/* If the command invoked returns a list, this function parses it and create an + appropriate MI out output. + + The returned values must be Python string, and can be contained within Python + lists and dictionaries. It is possible to have a multiple levels of lists + and/or dictionaries. */ + +static void +parse_mi_result (PyObject *result, const char *field_name) +{ + struct ui_out *uiout = current_uiout; + + if (PyDict_Check (result)) + { + PyObject *key, *value; + Py_ssize_t pos = 0; + ui_out_emit_tuple tuple_emitter (uiout, field_name); + while (PyDict_Next (result, &pos, &key, &value)) + { + if (!PyString_Check (key)) + { + gdbpy_ref<> key_repr (PyObject_Repr (key)); + if (PyErr_Occurred () != NULL) + { + gdbpy_err_fetch ex; + gdb::unique_xmalloc_ptr ex_msg (ex.to_string ()); + + if (ex_msg == NULL || *ex_msg == '\0') + error (_("Non-string object used as key.")); + else + error (_("Non-string object used as key: %s."), + ex_msg.get ()); + } + else + { + auto key_repr_string + = python_string_to_target_string (key_repr.get ()); + error (_("Non-string object used as key: %s."), + key_repr_string.get ()); + } + } + + auto key_string = python_string_to_target_string (key); + parse_mi_result (value, key_string.get ()); + } + } + else if (PySequence_Check (result) && !PyString_Check (result)) + { + ui_out_emit_list list_emitter (uiout, field_name); + for (Py_ssize_t i = 0; i < PySequence_Size (result); ++i) + { + gdbpy_ref<> item (PySequence_ITEM (result, i)); + parse_mi_result (item.get (), NULL); + } + } + else if (PyIter_Check (result)) + { + gdbpy_ref<> item; + ui_out_emit_list list_emitter (uiout, field_name); + while (item.reset (PyIter_Next (result)), item != nullptr) + parse_mi_result (item.get (), NULL); + } + else + { + gdb::unique_xmalloc_ptr string (gdbpy_obj_to_string (result)); + uiout->field_string (field_name, string.get ()); + } +} + +/* Object initializer; sets up gdb-side structures for MI command. + + Use: __init__(NAME). + + NAME is the name of the MI command to register. It must start with a dash + as traditional MI commands do. */ + +static int +micmdpy_init (PyObject *self, PyObject *args, PyObject *kw) +{ + const char *name; + gdbpy_ref<> self_ref = gdbpy_ref<>::new_reference (self); + + if (!PyArg_ParseTuple (args, "s", &name)) + return -1; + + /* Validate command name */ + const int name_len = strlen (name); + if (name_len == 0) + { + error (_("MI command name is empty.")); + return -1; + } + else if ((name_len < 2) || (name[0] != '-') || !isalnum (name[1])) + { + error (_("MI command name does not start with '-'" + " followed by at least one letter or digit.")); + return -1; + } + else + for (int i = 2; i < name_len; i++) + { + if (!isalnum (name[i]) && name[i] != '-') + { + error (_("MI command name contains invalid character: %c."), + name[i]); + return -1; + } + } + + if (!PyObject_HasAttr (self_ref.get (), invoke_cst)) + error (_("-%s: Python command object missing 'invoke' method."), name); + + try + { + mi_command_up micommand = mi_command_up(new mi_command_py (name + 1, self_ref)); + + bool result = insert_mi_cmd_entry (std::move (micommand)); + + if (!result) + { + error (_("Unable to insert command." + "The name might already be in use.")); + return -1; + } + } + catch (const gdb_exception &except) + { + GDB_PY_SET_HANDLE_EXCEPTION (except); + } + + return 0; +} + +mi_command_py::mi_command_py (const char *name, gdbpy_ref<> object) + : mi_command (NULL), + pyobj (object) +{ + gdb_assert (name != nullptr && name[0] != '\0' && name[0] != '-'); + m_name = name; +} + +void +mi_command_py::do_invoke (struct mi_parse *parse) const +{ + mi_parse_argv (parse->args, parse); + + if (parse->argv == NULL) + error (_("Problem parsing arguments: %s %s"), parse->command, parse->args); + + PyObject *obj = this->pyobj.get (); + + gdbpy_enter enter_py (get_current_arch (), current_language); + + gdb_assert (obj != nullptr); + + if (!PyObject_HasAttr (obj, invoke_cst)) + error (_("-%s: Python command object missing 'invoke' method."), + name ()); + + + gdbpy_ref<> argobj (PyList_New (parse->argc)); + if (argobj == nullptr) + { + gdbpy_print_stack (); + error (_("-%s: failed to create the Python arguments list."), + name ()); + } + + for (int i = 0; i < parse->argc; ++i) + { + gdbpy_ref<> str (PyUnicode_Decode (parse->argv[i], strlen (parse->argv[i]), + host_charset (), NULL)); + if (PyList_SetItem (argobj.get (), i, str.release ()) != 0) + { + error (_("-%s: failed to create the Python arguments list."), + name ()); + } + } + + gdb_assert (PyErr_Occurred () == NULL); + gdbpy_ref<> result ( + PyObject_CallMethodObjArgs (obj, invoke_cst, argobj.get (), NULL)); + if (PyErr_Occurred () != NULL) + { + gdbpy_err_fetch ex; + gdb::unique_xmalloc_ptr ex_msg (ex.to_string ()); + + if (ex_msg == NULL || *ex_msg == '\0') + error (_("-%s: failed to execute command"), name ()); + else + error (_("-%s: %s"), name (), ex_msg.get ()); + } + else + { + if (Py_None != result) + parse_mi_result (result.get (), "result"); + } +} + +void mi_command_py::finalize () +{ + this->pyobj.reset (nullptr); +} + +/* Initialize the MI command object. */ + +int +gdbpy_initialize_micommands () +{ + micmdpy_object_type.tp_new = PyType_GenericNew; + if (PyType_Ready (&micmdpy_object_type) < 0) + return -1; + + if (gdb_pymodule_addobject (gdb_module, "MICommand", + (PyObject *) &micmdpy_object_type) + < 0) + return -1; + + invoke_cst = PyString_FromString ("invoke"); + if (invoke_cst == NULL) + return -1; + + return 0; +} + +static PyMethodDef micmdpy_object_methods[] = {{0}}; + +PyTypeObject micmdpy_object_type = { + PyVarObject_HEAD_INIT (NULL, 0) "gdb.MICommand", /*tp_name */ + sizeof (micmdpy_object), /*tp_basicsize */ + 0, /*tp_itemsize */ + 0, /*tp_dealloc */ + 0, /*tp_print */ + 0, /*tp_getattr */ + 0, /*tp_setattr */ + 0, /*tp_compare */ + 0, /*tp_repr */ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash */ + 0, /*tp_call */ + 0, /*tp_str */ + 0, /*tp_getattro */ + 0, /*tp_setattro */ + 0, /*tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags */ + "GDB mi-command object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + micmdpy_object_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + micmdpy_init, /* tp_init */ + 0, /* tp_alloc */ +}; diff --git a/gdb/python/py-micmd.h b/gdb/python/py-micmd.h new file mode 100644 index 00000000000..b6b25351ac7 --- /dev/null +++ b/gdb/python/py-micmd.h @@ -0,0 +1,63 @@ +/* MI Command Set for GDB, the GNU debugger. + + Copyright (C) 2019 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 . */ + +#ifndef PY_MICMDS_H +#define PY_MICMDS_H + +#include "mi/mi-cmds.h" +#include "mi/mi-parse.h" +#include "python-internal.h" +#include "python/py-ref.h" + +struct micmdpy_object +{ + PyObject_HEAD +}; + +typedef struct micmdpy_object micmdpy_object; + +/* MI command implemented in Python. */ + +class mi_command_py : public mi_command +{ + public: + /* Constructs a new mi_command_py object. NAME is command name without + leading dash. OBJECT is a reference to a Python object implementing + the command. This object should inherit from gdb.MICommand and should + implement method invoke (args). */ + mi_command_py (const char *name, gdbpy_ref<> object); + + + /* This is called just before shutting down a Python interpreter + to release python object implementing the command. */ + void finalize (); + + virtual const char *name () const override + { return m_name.c_str(); } + + protected: + virtual void do_invoke(struct mi_parse *parse) const override; + + private: + std::string m_name; + + gdbpy_ref<> pyobj; +}; + +#endif diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index 583989c5a6d..d5a0b4a4a91 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -561,6 +561,8 @@ int gdbpy_initialize_membuf () CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION; int gdbpy_initialize_connection () CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION; +int gdbpy_initialize_micommands (void) + CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION; /* A wrapper for PyErr_Fetch that handles reference counting for the caller. */ diff --git a/gdb/python/python.c b/gdb/python/python.c index 4dcda53d9ab..6e2ed32bf90 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -37,6 +37,8 @@ #include "run-on-main-thread.h" #include "gdbsupport/selftest.h" #include "observable.h" +#include "mi/mi-cmds.h" +#include "py-micmd.h" /* Declared constants and enum for python stack printing. */ static const char python_excp_none[] = "none"; @@ -1699,6 +1701,14 @@ finalize_python (void *ignore) python_gdbarch = target_gdbarch (); python_language = current_language; + for (const auto& name_and_cmd : mi_cmd_table) + { + mi_command *cmd = name_and_cmd.second.get (); + mi_command_py *cmd_py = dynamic_cast (cmd); + if (cmd_py != nullptr) + cmd_py->finalize (); + } + Py_Finalize (); gdb_python_initialized = false; @@ -1887,7 +1897,8 @@ do_start_initialization () || gdbpy_initialize_unwind () < 0 || gdbpy_initialize_membuf () < 0 || gdbpy_initialize_connection () < 0 - || gdbpy_initialize_tui () < 0) + || gdbpy_initialize_tui () < 0 + || gdbpy_initialize_micommands () < 0) return false; #define GDB_PY_DEFINE_EVENT_TYPE(name, py_name, doc, base) \ -- 2.30.2