| # Xmethod commands. |
| # Copyright 2013-2016 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 <http://www.gnu.org/licenses/>. |
| |
| import gdb |
| import re |
| |
| """GDB commands for working with xmethods.""" |
| |
| |
| def validate_xm_regexp(part_name, regexp): |
| try: |
| return re.compile(regexp) |
| except SyntaxError: |
| raise SyntaxError("Invalid %s regexp: %s", part_name, regexp) |
| |
| |
| def parse_xm_command_args(arg): |
| """Parses the arguments passed to a xmethod command. |
| |
| Arguments: |
| arg: The argument string passed to a xmethod command. |
| |
| Returns: |
| A 3-tuple: (<locus matching regular expression>, |
| <matcher matching regular expression>, |
| <name matching regular experession>) |
| """ |
| argv = gdb.string_to_argv(arg) |
| argc = len(argv) |
| if argc > 2: |
| raise SyntaxError("Too many arguments to command.") |
| locus_regexp = "" |
| matcher_name_regexp = "" |
| xm_name_regexp = None |
| if argc >= 1: |
| locus_regexp = argv[0] |
| if argc == 2: |
| parts = argv[1].split(";", 1) |
| matcher_name_regexp = parts[0] |
| if len(parts) > 1: |
| xm_name_regexp = parts[1] |
| if xm_name_regexp: |
| name_re = validate_xm_regexp("xmethod name", xm_name_regexp) |
| else: |
| name_re = None |
| return (validate_xm_regexp("locus", locus_regexp), |
| validate_xm_regexp("matcher name", matcher_name_regexp), |
| name_re) |
| |
| |
| def get_global_method_matchers(locus_re, matcher_re): |
| """Returns a dict of matching globally registered xmethods. |
| |
| Arguments: |
| locus_re: Even though only globally registered xmethods are |
| looked up, they will be looked up only if 'global' matches |
| LOCUS_RE. |
| matcher_re: The regular expression matching the names of xmethods. |
| |
| Returns: |
| A dict of matching globally registered xmethod matchers. The only |
| key in the dict will be 'global'. |
| """ |
| locus_str = "global" |
| xm_dict = { locus_str: [] } |
| if locus_re.match("global"): |
| xm_dict[locus_str].extend( |
| [m for m in gdb.xmethods if matcher_re.match(m.name)]) |
| return xm_dict |
| |
| |
| def get_method_matchers_in_loci(loci, locus_re, matcher_re): |
| """Returns a dict of matching registered xmethods in the LOCI. |
| |
| Arguments: |
| loci: The list of loci to lookup matching xmethods in. |
| locus_re: If a locus is an objfile, then xmethod matchers will be |
| looked up in it only if its filename matches the regular |
| expression LOCUS_RE. If a locus is the current progspace, |
| then xmethod matchers will be looked up in it only if the |
| string "progspace" matches LOCUS_RE. |
| matcher_re: The regular expression to match the xmethod matcher |
| names. |
| |
| Returns: |
| A dict of matching xmethod matchers. The keys of the dict are the |
| filenames of the loci the xmethod matchers belong to. |
| """ |
| xm_dict = {} |
| for locus in loci: |
| if isinstance(locus, gdb.Progspace): |
| if not locus_re.match('progspace'): |
| continue |
| locus_type = "progspace" |
| else: |
| if not locus_re.match(locus.filename): |
| continue |
| locus_type = "objfile" |
| locus_str = "%s %s" % (locus_type, locus.filename) |
| xm_dict[locus_str] = [ |
| m for m in locus.xmethods if matcher_re.match(m.name)] |
| return xm_dict |
| |
| |
| def print_xm_info(xm_dict, name_re): |
| """Print a dictionary of xmethods.""" |
| def get_status_string(m): |
| if not m.enabled: |
| return " [disabled]" |
| else: |
| return "" |
| |
| if not xm_dict: |
| return |
| for locus_str in xm_dict: |
| if not xm_dict[locus_str]: |
| continue |
| print ("Xmethods in %s:" % locus_str) |
| for matcher in xm_dict[locus_str]: |
| print (" %s%s" % (matcher.name, get_status_string(matcher))) |
| if not matcher.methods: |
| continue |
| for m in matcher.methods: |
| if name_re is None or name_re.match(m.name): |
| print (" %s%s" % (m.name, get_status_string(m))) |
| |
| |
| def set_xm_status1(xm_dict, name_re, status): |
| """Set the status (enabled/disabled) of a dictionary of xmethods.""" |
| for locus_str, matchers in xm_dict.items(): |
| for matcher in matchers: |
| if not name_re: |
| # If the name regex is missing, then set the status of the |
| # matcher and move on. |
| matcher.enabled = status |
| continue |
| if not matcher.methods: |
| # The methods attribute could be None. Move on. |
| continue |
| for m in matcher.methods: |
| if name_re.match(m.name): |
| m.enabled = status |
| |
| |
| def set_xm_status(arg, status): |
| """Set the status (enabled/disabled) of xmethods matching ARG. |
| This is a helper function for enable/disable commands. ARG is the |
| argument string passed to the commands. |
| """ |
| locus_re, matcher_re, name_re = parse_xm_command_args(arg) |
| set_xm_status1(get_global_method_matchers(locus_re, matcher_re), name_re, |
| status) |
| set_xm_status1( |
| get_method_matchers_in_loci( |
| [gdb.current_progspace()], locus_re, matcher_re), |
| name_re, |
| status) |
| set_xm_status1( |
| get_method_matchers_in_loci(gdb.objfiles(), locus_re, matcher_re), |
| name_re, |
| status) |
| |
| |
| class InfoXMethod(gdb.Command): |
| """GDB command to list registered xmethod matchers. |
| |
| Usage: info xmethod [locus-regexp [name-regexp]] |
| |
| LOCUS-REGEXP is a regular expression matching the location of the |
| xmethod matchers. If it is omitted, all registered xmethod matchers |
| from all loci are listed. A locus could be 'global', a regular expression |
| matching the current program space's filename, or a regular expression |
| matching filenames of objfiles. Locus could be 'progspace' to specify that |
| only xmethods from the current progspace should be listed. |
| |
| NAME-REGEXP is a regular expression matching the names of xmethod |
| matchers. If this omitted for a specified locus, then all registered |
| xmethods in the locus are listed. To list only a certain xmethods |
| managed by a single matcher, the name regexp can be specified as |
| matcher-name-regexp;xmethod-name-regexp. |
| """ |
| |
| def __init__(self): |
| super(InfoXMethod, self).__init__("info xmethod", |
| gdb.COMMAND_DATA) |
| |
| def invoke(self, arg, from_tty): |
| locus_re, matcher_re, name_re = parse_xm_command_args(arg) |
| print_xm_info(get_global_method_matchers(locus_re, matcher_re), |
| name_re) |
| print_xm_info( |
| get_method_matchers_in_loci( |
| [gdb.current_progspace()], locus_re, matcher_re), |
| name_re) |
| print_xm_info( |
| get_method_matchers_in_loci(gdb.objfiles(), locus_re, matcher_re), |
| name_re) |
| |
| |
| class EnableXMethod(gdb.Command): |
| """GDB command to enable a specified (group of) xmethod(s). |
| |
| Usage: enable xmethod [locus-regexp [name-regexp]] |
| |
| LOCUS-REGEXP is a regular expression matching the location of the |
| xmethod matchers. If it is omitted, all registered xmethods matchers |
| from all loci are enabled. A locus could be 'global', a regular expression |
| matching the current program space's filename, or a regular expression |
| matching filenames of objfiles. Locus could be 'progspace' to specify that |
| only xmethods from the current progspace should be enabled. |
| |
| NAME-REGEXP is a regular expression matching the names of xmethods |
| within a given locus. If this omitted for a specified locus, then all |
| registered xmethod matchers in the locus are enabled. To enable only |
| a certain xmethods managed by a single matcher, the name regexp can be |
| specified as matcher-name-regexp;xmethod-name-regexp. |
| """ |
| |
| def __init__(self): |
| super(EnableXMethod, self).__init__("enable xmethod", |
| gdb.COMMAND_DATA) |
| |
| def invoke(self, arg, from_tty): |
| set_xm_status(arg, True) |
| |
| |
| class DisableXMethod(gdb.Command): |
| """GDB command to disable a specified (group of) xmethod(s). |
| |
| Usage: disable xmethod [locus-regexp [name-regexp]] |
| |
| LOCUS-REGEXP is a regular expression matching the location of the |
| xmethod matchers. If it is omitted, all registered xmethod matchers |
| from all loci are disabled. A locus could be 'global', a regular |
| expression matching the current program space's filename, or a regular |
| expression filenames of objfiles. Locus could be 'progspace' to specify |
| that only xmethods from the current progspace should be disabled. |
| |
| NAME-REGEXP is a regular expression matching the names of xmethods |
| within a given locus. If this omitted for a specified locus, then all |
| registered xmethod matchers in the locus are disabled. To disable |
| only a certain xmethods managed by a single matcher, the name regexp |
| can be specified as matcher-name-regexp;xmethod-name-regexp. |
| """ |
| |
| def __init__(self): |
| super(DisableXMethod, self).__init__("disable xmethod", |
| gdb.COMMAND_DATA) |
| |
| def invoke(self, arg, from_tty): |
| set_xm_status(arg, False) |
| |
| |
| def register_xmethod_commands(): |
| """Installs the xmethod commands.""" |
| InfoXMethod() |
| EnableXMethod() |
| DisableXMethod() |
| |
| |
| register_xmethod_commands() |