1# Xmethod commands. 2# Copyright 2013-2020 Free Software Foundation, Inc. 3 4# This program is free software; you can redistribute it and/or modify 5# it under the terms of the GNU General Public License as published by 6# the Free Software Foundation; either version 3 of the License, or 7# (at your option) any later version. 8# 9# This program is distributed in the hope that it will be useful, 10# but WITHOUT ANY WARRANTY; without even the implied warranty of 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12# GNU General Public License for more details. 13# 14# You should have received a copy of the GNU General Public License 15# along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17import gdb 18import re 19 20"""GDB commands for working with xmethods.""" 21 22 23def validate_xm_regexp(part_name, regexp): 24 try: 25 return re.compile(regexp) 26 except SyntaxError: 27 raise SyntaxError("Invalid %s regexp: %s", part_name, regexp) 28 29 30def parse_xm_command_args(arg): 31 """Parses the arguments passed to a xmethod command. 32 33 Arguments: 34 arg: The argument string passed to a xmethod command. 35 36 Returns: 37 A 3-tuple: (<locus matching regular expression>, 38 <matcher matching regular expression>, 39 <name matching regular experession>) 40 """ 41 argv = gdb.string_to_argv(arg) 42 argc = len(argv) 43 if argc > 2: 44 raise SyntaxError("Too many arguments to command.") 45 locus_regexp = "" 46 matcher_name_regexp = "" 47 xm_name_regexp = None 48 if argc >= 1: 49 locus_regexp = argv[0] 50 if argc == 2: 51 parts = argv[1].split(";", 1) 52 matcher_name_regexp = parts[0] 53 if len(parts) > 1: 54 xm_name_regexp = parts[1] 55 if xm_name_regexp: 56 name_re = validate_xm_regexp("xmethod name", xm_name_regexp) 57 else: 58 name_re = None 59 return (validate_xm_regexp("locus", locus_regexp), 60 validate_xm_regexp("matcher name", matcher_name_regexp), 61 name_re) 62 63 64def get_global_method_matchers(locus_re, matcher_re): 65 """Returns a dict of matching globally registered xmethods. 66 67 Arguments: 68 locus_re: Even though only globally registered xmethods are 69 looked up, they will be looked up only if 'global' matches 70 LOCUS_RE. 71 matcher_re: The regular expression matching the names of xmethods. 72 73 Returns: 74 A dict of matching globally registered xmethod matchers. The only 75 key in the dict will be 'global'. 76 """ 77 locus_str = "global" 78 xm_dict = { locus_str: [] } 79 if locus_re.match("global"): 80 xm_dict[locus_str].extend( 81 [m for m in gdb.xmethods if matcher_re.match(m.name)]) 82 return xm_dict 83 84 85def get_method_matchers_in_loci(loci, locus_re, matcher_re): 86 """Returns a dict of matching registered xmethods in the LOCI. 87 88 Arguments: 89 loci: The list of loci to lookup matching xmethods in. 90 locus_re: If a locus is an objfile, then xmethod matchers will be 91 looked up in it only if its filename matches the regular 92 expression LOCUS_RE. If a locus is the current progspace, 93 then xmethod matchers will be looked up in it only if the 94 string "progspace" matches LOCUS_RE. 95 matcher_re: The regular expression to match the xmethod matcher 96 names. 97 98 Returns: 99 A dict of matching xmethod matchers. The keys of the dict are the 100 filenames of the loci the xmethod matchers belong to. 101 """ 102 xm_dict = {} 103 for locus in loci: 104 if isinstance(locus, gdb.Progspace): 105 if not locus_re.match('progspace'): 106 continue 107 locus_type = "progspace" 108 else: 109 if not locus_re.match(locus.filename): 110 continue 111 locus_type = "objfile" 112 locus_str = "%s %s" % (locus_type, locus.filename) 113 xm_dict[locus_str] = [ 114 m for m in locus.xmethods if matcher_re.match(m.name)] 115 return xm_dict 116 117 118def print_xm_info(xm_dict, name_re): 119 """Print a dictionary of xmethods.""" 120 def get_status_string(m): 121 if not m.enabled: 122 return " [disabled]" 123 else: 124 return "" 125 126 if not xm_dict: 127 return 128 for locus_str in xm_dict: 129 if not xm_dict[locus_str]: 130 continue 131 print ("Xmethods in %s:" % locus_str) 132 for matcher in xm_dict[locus_str]: 133 print (" %s%s" % (matcher.name, get_status_string(matcher))) 134 if not matcher.methods: 135 continue 136 for m in matcher.methods: 137 if name_re is None or name_re.match(m.name): 138 print (" %s%s" % (m.name, get_status_string(m))) 139 140 141def set_xm_status1(xm_dict, name_re, status): 142 """Set the status (enabled/disabled) of a dictionary of xmethods.""" 143 for locus_str, matchers in xm_dict.items(): 144 for matcher in matchers: 145 if not name_re: 146 # If the name regex is missing, then set the status of the 147 # matcher and move on. 148 matcher.enabled = status 149 continue 150 if not matcher.methods: 151 # The methods attribute could be None. Move on. 152 continue 153 for m in matcher.methods: 154 if name_re.match(m.name): 155 m.enabled = status 156 157 158def set_xm_status(arg, status): 159 """Set the status (enabled/disabled) of xmethods matching ARG. 160 This is a helper function for enable/disable commands. ARG is the 161 argument string passed to the commands. 162 """ 163 locus_re, matcher_re, name_re = parse_xm_command_args(arg) 164 set_xm_status1(get_global_method_matchers(locus_re, matcher_re), name_re, 165 status) 166 set_xm_status1( 167 get_method_matchers_in_loci( 168 [gdb.current_progspace()], locus_re, matcher_re), 169 name_re, 170 status) 171 set_xm_status1( 172 get_method_matchers_in_loci(gdb.objfiles(), locus_re, matcher_re), 173 name_re, 174 status) 175 176 177class InfoXMethod(gdb.Command): 178 """GDB command to list registered xmethod matchers. 179 180Usage: info xmethod [LOCUS-REGEXP [NAME-REGEXP]] 181 182LOCUS-REGEXP is a regular expression matching the location of the 183xmethod matchers. If it is omitted, all registered xmethod matchers 184from all loci are listed. A locus could be 'global', a regular expression 185matching the current program space's filename, or a regular expression 186matching filenames of objfiles. Locus could be 'progspace' to specify that 187only xmethods from the current progspace should be listed. 188 189NAME-REGEXP is a regular expression matching the names of xmethod 190matchers. If this omitted for a specified locus, then all registered 191xmethods in the locus are listed. To list only a certain xmethods 192managed by a single matcher, the name regexp can be specified as 193matcher-name-regexp;xmethod-name-regexp.""" 194 195 def __init__(self): 196 super(InfoXMethod, self).__init__("info xmethod", 197 gdb.COMMAND_DATA) 198 199 def invoke(self, arg, from_tty): 200 locus_re, matcher_re, name_re = parse_xm_command_args(arg) 201 print_xm_info(get_global_method_matchers(locus_re, matcher_re), 202 name_re) 203 print_xm_info( 204 get_method_matchers_in_loci( 205 [gdb.current_progspace()], locus_re, matcher_re), 206 name_re) 207 print_xm_info( 208 get_method_matchers_in_loci(gdb.objfiles(), locus_re, matcher_re), 209 name_re) 210 211 212class EnableXMethod(gdb.Command): 213 """GDB command to enable a specified (group of) xmethod(s). 214 215Usage: enable xmethod [LOCUS-REGEXP [NAME-REGEXP]] 216 217LOCUS-REGEXP is a regular expression matching the location of the 218xmethod matchers. If it is omitted, all registered xmethods matchers 219from all loci are enabled. A locus could be 'global', a regular expression 220matching the current program space's filename, or a regular expression 221matching filenames of objfiles. Locus could be 'progspace' to specify that 222only xmethods from the current progspace should be enabled. 223 224NAME-REGEXP is a regular expression matching the names of xmethods 225within a given locus. If this omitted for a specified locus, then all 226registered xmethod matchers in the locus are enabled. To enable only 227a certain xmethods managed by a single matcher, the name regexp can be 228specified as matcher-name-regexp;xmethod-name-regexp.""" 229 230 def __init__(self): 231 super(EnableXMethod, self).__init__("enable xmethod", 232 gdb.COMMAND_DATA) 233 234 def invoke(self, arg, from_tty): 235 set_xm_status(arg, True) 236 237 238class DisableXMethod(gdb.Command): 239 """GDB command to disable a specified (group of) xmethod(s). 240 241Usage: disable xmethod [LOCUS-REGEXP [NAME-REGEXP]] 242 243LOCUS-REGEXP is a regular expression matching the location of the 244xmethod matchers. If it is omitted, all registered xmethod matchers 245from all loci are disabled. A locus could be 'global', a regular 246expression matching the current program space's filename, or a regular 247expression filenames of objfiles. Locus could be 'progspace' to specify 248that only xmethods from the current progspace should be disabled. 249 250NAME-REGEXP is a regular expression matching the names of xmethods 251within a given locus. If this omitted for a specified locus, then all 252registered xmethod matchers in the locus are disabled. To disable 253only a certain xmethods managed by a single matcher, the name regexp 254can be specified as matcher-name-regexp;xmethod-name-regexp.""" 255 256 def __init__(self): 257 super(DisableXMethod, self).__init__("disable xmethod", 258 gdb.COMMAND_DATA) 259 260 def invoke(self, arg, from_tty): 261 set_xm_status(arg, False) 262 263 264def register_xmethod_commands(): 265 """Installs the xmethod commands.""" 266 InfoXMethod() 267 EnableXMethod() 268 DisableXMethod() 269 270 271register_xmethod_commands() 272