1# Xmethods for libstdc++. 2 3# Copyright (C) 2014-2015 Free Software Foundation, Inc. 4 5# This program is free software; you can redistribute it and/or modify 6# it under the terms of the GNU General Public License as published by 7# the Free Software Foundation; either version 3 of the License, or 8# (at your option) any later version. 9# 10# This program is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18import gdb 19import gdb.xmethod 20import re 21 22matcher_name_prefix = 'libstdc++::' 23 24def get_bool_type(): 25 return gdb.lookup_type('bool') 26 27def get_std_size_type(): 28 return gdb.lookup_type('std::size_t') 29 30class LibStdCxxXMethod(gdb.xmethod.XMethod): 31 def __init__(self, name, worker_class): 32 gdb.xmethod.XMethod.__init__(self, name) 33 self.worker_class = worker_class 34 35# Xmethods for std::array 36 37class ArrayWorkerBase(gdb.xmethod.XMethodWorker): 38 def __init__(self, val_type, size): 39 self._val_type = val_type 40 self._size = size 41 42 def null_value(self): 43 nullptr = gdb.parse_and_eval('(void *) 0') 44 return nullptr.cast(self._val_type.pointer()).dereference() 45 46class ArraySizeWorker(ArrayWorkerBase): 47 def __init__(self, val_type, size): 48 ArrayWorkerBase.__init__(self, val_type, size) 49 50 def get_arg_types(self): 51 return None 52 53 def get_result_type(self, obj): 54 return get_std_size_type() 55 56 def __call__(self, obj): 57 return self._size 58 59class ArrayEmptyWorker(ArrayWorkerBase): 60 def __init__(self, val_type, size): 61 ArrayWorkerBase.__init__(self, val_type, size) 62 63 def get_arg_types(self): 64 return None 65 66 def get_result_type(self, obj): 67 return get_bool_type() 68 69 def __call__(self, obj): 70 return (int(self._size) == 0) 71 72class ArrayFrontWorker(ArrayWorkerBase): 73 def __init__(self, val_type, size): 74 ArrayWorkerBase.__init__(self, val_type, size) 75 76 def get_arg_types(self): 77 return None 78 79 def get_result_type(self, obj): 80 return self._val_type 81 82 def __call__(self, obj): 83 if int(self._size) > 0: 84 return obj['_M_elems'][0] 85 else: 86 return self.null_value() 87 88class ArrayBackWorker(ArrayWorkerBase): 89 def __init__(self, val_type, size): 90 ArrayWorkerBase.__init__(self, val_type, size) 91 92 def get_arg_types(self): 93 return None 94 95 def get_result_type(self, obj): 96 return self._val_type 97 98 def __call__(self, obj): 99 if int(self._size) > 0: 100 return obj['_M_elems'][self._size - 1] 101 else: 102 return self.null_value() 103 104class ArrayAtWorker(ArrayWorkerBase): 105 def __init__(self, val_type, size): 106 ArrayWorkerBase.__init__(self, val_type, size) 107 108 def get_arg_types(self): 109 return get_std_size_type() 110 111 def get_result_type(self, obj, index): 112 return self._val_type 113 114 def __call__(self, obj, index): 115 if int(index) >= int(self._size): 116 raise IndexError('Array index "%d" should not be >= %d.' % 117 ((int(index), self._size))) 118 return obj['_M_elems'][index] 119 120class ArraySubscriptWorker(ArrayWorkerBase): 121 def __init__(self, val_type, size): 122 ArrayWorkerBase.__init__(self, val_type, size) 123 124 def get_arg_types(self): 125 return get_std_size_type() 126 127 def get_result_type(self, obj, index): 128 return self._val_type 129 130 def __call__(self, obj, index): 131 if int(self._size) > 0: 132 return obj['_M_elems'][index] 133 else: 134 return self.null_value() 135 136class ArrayMethodsMatcher(gdb.xmethod.XMethodMatcher): 137 def __init__(self): 138 gdb.xmethod.XMethodMatcher.__init__(self, 139 matcher_name_prefix + 'array') 140 self._method_dict = { 141 'size': LibStdCxxXMethod('size', ArraySizeWorker), 142 'empty': LibStdCxxXMethod('empty', ArrayEmptyWorker), 143 'front': LibStdCxxXMethod('front', ArrayFrontWorker), 144 'back': LibStdCxxXMethod('back', ArrayBackWorker), 145 'at': LibStdCxxXMethod('at', ArrayAtWorker), 146 'operator[]': LibStdCxxXMethod('operator[]', ArraySubscriptWorker), 147 } 148 self.methods = [self._method_dict[m] for m in self._method_dict] 149 150 def match(self, class_type, method_name): 151 if not re.match('^std::array<.*>$', class_type.tag): 152 return None 153 method = self._method_dict.get(method_name) 154 if method is None or not method.enabled: 155 return None 156 try: 157 value_type = class_type.template_argument(0) 158 size = class_type.template_argument(1) 159 except: 160 return None 161 return method.worker_class(value_type, size) 162 163# Xmethods for std::deque 164 165class DequeWorkerBase(gdb.xmethod.XMethodWorker): 166 def __init__(self, val_type): 167 self._val_type = val_type 168 self._bufsize = 512 // val_type.sizeof or 1 169 170 def size(self, obj): 171 first_node = obj['_M_impl']['_M_start']['_M_node'] 172 last_node = obj['_M_impl']['_M_finish']['_M_node'] 173 cur = obj['_M_impl']['_M_finish']['_M_cur'] 174 first = obj['_M_impl']['_M_finish']['_M_first'] 175 return (last_node - first_node) * self._bufsize + (cur - first) 176 177 def index(self, obj, idx): 178 first_node = obj['_M_impl']['_M_start']['_M_node'] 179 index_node = first_node + int(idx) // self._bufsize 180 return index_node[0][idx % self._bufsize] 181 182class DequeEmptyWorker(DequeWorkerBase): 183 def get_arg_types(self): 184 return None 185 186 def get_result_type(self, obj): 187 return get_bool_type() 188 189 def __call__(self, obj): 190 return (obj['_M_impl']['_M_start']['_M_cur'] == 191 obj['_M_impl']['_M_finish']['_M_cur']) 192 193class DequeSizeWorker(DequeWorkerBase): 194 def get_arg_types(self): 195 return None 196 197 def get_result_type(self, obj): 198 return get_std_size_type() 199 200 def __call__(self, obj): 201 return self.size(obj) 202 203class DequeFrontWorker(DequeWorkerBase): 204 def get_arg_types(self): 205 return None 206 207 def get_result_type(self, obj): 208 return self._val_type 209 210 def __call__(self, obj): 211 return obj['_M_impl']['_M_start']['_M_cur'][0] 212 213class DequeBackWorker(DequeWorkerBase): 214 def get_arg_types(self): 215 return None 216 217 def get_result_type(self, obj): 218 return self._val_type 219 220 def __call__(self, obj): 221 if (obj['_M_impl']['_M_finish']['_M_cur'] == 222 obj['_M_impl']['_M_finish']['_M_first']): 223 prev_node = obj['_M_impl']['_M_finish']['_M_node'] - 1 224 return prev_node[0][self._bufsize - 1] 225 else: 226 return obj['_M_impl']['_M_finish']['_M_cur'][-1] 227 228class DequeSubscriptWorker(DequeWorkerBase): 229 def get_arg_types(self): 230 return get_std_size_type() 231 232 def get_result_type(self, obj, subscript): 233 return self._val_type 234 235 def __call__(self, obj, subscript): 236 return self.index(obj, subscript) 237 238class DequeAtWorker(DequeWorkerBase): 239 def get_arg_types(self): 240 return get_std_size_type() 241 242 def get_result_type(self, obj, index): 243 return self._val_type 244 245 def __call__(self, obj, index): 246 deque_size = int(self.size(obj)) 247 if int(index) >= deque_size: 248 raise IndexError('Deque index "%d" should not be >= %d.' % 249 (int(index), deque_size)) 250 else: 251 return self.index(obj, index) 252 253class DequeMethodsMatcher(gdb.xmethod.XMethodMatcher): 254 def __init__(self): 255 gdb.xmethod.XMethodMatcher.__init__(self, 256 matcher_name_prefix + 'deque') 257 self._method_dict = { 258 'empty': LibStdCxxXMethod('empty', DequeEmptyWorker), 259 'size': LibStdCxxXMethod('size', DequeSizeWorker), 260 'front': LibStdCxxXMethod('front', DequeFrontWorker), 261 'back': LibStdCxxXMethod('back', DequeBackWorker), 262 'operator[]': LibStdCxxXMethod('operator[]', DequeSubscriptWorker), 263 'at': LibStdCxxXMethod('at', DequeAtWorker) 264 } 265 self.methods = [self._method_dict[m] for m in self._method_dict] 266 267 def match(self, class_type, method_name): 268 if not re.match('^std::deque<.*>$', class_type.tag): 269 return None 270 method = self._method_dict.get(method_name) 271 if method is None or not method.enabled: 272 return None 273 return method.worker_class(class_type.template_argument(0)) 274 275# Xmethods for std::forward_list 276 277class ForwardListWorkerBase(gdb.xmethod.XMethodMatcher): 278 def __init__(self, val_type, node_type): 279 self._val_type = val_type 280 self._node_type = node_type 281 282 def get_arg_types(self): 283 return None 284 285class ForwardListEmptyWorker(ForwardListWorkerBase): 286 def get_result_type(self, obj): 287 return get_bool_type() 288 289 def __call__(self, obj): 290 return obj['_M_impl']['_M_head']['_M_next'] == 0 291 292class ForwardListFrontWorker(ForwardListWorkerBase): 293 def get_result_type(self, obj): 294 return self._val_type 295 296 def __call__(self, obj): 297 node = obj['_M_impl']['_M_head']['_M_next'].cast(self._node_type) 298 val_address = node['_M_storage']['_M_storage'].address 299 return val_address.cast(self._val_type.pointer()).dereference() 300 301class ForwardListMethodsMatcher(gdb.xmethod.XMethodMatcher): 302 def __init__(self): 303 matcher_name = matcher_name_prefix + 'forward_list' 304 gdb.xmethod.XMethodMatcher.__init__(self, matcher_name) 305 self._method_dict = { 306 'empty': LibStdCxxXMethod('empty', ForwardListEmptyWorker), 307 'front': LibStdCxxXMethod('front', ForwardListFrontWorker) 308 } 309 self.methods = [self._method_dict[m] for m in self._method_dict] 310 311 def match(self, class_type, method_name): 312 if not re.match('^std::forward_list<.*>$', class_type.tag): 313 return None 314 method = self._method_dict.get(method_name) 315 if method is None or not method.enabled: 316 return None 317 val_type = class_type.template_argument(0) 318 node_type = gdb.lookup_type(str(class_type) + '::_Node').pointer() 319 return method.worker_class(val_type, node_type) 320 321# Xmethods for std::list 322 323class ListWorkerBase(gdb.xmethod.XMethodWorker): 324 def __init__(self, val_type, node_type): 325 self._val_type = val_type 326 self._node_type = node_type 327 328 def get_arg_types(self): 329 return None 330 331class ListEmptyWorker(ListWorkerBase): 332 def get_result_type(self, obj): 333 return get_bool_type() 334 335 def __call__(self, obj): 336 base_node = obj['_M_impl']['_M_node'] 337 if base_node['_M_next'] == base_node.address: 338 return True 339 else: 340 return False 341 342class ListSizeWorker(ListWorkerBase): 343 def get_result_type(self, obj): 344 return get_std_size_type() 345 346 def __call__(self, obj): 347 begin_node = obj['_M_impl']['_M_node']['_M_next'] 348 end_node = obj['_M_impl']['_M_node'].address 349 size = 0 350 while begin_node != end_node: 351 begin_node = begin_node['_M_next'] 352 size += 1 353 return size 354 355class ListFrontWorker(ListWorkerBase): 356 def get_result_type(self, obj): 357 return self._val_type 358 359 def __call__(self, obj): 360 node = obj['_M_impl']['_M_node']['_M_next'].cast(self._node_type) 361 return node['_M_data'] 362 363class ListBackWorker(ListWorkerBase): 364 def get_result_type(self, obj): 365 return self._val_type 366 367 def __call__(self, obj): 368 prev_node = obj['_M_impl']['_M_node']['_M_prev'].cast(self._node_type) 369 return prev_node['_M_data'] 370 371class ListMethodsMatcher(gdb.xmethod.XMethodMatcher): 372 def __init__(self): 373 gdb.xmethod.XMethodMatcher.__init__(self, 374 matcher_name_prefix + 'list') 375 self._method_dict = { 376 'empty': LibStdCxxXMethod('empty', ListEmptyWorker), 377 'size': LibStdCxxXMethod('size', ListSizeWorker), 378 'front': LibStdCxxXMethod('front', ListFrontWorker), 379 'back': LibStdCxxXMethod('back', ListBackWorker) 380 } 381 self.methods = [self._method_dict[m] for m in self._method_dict] 382 383 def match(self, class_type, method_name): 384 if not re.match('^std::(__cxx11::)?list<.*>$', class_type.tag): 385 return None 386 method = self._method_dict.get(method_name) 387 if method is None or not method.enabled: 388 return None 389 val_type = class_type.template_argument(0) 390 node_type = gdb.lookup_type(str(class_type) + '::_Node').pointer() 391 return method.worker_class(val_type, node_type) 392 393# Xmethods for std::vector 394 395class VectorWorkerBase(gdb.xmethod.XMethodWorker): 396 def __init__(self, val_type): 397 self._val_type = val_type 398 399 def size(self, obj): 400 if self._val_type.code == gdb.TYPE_CODE_BOOL: 401 start = obj['_M_impl']['_M_start']['_M_p'] 402 finish = obj['_M_impl']['_M_finish']['_M_p'] 403 finish_offset = obj['_M_impl']['_M_finish']['_M_offset'] 404 bit_size = start.dereference().type.sizeof * 8 405 return (finish - start) * bit_size + finish_offset 406 else: 407 return obj['_M_impl']['_M_finish'] - obj['_M_impl']['_M_start'] 408 409 def get(self, obj, index): 410 if self._val_type.code == gdb.TYPE_CODE_BOOL: 411 start = obj['_M_impl']['_M_start']['_M_p'] 412 bit_size = start.dereference().type.sizeof * 8 413 valp = start + index // bit_size 414 offset = index % bit_size 415 return (valp.dereference() & (1 << offset)) > 0 416 else: 417 return obj['_M_impl']['_M_start'][index] 418 419class VectorEmptyWorker(VectorWorkerBase): 420 def get_arg_types(self): 421 return None 422 423 def get_result_type(self, obj): 424 return get_bool_type() 425 426 def __call__(self, obj): 427 return int(self.size(obj)) == 0 428 429class VectorSizeWorker(VectorWorkerBase): 430 def get_arg_types(self): 431 return None 432 433 def get_result_type(self, obj): 434 return get_std_size_type() 435 436 def __call__(self, obj): 437 return self.size(obj) 438 439class VectorFrontWorker(VectorWorkerBase): 440 def get_arg_types(self): 441 return None 442 443 def get_result_type(self, obj): 444 return self._val_type 445 446 def __call__(self, obj): 447 return self.get(obj, 0) 448 449class VectorBackWorker(VectorWorkerBase): 450 def get_arg_types(self): 451 return None 452 453 def get_result_type(self, obj): 454 return self._val_type 455 456 def __call__(self, obj): 457 return self.get(obj, int(self.size(obj)) - 1) 458 459class VectorAtWorker(VectorWorkerBase): 460 def get_arg_types(self): 461 return get_std_size_type() 462 463 def get_result_type(self, obj, index): 464 return self._val_type 465 466 def __call__(self, obj, index): 467 size = int(self.size(obj)) 468 if int(index) >= size: 469 raise IndexError('Vector index "%d" should not be >= %d.' % 470 ((int(index), size))) 471 return self.get(obj, int(index)) 472 473class VectorSubscriptWorker(VectorWorkerBase): 474 def get_arg_types(self): 475 return get_std_size_type() 476 477 def get_result_type(self, obj, subscript): 478 return self._val_type 479 480 def __call__(self, obj, subscript): 481 return self.get(obj, int(subscript)) 482 483class VectorMethodsMatcher(gdb.xmethod.XMethodMatcher): 484 def __init__(self): 485 gdb.xmethod.XMethodMatcher.__init__(self, 486 matcher_name_prefix + 'vector') 487 self._method_dict = { 488 'size': LibStdCxxXMethod('size', VectorSizeWorker), 489 'empty': LibStdCxxXMethod('empty', VectorEmptyWorker), 490 'front': LibStdCxxXMethod('front', VectorFrontWorker), 491 'back': LibStdCxxXMethod('back', VectorBackWorker), 492 'at': LibStdCxxXMethod('at', VectorAtWorker), 493 'operator[]': LibStdCxxXMethod('operator[]', 494 VectorSubscriptWorker), 495 } 496 self.methods = [self._method_dict[m] for m in self._method_dict] 497 498 def match(self, class_type, method_name): 499 if not re.match('^std::vector<.*>$', class_type.tag): 500 return None 501 method = self._method_dict.get(method_name) 502 if method is None or not method.enabled: 503 return None 504 return method.worker_class(class_type.template_argument(0)) 505 506# Xmethods for associative containers 507 508class AssociativeContainerWorkerBase(gdb.xmethod.XMethodWorker): 509 def __init__(self, unordered): 510 self._unordered = unordered 511 512 def node_count(self, obj): 513 if self._unordered: 514 return obj['_M_h']['_M_element_count'] 515 else: 516 return obj['_M_t']['_M_impl']['_M_node_count'] 517 518 def get_arg_types(self): 519 return None 520 521class AssociativeContainerEmptyWorker(AssociativeContainerWorkerBase): 522 def get_result_type(self, obj): 523 return get_bool_type() 524 525 def __call__(self, obj): 526 return int(self.node_count(obj)) == 0 527 528class AssociativeContainerSizeWorker(AssociativeContainerWorkerBase): 529 def get_result_type(self, obj): 530 return get_std_size_type() 531 532 def __call__(self, obj): 533 return self.node_count(obj) 534 535class AssociativeContainerMethodsMatcher(gdb.xmethod.XMethodMatcher): 536 def __init__(self, name): 537 gdb.xmethod.XMethodMatcher.__init__(self, 538 matcher_name_prefix + name) 539 self._name = name 540 self._method_dict = { 541 'size': LibStdCxxXMethod('size', AssociativeContainerSizeWorker), 542 'empty': LibStdCxxXMethod('empty', 543 AssociativeContainerEmptyWorker), 544 } 545 self.methods = [self._method_dict[m] for m in self._method_dict] 546 547 def match(self, class_type, method_name): 548 if not re.match('^std::%s<.*>$' % self._name, class_type.tag): 549 return None 550 method = self._method_dict.get(method_name) 551 if method is None or not method.enabled: 552 return None 553 unordered = 'unordered' in self._name 554 return method.worker_class(unordered) 555 556# Xmethods for std::unique_ptr 557 558class UniquePtrGetWorker(gdb.xmethod.XMethodWorker): 559 def __init__(self, elem_type): 560 self._elem_type = elem_type 561 562 def get_arg_types(self): 563 return None 564 565 def get_result_type(self, obj): 566 return self._elem_type.pointer() 567 568 def __call__(self, obj): 569 return obj['_M_t']['_M_head_impl'] 570 571class UniquePtrDerefWorker(UniquePtrGetWorker): 572 def __init__(self, elem_type): 573 UniquePtrGetWorker.__init__(self, elem_type) 574 575 def get_result_type(self, obj): 576 return self._elem_type 577 578 def __call__(self, obj): 579 return UniquePtrGetWorker.__call__(self, obj).dereference() 580 581class UniquePtrMethodsMatcher(gdb.xmethod.XMethodMatcher): 582 def __init__(self): 583 gdb.xmethod.XMethodMatcher.__init__(self, 584 matcher_name_prefix + 'unique_ptr') 585 self._method_dict = { 586 'get': LibStdCxxXMethod('get', UniquePtrGetWorker), 587 'operator*': LibStdCxxXMethod('operator*', UniquePtrDerefWorker), 588 } 589 self.methods = [self._method_dict[m] for m in self._method_dict] 590 591 def match(self, class_type, method_name): 592 if not re.match('^std::unique_ptr<.*>$', class_type.tag): 593 return None 594 method = self._method_dict.get(method_name) 595 if method is None or not method.enabled: 596 return None 597 return method.worker_class(class_type.template_argument(0)) 598 599def register_libstdcxx_xmethods(locus): 600 gdb.xmethod.register_xmethod_matcher(locus, ArrayMethodsMatcher()) 601 gdb.xmethod.register_xmethod_matcher(locus, ForwardListMethodsMatcher()) 602 gdb.xmethod.register_xmethod_matcher(locus, DequeMethodsMatcher()) 603 gdb.xmethod.register_xmethod_matcher(locus, ListMethodsMatcher()) 604 gdb.xmethod.register_xmethod_matcher(locus, VectorMethodsMatcher()) 605 gdb.xmethod.register_xmethod_matcher( 606 locus, AssociativeContainerMethodsMatcher('set')) 607 gdb.xmethod.register_xmethod_matcher( 608 locus, AssociativeContainerMethodsMatcher('map')) 609 gdb.xmethod.register_xmethod_matcher( 610 locus, AssociativeContainerMethodsMatcher('multiset')) 611 gdb.xmethod.register_xmethod_matcher( 612 locus, AssociativeContainerMethodsMatcher('multimap')) 613 gdb.xmethod.register_xmethod_matcher( 614 locus, AssociativeContainerMethodsMatcher('unordered_set')) 615 gdb.xmethod.register_xmethod_matcher( 616 locus, AssociativeContainerMethodsMatcher('unordered_map')) 617 gdb.xmethod.register_xmethod_matcher( 618 locus, AssociativeContainerMethodsMatcher('unordered_multiset')) 619 gdb.xmethod.register_xmethod_matcher( 620 locus, AssociativeContainerMethodsMatcher('unordered_multimap')) 621 gdb.xmethod.register_xmethod_matcher(locus, UniquePtrMethodsMatcher()) 622