From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 52295 invoked by alias); 13 Nov 2017 19:29:15 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 51731 invoked by uid 89); 13 Nov 2017 19:29:15 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.8 required=5.0 tests=BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,KAM_STOCKGEN,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.2 spammy=tuple, frees, peculiarity, Phil X-HELO: mail-wr0-f179.google.com Received: from mail-wr0-f179.google.com (HELO mail-wr0-f179.google.com) (209.85.128.179) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 13 Nov 2017 19:29:12 +0000 Received: by mail-wr0-f179.google.com with SMTP id u97so15458110wrc.1 for ; Mon, 13 Nov 2017 11:29:11 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:from:to:cc:references:message-id:date :mime-version:in-reply-to:content-language:content-transfer-encoding; bh=2Xsd6Ge+dZ1KLz9Ql9dqdNvR0iMFYGaWZHn4IYtJAiE=; b=EHGll0t36+u2SGe8v2YDi7s+FaL8UpUtk80wBO0mJP+XMjsAfza4zcrXXXaxCmBFql pTUvE/wMkCxyBt1kk0wZ5oyqcOucL787KtoJOZLJ/BSXLzd7tOFHo6PZ/kWgs+VH0jHm 4SIU/XnlAzJ2n6wAecMxgatdWUoCMFuPMnSyDsVcgIzGoUoY9/x43ucREmBILNOKBjXx +9xmSEw9yyqV4TVN1JwHaSK0i20QPP5V1icAsOGpJa/KYCgr2tBw83flN1e9UhCqHsof R+axhl5FGwFRKz/2ApEG5gxZ6ACc1kMGeBrYkJRdpi1GY20vZRasp74+PJTNp3E/YM9B meYQ== X-Gm-Message-State: AJaThX6hcCDaEsK0dxKooGIZV8/tFSNMkB1uM1TLP0lhlP6AoITFhB28 WzVEN1pB1zLmyPXCNSMddM5LebWNx78= X-Google-Smtp-Source: AGs4zMbWi+2XFdJXPz7lTlkQaJ2d304zXEEec3N4RZHb4BBPQb+9SN9bl3zabYwRhZBHOy5g7+CgOA== X-Received: by 10.223.172.228 with SMTP id o91mr8324350wrc.197.1510601349093; Mon, 13 Nov 2017 11:29:09 -0800 (PST) Received: from ?IPv6:2a02:c7f:ae6a:ed00:4685:ff:fe66:9f4? ([2a02:c7f:ae6a:ed00:4685:ff:fe66:9f4]) by smtp.gmail.com with ESMTPSA id v35sm32105558wrc.13.2017.11.13.11.29.07 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 13 Nov 2017 11:29:07 -0800 (PST) Subject: Re: [python][patch] Python rbreak From: Phil Muldoon To: Simon Marchi Cc: gdb-patches@sourceware.org References: <5e1ba7e3-5f6e-2478-30a5-7670ec7a9879@redhat.com> <3193f5c7a0c98c548722bb6c143f347e@polymtl.ca> <8ee8a4c0-4580-474f-a5aa-6f76a8d22960@redhat.com> Message-ID: Date: Mon, 13 Nov 2017 19:29:00 -0000 MIME-Version: 1.0 In-Reply-To: <8ee8a4c0-4580-474f-a5aa-6f76a8d22960@redhat.com> Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit X-IsSubscribed: yes X-SW-Source: 2017-11/txt/msg00247.txt.bz2 Ping On 03/11/17 09:46, Phil Muldoon wrote: > On 17/10/17 01:24, Simon Marchi wrote: >> On 2017-10-16 19:01, Phil Muldoon wrote: > > >>>> I can't find a reference, but I think we want test names to start >>>> with a lower case letter and not end with a dot. I'll see if we >>>> can add this to the testcase cookbook wiki page. >>> >>> As I mentioned on IRC, I've not heard of it but will happily change >>> the names to comply. > > Sorry this took a bit longer to get back out than I would have liked. > Modified patch follows. I believe I have incorporated yours, Eli's and > Kevin's comments. ChangeLogs remain the same (other than the new NEWS > entry which I have added locally.) > > Cheers > > -- > > diff --git a/gdb/NEWS b/gdb/NEWS > index 2bad096a86..1d26ea4af7 100644 > --- a/gdb/NEWS > +++ b/gdb/NEWS > @@ -24,6 +24,10 @@ > gdb.new_thread are emitted. See the manual for further > description of these. > > + ** A new command, "rbreak" has been added to the Python API. This > + command allows the setting of a large number of breakpoints via a > + regex pattern in Python. See the manual for further details. > + > * New features in the GDB remote stub, GDBserver > > ** GDBserver is now able to start inferior processes with a > diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi > index f661e489bb..f411f60d7e 100644 > --- a/gdb/doc/python.texi > +++ b/gdb/doc/python.texi > @@ -243,6 +243,23 @@ were no breakpoints. This peculiarity was subsequently fixed, and now > @code{gdb.breakpoints} returns an empty sequence in this case. > @end defun > > +@defun gdb.rbreak (regex @r{[}, minsyms @r{[}, throttle, @r{[}, symtabs @r{]]]}) > +Return a Python list holding a collection of newly set > +@code{gdb.Breakpoint} objects matching function names defined by the > +@var{regex} pattern. If the @var{minsyms} keyword is @code{True}, all > +system functions (those not explicitly defined in the inferior) will > +also be included in the match. The @var{throttle} keyword takes an > +integer that defines the maximum number of pattern matches for > +functions matched by the @var{regex} pattern. If the number of > +matches exceeds the integer value of @var{throttle}, a > +@code{RuntimeError} will be raised and no breakpoints will be created. > +If @var{throttle} is not defined then there is no imposed limit on the > +maximum number of matches and breakpoints to be created. The > +@var{symtabs} keyword takes a Python iterable that yields a collection > +of @code{gdb.Symtab} objects and will restrict the search to those > +functions only contained within the @code{gdb.Symtab} objects. > +@end defun > + > @findex gdb.parameter > @defun gdb.parameter (parameter) > Return the value of a @value{GDBN} @var{parameter} given by its name, > diff --git a/gdb/python/python.c b/gdb/python/python.c > index b04057ec4a..a044b8ff8b 100644 > --- a/gdb/python/python.c > +++ b/gdb/python/python.c > @@ -642,6 +642,190 @@ gdbpy_solib_name (PyObject *self, PyObject *args) > return str_obj; > } > > +/* Implementation of Python rbreak command. Take a REGEX and > + optionally a MINSYMS, THROTTLE and SYMTABS keyword and return a > + Python list that contains newly set breakpoints that match that > + criteria. REGEX refers to a GDB format standard regex pattern of > + symbols names to search; MINSYMS is an optional boolean (default > + False) that indicates if the function should search GDB's minimal > + symbols; THROTTLE is an optional integer (default unlimited) that > + indicates the maximum amount of breakpoints allowable before the > + function exits (note, if the throttle bound is passed, no > + breakpoints will be set and a runtime error returned); SYMTABS is > + an optional Python iterable that contains a set of gdb.Symtabs to > + constrain the search within. */ > + > +static PyObject * > +gdbpy_rbreak (PyObject *self, PyObject *args, PyObject *kw) > +{ > + /* A simple type to ensure clean up of a vector of allocated strings > + when a C interface demands a const char *array[] type > + interface. */ > + struct symtab_list_type > + { > + ~symtab_list_type () > + { > + for (const char *elem: vec) > + xfree ((void *) elem); > + } > + std::vector vec; > + }; > + > + char *regex = NULL; > + std::vector symbols; > + unsigned long count = 0; > + PyObject *symtab_list = NULL; > + PyObject *minsyms_p_obj = NULL; > + int minsyms_p = 0; > + unsigned int throttle = 0; > + static const char *keywords[] = {"regex","minsyms", "throttle", > + "symtabs", NULL}; > + symtab_list_type symtab_paths; > + > + if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|O!IO", keywords, > + ®ex, &PyBool_Type, > + &minsyms_p_obj, &throttle, > + &symtab_list)) > + return NULL; > + > + /* Parse minsyms keyword. */ > + if (minsyms_p_obj != NULL) > + { > + int cmp = PyObject_IsTrue (minsyms_p_obj); > + if (cmp < 0) > + return NULL; > + minsyms_p = cmp; > + } > + > + /* The "symtabs" keyword is any Python iterable object that returns > + a gdb.Symtab on each iteration. If specified, iterate through > + the provided gdb.Symtabs and extract their full path. As > + python_string_to_target_string returns a > + gdb::unique_xmalloc_ptr and a vector containing these types > + cannot be coerced to a const char **p[] via the vector.data call, > + release the value from the unique_xmalloc_ptr and place it in a > + simple type symtab_list_type (which holds the vector and a > + destructor that frees the contents of the allocated strings. */ > + if (symtab_list != NULL) > + { > + gdbpy_ref<> iter (PyObject_GetIter (symtab_list)); > + > + if (iter == NULL) > + return NULL; > + > + while (true) > + { > + gdbpy_ref<> next (PyIter_Next (iter.get ())); > + > + if (next == NULL) > + { > + if (PyErr_Occurred ()) > + return NULL; > + break; > + } > + > + gdbpy_ref<> obj_name (PyObject_GetAttrString (next.get (), > + "filename")); > + > + if (obj_name == NULL) > + return NULL; > + > + /* Is the object file still valid? */ > + if (obj_name == Py_None) > + continue; > + > + gdb::unique_xmalloc_ptr filename = > + python_string_to_target_string (obj_name.get ()); > + > + if (filename == NULL) > + return NULL; > + > + /* Make sure there is a definite place to store the value of > + s before it is released. */ > + symtab_paths.vec.push_back (nullptr); > + symtab_paths.vec.back () = filename.release (); > + } > + } > + > + if (symtab_list) > + { > + const char **files = symtab_paths.vec.data (); > + > + symbols = search_symbols (regex, FUNCTIONS_DOMAIN, > + symtab_paths.vec.size (), files); > + } > + else > + symbols = search_symbols (regex, FUNCTIONS_DOMAIN, 0, NULL); > + > + /* Count the number of symbols (both symbols and optionally minimal > + symbols) so we can correctly check the throttle limit. */ > + for (const symbol_search &p : symbols) > + { > + /* Minimal symbols included? */ > + if (minsyms_p) > + { > + if (p.msymbol.minsym != NULL) > + count++; > + } > + > + if (p.symbol != NULL) > + count++; > + } > + > + /* Check throttle bounds and exit if in excess. */ > + if (throttle != 0 && count > throttle) > + { > + PyErr_SetString (PyExc_RuntimeError, > + _("Number of breakpoints exceeds throttled maximum.")); > + return NULL; > + } > + > + gdbpy_ref<> return_list (PyList_New (0)); > + > + if (return_list == NULL) > + return NULL; > + > + /* Construct full path names for symbols and call the Python > + breakpoint constructor on the resulting names. Be tolerant of > + individual breakpoint failures. */ > + for (const symbol_search &p : symbols) > + { > + std::string symbol_name; > + > + /* Skipping minimal symbols? */ > + if (minsyms_p == 0) > + if (p.msymbol.minsym != NULL) > + continue; > + > + if (p.msymbol.minsym == NULL) > + { > + struct symtab *symtab = symbol_symtab (p.symbol); > + const char *fullname = symtab_to_fullname (symtab); > + > + symbol_name = fullname; > + symbol_name += ":"; > + symbol_name += SYMBOL_LINKAGE_NAME (p.symbol); > + } > + else > + symbol_name = MSYMBOL_LINKAGE_NAME (p.msymbol.minsym); > + > + gdbpy_ref<> argList (Py_BuildValue("(s)", symbol_name.c_str ())); > + gdbpy_ref<> obj (PyObject_CallObject ((PyObject *) > + &breakpoint_object_type, > + argList.get ())); > + > + /* Tolerate individual breakpoint failures. */ > + if (obj == NULL) > + gdbpy_print_stack (); > + else > + { > + if (PyList_Append (return_list.get (), obj.get ()) == -1) > + return NULL; > + } > + } > + return return_list.release (); > +} > + > /* A Python function which is a wrapper for decode_line_1. */ > > static PyObject * > @@ -1912,7 +2096,9 @@ Return the name of the current target charset." }, > { "target_wide_charset", gdbpy_target_wide_charset, METH_NOARGS, > "target_wide_charset () -> string.\n\ > Return the name of the current target wide charset." }, > - > + { "rbreak", (PyCFunction) gdbpy_rbreak, METH_VARARGS | METH_KEYWORDS, > + "rbreak (Regex) -> List.\n\ > +Return a Tuple containing gdb.Breakpoint objects that match the given Regex." }, > { "string_to_argv", gdbpy_string_to_argv, METH_VARARGS, > "string_to_argv (String) -> Array.\n\ > Parse String and return an argv-like array.\n\ > diff --git a/gdb/testsuite/gdb.python/py-rbreak-func2.c b/gdb/testsuite/gdb.python/py-rbreak-func2.c > new file mode 100644 > index 0000000000..2d24b6b557 > --- /dev/null > +++ b/gdb/testsuite/gdb.python/py-rbreak-func2.c > @@ -0,0 +1,34 @@ > +/* This testcase is part of GDB, the GNU debugger. > + > + Copyright 2017 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 . */ > + > +int > +efunc1 () > +{ > + return 1; > +} > + > +int > +efunc2 () > +{ > + return 2; > +} > + > +int > +efunc3 () > +{ > + return 3; > +} > diff --git a/gdb/testsuite/gdb.python/py-rbreak.c b/gdb/testsuite/gdb.python/py-rbreak.c > new file mode 100644 > index 0000000000..e79d2a34ae > --- /dev/null > +++ b/gdb/testsuite/gdb.python/py-rbreak.c > @@ -0,0 +1,70 @@ > +/* This testcase is part of GDB, the GNU debugger. > + > + Copyright 2013-2017 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 . */ > + > +int > +func1 () > +{ > + return 1; > +} > + > +int > +func2 () > +{ > + return 2; > +} > + > +int > +func3 () > +{ > + return 3; > +} > + > +int > +func4 () > +{ > + return 4; > +} > + > +int > +func5 () > +{ > + return 5; > +} > + > +void > +func6 () > +{ > + return; > +} > + > +void > +outside_scope () > +{ > + return; > +} > + > +int > +main() > +{ > + func1 (); /* Break func1. */ > + func2 (); > + func3 (); > + func4 (); > + func5 (); > + func6 (); > + outside_scope (); > +} > diff --git a/gdb/testsuite/gdb.python/py-rbreak.exp b/gdb/testsuite/gdb.python/py-rbreak.exp > new file mode 100644 > index 0000000000..5aaf2975c9 > --- /dev/null > +++ b/gdb/testsuite/gdb.python/py-rbreak.exp > @@ -0,0 +1,61 @@ > +# Copyright (C) 2017 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 . > + > +# This file is part of the GDB testsuite. It tests the mechanism > +# exposing values to Python. > + > +load_lib gdb-python.exp > + > +standard_testfile py-rbreak.c py-rbreak-func2.c > + > +if {[prepare_for_testing "failed to prepare" ${testfile} [list $srcfile $srcfile2]] } { > + return 1 > +} > + > +# Skip all tests if Python scripting is not enabled. > +if { [skip_python_tests] } { continue } > + > +if ![runto_main] then { > + fail "can't run to main" > + return 0 > +} > + > +gdb_py_test_silent_cmd "py sl = gdb.rbreak(\"\",minsyms=False)" \ > + "get all function breakpoints" 0 > +gdb_test "py print(len(sl))" "11" \ > + "check number of returned breakpoints is 11" > +gdb_py_test_silent_cmd "py sl = gdb.rbreak(\"main\.\*\",minsyms=False)" \ > + "get main function breakpoint" 0 > +gdb_test "py print(len(sl))" "1" \ > + "check number of returned breakpoints is 1" > +gdb_py_test_silent_cmd "py sl = gdb.rbreak(\"func\.\*\",minsyms=False,throttle=10)" \ > + "get functions matching func.*" 0 > +gdb_test "py print(len(sl))" "9" \ > + "check number of returned breakpoints is 9" > +gdb_test "py gdb.rbreak(\"func\.\*\",minsyms=False,throttle=5)" \ > + "Number of breakpoints exceeds throttled maximum.*" \ > + "check throttle errors on too many breakpoints" > +gdb_py_test_silent_cmd "py sl = gdb.rbreak(\"func1\",minsyms=True)" \ > + "including minimal symbols, get functions matching func.*" 0 > +gdb_test "py print(len(sl))" "2" \ > + "check number of returned breakpoints is 2" > +gdb_py_test_silent_cmd "python sym = gdb.lookup_symbol(\"efunc1\")" \ > + "find a symbol in objfile" 1 > +gdb_py_test_silent_cmd "python symtab = sym\[0\].symtab" \ > + "get backing symbol table" 1 > +gdb_py_test_silent_cmd "py sl = gdb.rbreak(\"func\.\*\",minsyms=False,throttle=10,symtabs=\[symtab\])" \ > + "get functions matching func.* in one symtab only" 0 > +gdb_test "py print(len(sl))" "3" \ > + "check number of returned breakpoints is 3" >