1############################################################################ 2# 3# Copyright (C) 2016 The Qt Company Ltd. 4# Contact: https://www.qt.io/licensing/ 5# 6# This file is part of Qt Creator. 7# 8# Commercial License Usage 9# Licensees holding valid commercial Qt licenses may use this file in 10# accordance with the commercial license agreement provided with the 11# Software or, alternatively, in accordance with the terms contained in 12# a written agreement between you and The Qt Company. For licensing terms 13# and conditions see https://www.qt.io/terms-conditions. For further 14# information use the contact form at https://www.qt.io/contact-us. 15# 16# GNU General Public License Usage 17# Alternatively, this file may be used under the terms of the GNU 18# General Public License version 3 as published by the Free Software 19# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 20# included in the packaging of this file. Please review the following 21# information to ensure the GNU General Public License requirements will 22# be met: https://www.gnu.org/licenses/gpl-3.0.html. 23# 24############################################################################ 25 26try: 27 import __builtin__ 28except: 29 import builtins 30 31import gdb 32import os 33import os.path 34import re 35import sys 36import struct 37import tempfile 38 39from dumper import DumperBase, Children, toInteger, TopLevelItem 40from utils import TypeCode 41from gdbtracepoint import * 42 43 44####################################################################### 45# 46# Infrastructure 47# 48####################################################################### 49 50 51def safePrint(output): 52 try: 53 print(output) 54 except: 55 out = '' 56 for c in output: 57 cc = ord(c) 58 if cc > 127: 59 out += '\\\\%d' % cc 60 elif cc < 0: 61 out += '\\\\%d' % (cc + 256) 62 else: 63 out += c 64 print(out) 65 66 67def registerCommand(name, func): 68 69 class Command(gdb.Command): 70 def __init__(self): 71 super(Command, self).__init__(name, gdb.COMMAND_OBSCURE) 72 73 def invoke(self, args, from_tty): 74 safePrint(func(args)) 75 76 Command() 77 78 79####################################################################### 80# 81# Convenience 82# 83####################################################################### 84 85# For CLI dumper use, see README.txt 86class PPCommand(gdb.Command): 87 def __init__(self): 88 super(PPCommand, self).__init__('pp', gdb.COMMAND_OBSCURE) 89 90 def invoke(self, args, from_tty): 91 print(theCliDumper.fetchVariable(args)) 92 93 94PPCommand() 95 96# Just convenience for 'python print gdb.parse_and_eval(...)' 97 98 99class PPPCommand(gdb.Command): 100 def __init__(self): 101 super(PPPCommand, self).__init__('ppp', gdb.COMMAND_OBSCURE) 102 103 def invoke(self, args, from_tty): 104 print(gdb.parse_and_eval(args)) 105 106 107PPPCommand() 108 109 110def scanStack(p, n): 111 p = int(p) 112 r = [] 113 for i in range(n): 114 f = gdb.parse_and_eval('{void*}%s' % p) 115 m = gdb.execute('info symbol %s' % f, to_string=True) 116 if not m.startswith('No symbol matches'): 117 r.append(m) 118 p += f.type.sizeof 119 return r 120 121 122class ScanStackCommand(gdb.Command): 123 def __init__(self): 124 super(ScanStackCommand, self).__init__('scanStack', gdb.COMMAND_OBSCURE) 125 126 def invoke(self, args, from_tty): 127 if len(args) == 0: 128 args = 20 129 safePrint(scanStack(gdb.parse_and_eval('$sp'), int(args))) 130 131 132ScanStackCommand() 133 134 135####################################################################### 136# 137# Import plain gdb pretty printers 138# 139####################################################################### 140 141class PlainDumper(): 142 def __init__(self, printer): 143 self.printer = printer 144 self.typeCache = {} 145 146 def __call__(self, d, value): 147 if value.nativeValue is None: 148 # warn('PlainDumper(gdb): value.nativeValue is missing (%s)'%value) 149 nativeType = theDumper.lookupNativeType(value.type.name) 150 nativeTypePointer = nativeType.pointer() 151 nativePointer = gdb.Value(value.laddress) 152 value.nativeValue = nativePointer.cast(nativeTypePointer).dereference() 153 try: 154 printer = self.printer.gen_printer(value.nativeValue) 155 except: 156 printer = self.printer.invoke(value.nativeValue) 157 d.putType(value.nativeValue.type.name) 158 val = printer.to_string() 159 if isinstance(val, str): 160 # encode and avoid extra quotes ('"') at beginning and end 161 d.putValue(d.hexencode(val), 'utf8:1:0') 162 elif sys.version_info[0] <= 2 and isinstance(val, unicode): 163 d.putValue(val) 164 elif val is not None: # Assuming LazyString 165 d.putCharArrayValue(val.address, val.length, 166 val.type.target().sizeof) 167 168 lister = getattr(printer, 'children', None) 169 if lister is None: 170 d.putNumChild(0) 171 else: 172 if d.isExpanded(): 173 children = lister() 174 with Children(d): 175 i = 0 176 for (name, child) in children: 177 d.putSubItem(name, d.fromNativeValue(child)) 178 i += 1 179 if i > 1000: 180 break 181 d.putNumChild(1) 182 183 184def importPlainDumpers(args): 185 if args == 'off': 186 theDumper.usePlainDumpers = False 187 try: 188 gdb.execute('disable pretty-printer .* .*') 189 except: 190 # Might occur in non-ASCII directories 191 DumperBase.warn('COULD NOT DISABLE PRETTY PRINTERS') 192 else: 193 theDumper.usePlainDumpers = True 194 theDumper.importPlainDumpers() 195 196 197registerCommand('importPlainDumpers', importPlainDumpers) 198 199 200class OutputSaver(): 201 def __init__(self, d): 202 self.d = d 203 204 def __enter__(self): 205 self.savedOutput = self.d.output 206 self.d.output = '' 207 208 def __exit__(self, exType, exValue, exTraceBack): 209 if self.d.passExceptions and exType is not None: 210 self.d.showException('OUTPUTSAVER', exType, exValue, exTraceBack) 211 self.d.output = self.savedOutput 212 else: 213 self.savedOutput += self.d.output 214 self.d.output = self.savedOutput 215 return False 216 217 218####################################################################### 219# 220# The Dumper Class 221# 222####################################################################### 223 224 225class Dumper(DumperBase): 226 227 def __init__(self): 228 DumperBase.__init__(self) 229 230 # whether to load plain dumpers for objfiles 231 self.usePlainDumpers = False 232 233 # These values will be kept between calls to 'fetchVariables'. 234 self.isGdb = True 235 self.typeCache = {} 236 self.interpreterBreakpointResolvers = [] 237 238 def prepare(self, args): 239 self.output = '' 240 self.setVariableFetchingOptions(args) 241 242 def fromFrameValue(self, nativeValue): 243 #DumperBase.warn('FROM FRAME VALUE: %s' % nativeValue.address) 244 val = nativeValue 245 if self.useDynamicType: 246 try: 247 val = nativeValue.cast(nativeValue.dynamic_type) 248 except: 249 pass 250 return self.fromNativeValue(val) 251 252 def nativeValueType(self, nativeValue): 253 return self.fromNativeType(nativeValue.type) 254 255 def fromNativeValue(self, nativeValue): 256 #DumperBase.warn('FROM NATIVE VALUE: %s' % nativeValue) 257 self.check(isinstance(nativeValue, gdb.Value)) 258 nativeType = nativeValue.type 259 code = nativeType.code 260 if code == gdb.TYPE_CODE_REF: 261 targetType = self.fromNativeType(nativeType.target().unqualified()) 262 val = self.createReferenceValue(toInteger(nativeValue.address), targetType) 263 val.nativeValue = nativeValue 264 #DumperBase.warn('CREATED REF: %s' % val) 265 return val 266 if code == gdb.TYPE_CODE_PTR: 267 try: 268 nativeTargetValue = nativeValue.dereference() 269 except: 270 nativeTargetValue = None 271 targetType = self.fromNativeType(nativeType.target().unqualified()) 272 val = self.createPointerValue(toInteger(nativeValue), targetType) 273 # The nativeValue is needed in case of multiple inheritance, see 274 # QTCREATORBUG-17823. Using it triggers nativeValueDereferencePointer() 275 # later which 276 # is surprisingly expensive. 277 val.nativeValue = nativeValue 278 #DumperBase.warn('CREATED PTR 1: %s' % val) 279 if nativeValue.address is not None: 280 val.laddress = toInteger(nativeValue.address) 281 #DumperBase.warn('CREATED PTR 2: %s' % val) 282 return val 283 if code == gdb.TYPE_CODE_TYPEDEF: 284 targetType = nativeType.strip_typedefs().unqualified() 285 #DumperBase.warn('TARGET TYPE: %s' % targetType) 286 if targetType.code == gdb.TYPE_CODE_ARRAY: 287 val = self.Value(self) 288 else: 289 try: 290 # Cast may fail for arrays, for typedefs to __uint128_t with 291 # gdb.error: That operation is not available on integers 292 # of more than 8 bytes. 293 # See test for Bug5799, QTCREATORBUG-18450. 294 val = self.fromNativeValue(nativeValue.cast(targetType)) 295 except: 296 val = self.Value(self) 297 #DumperBase.warn('CREATED TYPEDEF: %s' % val) 298 else: 299 val = self.Value(self) 300 301 val.nativeValue = nativeValue 302 if nativeValue.address is not None: 303 val.laddress = toInteger(nativeValue.address) 304 else: 305 size = nativeType.sizeof 306 chars = self.lookupNativeType('unsigned char') 307 y = nativeValue.cast(chars.array(0, int(nativeType.sizeof - 1))) 308 buf = bytearray(struct.pack('x' * size)) 309 for i in range(size): 310 try: 311 buf[i] = int(y[i]) 312 except: 313 pass 314 val.ldata = bytes(buf) 315 316 val._type = self.fromNativeType(nativeType) 317 val.lIsInScope = not nativeValue.is_optimized_out 318 code = nativeType.code 319 if code == gdb.TYPE_CODE_ENUM: 320 val.ldisplay = str(nativeValue) 321 intval = int(nativeValue) 322 if val.ldisplay != intval: 323 val.ldisplay += ' (%s)' % intval 324 elif code == gdb.TYPE_CODE_COMPLEX: 325 val.ldisplay = str(nativeValue) 326 elif code in [gdb.TYPE_CODE_BOOL, gdb.TYPE_CODE_INT]: 327 try: 328 # extract int presentation from native value and remember it 329 val.lvalue = int(nativeValue) 330 except: 331 # GDB only support converting integers of max. 64 bits to Python int as of now 332 pass 333 #elif code == gdb.TYPE_CODE_ARRAY: 334 # val.type.ltarget = nativeValue[0].type.unqualified() 335 return val 336 337 def ptrSize(self): 338 result = gdb.lookup_type('void').pointer().sizeof 339 self.ptrSize = lambda: result 340 return result 341 342 def fromNativeType(self, nativeType): 343 self.check(isinstance(nativeType, gdb.Type)) 344 code = nativeType.code 345 #DumperBase.warn('FROM NATIVE TYPE: %s' % nativeType) 346 nativeType = nativeType.unqualified() 347 348 if code == gdb.TYPE_CODE_PTR: 349 #DumperBase.warn('PTR') 350 targetType = self.fromNativeType(nativeType.target().unqualified()) 351 return self.createPointerType(targetType) 352 353 if code == gdb.TYPE_CODE_REF: 354 #DumperBase.warn('REF') 355 targetType = self.fromNativeType(nativeType.target().unqualified()) 356 return self.createReferenceType(targetType) 357 358 if hasattr(gdb, "TYPE_CODE_RVALUE_REF"): 359 if code == gdb.TYPE_CODE_RVALUE_REF: 360 #DumperBase.warn('RVALUEREF') 361 targetType = self.fromNativeType(nativeType.target()) 362 return self.createRValueReferenceType(targetType) 363 364 if code == gdb.TYPE_CODE_ARRAY: 365 #DumperBase.warn('ARRAY') 366 nativeTargetType = nativeType.target().unqualified() 367 targetType = self.fromNativeType(nativeTargetType) 368 if nativeType.sizeof == 0: 369 # QTCREATORBUG-23998, note that nativeType.name == None here, 370 # whereas str(nativeType) returns sth like 'QObject [5]' 371 count = self.arrayItemCountFromTypeName(str(nativeType), 1) 372 else: 373 count = nativeType.sizeof // nativeTargetType.sizeof 374 return self.createArrayType(targetType, count) 375 376 if code == gdb.TYPE_CODE_TYPEDEF: 377 #DumperBase.warn('TYPEDEF') 378 nativeTargetType = nativeType.unqualified() 379 while nativeTargetType.code == gdb.TYPE_CODE_TYPEDEF: 380 nativeTargetType = nativeTargetType.strip_typedefs().unqualified() 381 targetType = self.fromNativeType(nativeTargetType) 382 return self.createTypedefedType(targetType, str(nativeType), 383 self.nativeTypeId(nativeType)) 384 385 if code == gdb.TYPE_CODE_ERROR: 386 self.warn('Type error: %s' % nativeType) 387 return self.Type(self, '') 388 389 typeId = self.nativeTypeId(nativeType) 390 res = self.typeData.get(typeId, None) 391 if res is None: 392 tdata = self.TypeData(self) 393 tdata.name = str(nativeType) 394 tdata.typeId = typeId 395 tdata.lbitsize = nativeType.sizeof * 8 396 tdata.code = { 397 #gdb.TYPE_CODE_TYPEDEF : TypeCode.Typedef, # Handled above. 398 gdb.TYPE_CODE_METHOD: TypeCode.Function, 399 gdb.TYPE_CODE_VOID: TypeCode.Void, 400 gdb.TYPE_CODE_FUNC: TypeCode.Function, 401 gdb.TYPE_CODE_METHODPTR: TypeCode.Function, 402 gdb.TYPE_CODE_MEMBERPTR: TypeCode.Function, 403 #gdb.TYPE_CODE_PTR : TypeCode.Pointer, # Handled above. 404 #gdb.TYPE_CODE_REF : TypeCode.Reference, # Handled above. 405 gdb.TYPE_CODE_BOOL: TypeCode.Integral, 406 gdb.TYPE_CODE_CHAR: TypeCode.Integral, 407 gdb.TYPE_CODE_INT: TypeCode.Integral, 408 gdb.TYPE_CODE_FLT: TypeCode.Float, 409 gdb.TYPE_CODE_ENUM: TypeCode.Enum, 410 #gdb.TYPE_CODE_ARRAY : TypeCode.Array, 411 gdb.TYPE_CODE_STRUCT: TypeCode.Struct, 412 gdb.TYPE_CODE_UNION: TypeCode.Struct, 413 gdb.TYPE_CODE_COMPLEX: TypeCode.Complex, 414 gdb.TYPE_CODE_STRING: TypeCode.FortranString, 415 }[code] 416 if tdata.code == TypeCode.Enum: 417 tdata.enumDisplay = lambda intval, addr, form: \ 418 self.nativeTypeEnumDisplay(nativeType, intval, form) 419 if tdata.code == TypeCode.Struct: 420 tdata.lalignment = lambda: \ 421 self.nativeStructAlignment(nativeType) 422 tdata.lfields = lambda value: \ 423 self.listMembers(value, nativeType) 424 tdata.templateArguments = self.listTemplateParameters(nativeType) 425 self.registerType(typeId, tdata) # Fix up fields and template args 426 # warn('CREATE TYPE: %s' % typeId) 427 #else: 428 # warn('REUSE TYPE: %s' % typeId) 429 return self.Type(self, typeId) 430 431 def listTemplateParameters(self, nativeType): 432 targs = [] 433 pos = 0 434 while True: 435 try: 436 targ = nativeType.template_argument(pos) 437 except: 438 break 439 if isinstance(targ, gdb.Type): 440 targs.append(self.fromNativeType(targ.unqualified())) 441 elif isinstance(targ, gdb.Value): 442 targs.append(self.fromNativeValue(targ).value()) 443 else: 444 raise RuntimeError('UNKNOWN TEMPLATE PARAMETER') 445 pos += 1 446 targs2 = self.listTemplateParametersManually(str(nativeType)) 447 return targs if len(targs) >= len(targs2) else targs2 448 449 def nativeTypeEnumDisplay(self, nativeType, intval, form): 450 try: 451 enumerators = [] 452 for field in nativeType.fields(): 453 # If we found an exact match, return it immediately 454 if field.enumval == intval: 455 return field.name + ' (' + (form % intval) + ')' 456 enumerators.append((field.name, field.enumval)) 457 458 # No match was found, try to return as flags 459 enumerators.sort(key=lambda x: x[1]) 460 flags = [] 461 v = intval 462 found = False 463 for (name, value) in enumerators: 464 if v & value != 0: 465 flags.append(name) 466 v = v & ~value 467 found = True 468 if not found or v != 0: 469 # Leftover value 470 flags.append('unknown: %d' % v) 471 return '(' + " | ".join(flags) + ') (' + (form % intval) + ')' 472 except: 473 pass 474 return form % intval 475 476 def nativeTypeId(self, nativeType): 477 if nativeType and (nativeType.code == gdb.TYPE_CODE_TYPEDEF): 478 return '%s{%s}' % (nativeType, nativeType.strip_typedefs()) 479 name = str(nativeType) 480 if len(name) == 0: 481 c = '0' 482 elif name == 'union {...}': 483 c = 'u' 484 elif name.endswith('{...}'): 485 c = 's' 486 else: 487 return name 488 typeId = c + ''.join(['{%s:%s}' % (f.name, self.nativeTypeId(f.type)) 489 for f in nativeType.fields()]) 490 return typeId 491 492 def nativeStructAlignment(self, nativeType): 493 #DumperBase.warn('NATIVE ALIGN FOR %s' % nativeType.name) 494 def handleItem(nativeFieldType, align): 495 a = self.fromNativeType(nativeFieldType).alignment() 496 return a if a > align else align 497 align = 1 498 for f in nativeType.fields(): 499 align = handleItem(f.type, align) 500 return align 501 502 #except: 503 # # Happens in the BoostList dumper for a 'const bool' 504 # # item named 'constant_time_size'. There isn't anything we can do 505 # # in this case. 506 # pass 507 508 #yield value.extractField(field) 509 510 def memberFromNativeFieldAndValue(self, nativeField, nativeValue, fieldName, value): 511 nativeMember = self.nativeMemberFromField(nativeValue, nativeField) 512 if nativeMember is None: 513 val = self.Value(self) 514 val.name = fieldName 515 val._type = self.fromNativeType(nativeField.type) 516 val.lIsInScope = False 517 return val 518 val = self.fromNativeValue(nativeMember) 519 nativeFieldType = nativeField.type.unqualified() 520 if nativeField.bitsize: 521 val.lvalue = int(nativeMember) 522 val.laddress = None 523 fieldType = self.fromNativeType(nativeFieldType) 524 val._type = self.createBitfieldType(fieldType, nativeField.bitsize) 525 val.isBaseClass = nativeField.is_base_class 526 val.name = fieldName 527 return val 528 529 def nativeMemberFromField(self, nativeValue, nativeField): 530 if nativeField.is_base_class: 531 return nativeValue.cast(nativeField.type) 532 try: 533 return nativeValue[nativeField] 534 except: 535 pass 536 try: 537 return nativeValue[nativeField.name] 538 except: 539 pass 540 return None 541 542 def listMembers(self, value, nativeType): 543 nativeValue = value.nativeValue 544 545 anonNumber = 0 546 547 #DumperBase.warn('LISTING FIELDS FOR %s' % nativeType) 548 for nativeField in nativeType.fields(): 549 fieldName = nativeField.name 550 # Something without a name. 551 # Anonymous union? We need a dummy name to distinguish 552 # multiple anonymous unions in the struct. 553 # Since GDB commit b5b08fb4 anonymous structs get also reported 554 # with a 'None' name. 555 if fieldName is None or len(fieldName) == 0: 556 # Something without a name. 557 # Anonymous union? We need a dummy name to distinguish 558 # multiple anonymous unions in the struct. 559 anonNumber += 1 560 fieldName = '#%s' % anonNumber 561 #DumperBase.warn('FIELD: %s' % fieldName) 562 # hasattr(nativeField, 'bitpos') == False indicates a static field, 563 # but if we have access to a nativeValue .fromNativeField will 564 # also succeed. We essentially skip only static members from 565 # artificial values, like array members constructed from address. 566 if hasattr(nativeField, 'bitpos') or nativeValue is not None: 567 yield self.fromNativeField(nativeField, nativeValue, fieldName) 568 569 def fromNativeField(self, nativeField, nativeValue, fieldName): 570 nativeFieldType = nativeField.type.unqualified() 571 #DumperBase.warn(' TYPE: %s' % nativeFieldType) 572 #DumperBase.warn(' TYPEID: %s' % self.nativeTypeId(nativeFieldType)) 573 574 if hasattr(nativeField, 'bitpos'): 575 bitpos = nativeField.bitpos 576 else: 577 bitpos = 0 578 579 if hasattr(nativeField, 'bitsize') and nativeField.bitsize != 0: 580 bitsize = nativeField.bitsize 581 else: 582 bitsize = 8 * nativeFieldType.sizeof 583 584 fieldType = self.fromNativeType(nativeFieldType) 585 if bitsize != nativeFieldType.sizeof * 8: 586 fieldType = self.createBitfieldType(fieldType, bitsize) 587 else: 588 fieldType = fieldType 589 590 if nativeValue is None: 591 extractor = None 592 else: 593 extractor = lambda value, \ 594 capturedNativeField = nativeField, \ 595 capturedNativeValue = nativeValue, \ 596 capturedFieldName = fieldName: \ 597 self.memberFromNativeFieldAndValue(capturedNativeField, 598 capturedNativeValue, 599 capturedFieldName, 600 value) 601 602 #DumperBase.warn("FOUND NATIVE FIELD: %s bitpos: %s" % (fieldName, bitpos)) 603 return self.Field(dumper=self, name=fieldName, isBase=nativeField.is_base_class, 604 bitsize=bitsize, bitpos=bitpos, type=fieldType, 605 extractor=extractor) 606 607 def listLocals(self, partialVar): 608 frame = gdb.selected_frame() 609 610 try: 611 block = frame.block() 612 #DumperBase.warn('BLOCK: %s ' % block) 613 except RuntimeError as error: 614 #DumperBase.warn('BLOCK IN FRAME NOT ACCESSIBLE: %s' % error) 615 return [] 616 except: 617 self.warn('BLOCK NOT ACCESSIBLE FOR UNKNOWN REASONS') 618 return [] 619 620 items = [] 621 shadowed = {} 622 while True: 623 if block is None: 624 self.warn("UNEXPECTED 'None' BLOCK") 625 break 626 for symbol in block: 627 628 # Filter out labels etc. 629 if symbol.is_variable or symbol.is_argument: 630 name = symbol.print_name 631 632 if name in ('__in_chrg', '__PRETTY_FUNCTION__'): 633 continue 634 635 if partialVar is not None and partialVar != name: 636 continue 637 638 # 'NotImplementedError: Symbol type not yet supported in 639 # Python scripts.' 640 #DumperBase.warn('SYMBOL %s (%s, %s)): ' % (symbol, name, symbol.name)) 641 if self.passExceptions and not self.isTesting: 642 nativeValue = frame.read_var(name, block) 643 value = self.fromFrameValue(nativeValue) 644 value.name = name 645 #DumperBase.warn('READ 0: %s' % value.stringify()) 646 items.append(value) 647 continue 648 649 try: 650 # Same as above, but for production. 651 nativeValue = frame.read_var(name, block) 652 value = self.fromFrameValue(nativeValue) 653 value.name = name 654 #DumperBase.warn('READ 1: %s' % value.stringify()) 655 items.append(value) 656 continue 657 except: 658 pass 659 660 try: 661 #DumperBase.warn('READ 2: %s' % item.value) 662 value = self.fromFrameValue(frame.read_var(name)) 663 value.name = name 664 items.append(value) 665 continue 666 except: 667 # RuntimeError: happens for 668 # void foo() { std::string s; std::wstring w; } 669 # ValueError: happens for (as of 2010/11/4) 670 # a local struct as found e.g. in 671 # gcc sources in gcc.c, int execute() 672 pass 673 674 try: 675 #DumperBase.warn('READ 3: %s %s' % (name, item.value)) 676 #DumperBase.warn('ITEM 3: %s' % item.value) 677 value = self.fromFrameValue(gdb.parse_and_eval(name)) 678 value.name = name 679 items.append(value) 680 except: 681 # Can happen in inlined code (see last line of 682 # RowPainter::paintChars(): 'RuntimeError: 683 # No symbol '__val' in current context.\n' 684 pass 685 686 # The outermost block in a function has the function member 687 # FIXME: check whether this is guaranteed. 688 if block.function is not None: 689 break 690 691 block = block.superblock 692 693 return items 694 695 def reportToken(self, args): 696 pass 697 698 # Hack to avoid QDate* dumper timeouts with GDB 7.4 on 32 bit 699 # due to misaligned %ebx in SSE calls (qstring.cpp:findChar) 700 # This seems to be fixed in 7.9 (or earlier) 701 def canCallLocale(self): 702 return self.ptrSize() == 8 703 704 def fetchVariables(self, args): 705 self.resetStats() 706 self.prepare(args) 707 708 self.isBigEndian = gdb.execute('show endian', to_string=True).find('big endian') > 0 709 self.packCode = '>' if self.isBigEndian else '<' 710 711 (ok, res) = self.tryFetchInterpreterVariables(args) 712 if ok: 713 safePrint(res) 714 return 715 716 self.output += 'data=[' 717 718 partialVar = args.get('partialvar', '') 719 isPartial = len(partialVar) > 0 720 partialName = partialVar.split('.')[1].split('@')[0] if isPartial else None 721 722 variables = self.listLocals(partialName) 723 #DumperBase.warn('VARIABLES: %s' % variables) 724 725 # Take care of the return value of the last function call. 726 if len(self.resultVarName) > 0: 727 try: 728 value = self.parseAndEvaluate(self.resultVarName) 729 value.name = self.resultVarName 730 value.iname = 'return.' + self.resultVarName 731 variables.append(value) 732 except: 733 # Don't bother. It's only supplementary information anyway. 734 pass 735 736 self.handleLocals(variables) 737 self.handleWatches(args) 738 739 self.output += '],typeinfo=[' 740 for name in self.typesToReport.keys(): 741 typeobj = self.typesToReport[name] 742 # Happens e.g. for '(anonymous namespace)::InsertDefOperation' 743 #if not typeobj is None: 744 # self.output.append('{name="%s",size="%s"}' 745 # % (self.hexencode(name), typeobj.sizeof)) 746 self.output += ']' 747 self.typesToReport = {} 748 749 if self.forceQtNamespace: 750 self.qtNamespaceToReport = self.qtNamespace() 751 752 if self.qtNamespaceToReport: 753 self.output += ',qtnamespace="%s"' % self.qtNamespaceToReport 754 self.qtNamespaceToReport = None 755 756 self.output += ',partial="%d"' % isPartial 757 self.output += ',counts=%s' % self.counts 758 self.output += ',timings=%s' % self.timings 759 self.reportResult(self.output) 760 761 def parseAndEvaluate(self, exp): 762 val = self.nativeParseAndEvaluate(exp) 763 return None if val is None else self.fromNativeValue(val) 764 765 def nativeParseAndEvaluate(self, exp): 766 #DumperBase.warn('EVALUATE "%s"' % exp) 767 try: 768 val = gdb.parse_and_eval(exp) 769 return val 770 except RuntimeError as error: 771 if self.passExceptions: 772 self.warn("Cannot evaluate '%s': %s" % (exp, error)) 773 return None 774 775 def callHelper(self, rettype, value, function, args): 776 if self.isWindowsTarget(): 777 raise Exception("gdb crashes when calling functions on Windows") 778 # args is a tuple. 779 arg = '' 780 for i in range(len(args)): 781 if i: 782 arg += ',' 783 a = args[i] 784 if (':' in a) and not ("'" in a): 785 arg = "'%s'" % a 786 else: 787 arg += a 788 789 #DumperBase.warn('CALL: %s -> %s(%s)' % (value, function, arg)) 790 typeName = value.type.name 791 if typeName.find(':') >= 0: 792 typeName = "'" + typeName + "'" 793 # 'class' is needed, see http://sourceware.org/bugzilla/show_bug.cgi?id=11912 794 #exp = '((class %s*)%s)->%s(%s)' % (typeName, value.laddress, function, arg) 795 addr = value.address() 796 if addr is None: 797 addr = self.pokeValue(value) 798 #DumperBase.warn('PTR: %s -> %s(%s)' % (value, function, addr)) 799 exp = '((%s*)0x%x)->%s(%s)' % (typeName, addr, function, arg) 800 #DumperBase.warn('CALL: %s' % exp) 801 result = gdb.parse_and_eval(exp) 802 #DumperBase.warn(' -> %s' % result) 803 res = self.fromNativeValue(result) 804 if value.address() is None: 805 self.releaseValue(addr) 806 return res 807 808 def makeExpression(self, value): 809 typename = '::' + value.type.name 810 #DumperBase.warn(' TYPE: %s' % typename) 811 exp = '(*(%s*)(0x%x))' % (typename, value.address()) 812 #DumperBase.warn(' EXP: %s' % exp) 813 return exp 814 815 def makeStdString(init): 816 # Works only for small allocators, but they are usually empty. 817 gdb.execute('set $d=(std::string*)calloc(sizeof(std::string), 2)') 818 gdb.execute('call($d->basic_string("' + init + 819 '",*(std::allocator<char>*)(1+$d)))') 820 value = gdb.parse_and_eval('$d').dereference() 821 return value 822 823 def pokeValue(self, value): 824 # Allocates inferior memory and copies the contents of value. 825 # Returns a pointer to the copy. 826 # Avoid malloc symbol clash with QVector 827 size = value.type.size() 828 data = value.data() 829 h = self.hexencode(data) 830 #DumperBase.warn('DATA: %s' % h) 831 string = ''.join('\\x' + h[2 * i:2 * i + 2] for i in range(size)) 832 exp = '(%s*)memcpy(calloc(%d, 1), "%s", %d)' \ 833 % (value.type.name, size, string, size) 834 #DumperBase.warn('EXP: %s' % exp) 835 res = gdb.parse_and_eval(exp) 836 #DumperBase.warn('RES: %s' % res) 837 return toInteger(res) 838 839 def releaseValue(self, address): 840 gdb.parse_and_eval('free(0x%x)' % address) 841 842 def setValue(self, address, typename, value): 843 cmd = 'set {%s}%s=%s' % (typename, address, value) 844 gdb.execute(cmd) 845 846 def setValues(self, address, typename, values): 847 cmd = 'set {%s[%s]}%s={%s}' \ 848 % (typename, len(values), address, ','.join(map(str, values))) 849 gdb.execute(cmd) 850 851 def selectedInferior(self): 852 try: 853 # gdb.Inferior is new in gdb 7.2 854 self.cachedInferior = gdb.selected_inferior() 855 except: 856 # Pre gdb 7.4. Right now we don't have more than one inferior anyway. 857 self.cachedInferior = gdb.inferiors()[0] 858 859 # Memoize result. 860 self.selectedInferior = lambda: self.cachedInferior 861 return self.cachedInferior 862 863 def readRawMemory(self, address, size): 864 #DumperBase.warn('READ: %s FROM 0x%x' % (size, address)) 865 if address == 0 or size == 0: 866 return bytes() 867 res = self.selectedInferior().read_memory(address, size) 868 return res 869 870 def findStaticMetaObject(self, type): 871 symbolName = type.name + '::staticMetaObject' 872 symbol = gdb.lookup_global_symbol(symbolName, gdb.SYMBOL_VAR_DOMAIN) 873 if not symbol: 874 return 0 875 try: 876 # Older GDB ~7.4 don't have gdb.Symbol.value() 877 return toInteger(symbol.value().address) 878 except: 879 pass 880 881 address = gdb.parse_and_eval("&'%s'" % symbolName) 882 return toInteger(address) 883 884 def isArmArchitecture(self): 885 return 'arm' in gdb.TARGET_CONFIG.lower() 886 887 def isQnxTarget(self): 888 return 'qnx' in gdb.TARGET_CONFIG.lower() 889 890 def isWindowsTarget(self): 891 # We get i686-w64-mingw32 892 return 'mingw' in gdb.TARGET_CONFIG.lower() 893 894 def isMsvcTarget(self): 895 return False 896 897 def prettySymbolByAddress(self, address): 898 try: 899 return str(gdb.parse_and_eval('(void(*))0x%x' % address)) 900 except: 901 return '0x%x' % address 902 903 def qtVersionString(self): 904 try: 905 return str(gdb.lookup_symbol('qVersion')[0].value()()) 906 except: 907 pass 908 try: 909 ns = self.qtNamespace() 910 return str(gdb.parse_and_eval("((const char*(*)())'%sqVersion')()" % ns)) 911 except: 912 pass 913 return None 914 915 def qtVersion(self): 916 try: 917 # Only available with Qt 5.3+ 918 qtversion = int(str(gdb.parse_and_eval('((void**)&qtHookData)[2]')), 16) 919 self.qtVersion = lambda: qtversion 920 return qtversion 921 except: 922 pass 923 924 try: 925 version = self.qtVersionString() 926 (major, minor, patch) = version[version.find('"') + 1:version.rfind('"')].split('.') 927 qtversion = 0x10000 * int(major) + 0x100 * int(minor) + int(patch) 928 self.qtVersion = lambda: qtversion 929 return qtversion 930 except: 931 # Use fallback until we have a better answer. 932 return self.fallbackQtVersion 933 934 def createSpecialBreakpoints(self, args): 935 self.specialBreakpoints = [] 936 937 def newSpecial(spec): 938 # GDB < 8.1 does not have the 'qualified' parameter here, 939 # GDB >= 8.1 applies some generous pattern matching, hitting 940 # e.g. also Foo::abort() when asking for '::abort' 941 class Pre81SpecialBreakpoint(gdb.Breakpoint): 942 def __init__(self, spec): 943 super(Pre81SpecialBreakpoint, self).__init__(spec, 944 gdb.BP_BREAKPOINT, internal=True) 945 self.spec = spec 946 947 def stop(self): 948 print("Breakpoint on '%s' hit." % self.spec) 949 return True 950 951 class SpecialBreakpoint(gdb.Breakpoint): 952 def __init__(self, spec): 953 super(SpecialBreakpoint, self).__init__(spec, 954 gdb.BP_BREAKPOINT, 955 internal=True, 956 qualified=True) 957 self.spec = spec 958 959 def stop(self): 960 print("Breakpoint on '%s' hit." % self.spec) 961 return True 962 963 try: 964 return SpecialBreakpoint(spec) 965 except: 966 return Pre81SpecialBreakpoint(spec) 967 968 # FIXME: ns is accessed too early. gdb.Breakpoint() has no 969 # 'rbreak' replacement, and breakpoints created with 970 # 'gdb.execute('rbreak...') cannot be made invisible. 971 # So let's ignore the existing of namespaced builds for this 972 # fringe feature here for now. 973 ns = self.qtNamespace() 974 if args.get('breakonabort', 0): 975 self.specialBreakpoints.append(newSpecial('abort')) 976 977 if args.get('breakonwarning', 0): 978 self.specialBreakpoints.append(newSpecial(ns + 'qWarning')) 979 self.specialBreakpoints.append(newSpecial(ns + 'QMessageLogger::warning')) 980 981 if args.get('breakonfatal', 0): 982 self.specialBreakpoints.append(newSpecial(ns + 'qFatal')) 983 self.specialBreakpoints.append(newSpecial(ns + 'QMessageLogger::fatal')) 984 985 #def threadname(self, maximalStackDepth, objectPrivateType): 986 # e = gdb.selected_frame() 987 # out = '' 988 # ns = self.qtNamespace() 989 # while True: 990 # maximalStackDepth -= 1 991 # if maximalStackDepth < 0: 992 # break 993 # e = e.older() 994 # if e == None or e.name() == None: 995 # break 996 # if e.name() in (ns + 'QThreadPrivate::start', '_ZN14QThreadPrivate5startEPv@4'): 997 # try: 998 # thrptr = e.read_var('thr').dereference() 999 # d_ptr = thrptr['d_ptr']['d'].cast(objectPrivateType).dereference() 1000 # try: 1001 # objectName = d_ptr['objectName'] 1002 # except: # Qt 5 1003 # p = d_ptr['extraData'] 1004 # if not self.isNull(p): 1005 # objectName = p.dereference()['objectName'] 1006 # if not objectName is None: 1007 # (data, size, alloc) = self.stringData(objectName) 1008 # if size > 0: 1009 # s = self.readMemory(data, 2 * size) 1010 # 1011 # thread = gdb.selected_thread() 1012 # inner = '{valueencoded="uf16:2:0",id="' 1013 # inner += str(thread.num) + '",value="' 1014 # inner += s 1015 # #inner += self.encodeString(objectName) 1016 # inner += '"},' 1017 # 1018 # out += inner 1019 # except: 1020 # pass 1021 # return out 1022 1023 def threadnames(self, maximalStackDepth): 1024 # FIXME: This needs a proper implementation for MinGW, and only there. 1025 # Linux, Mac and QNX mirror the objectName() to the underlying threads, 1026 # so we get the names already as part of the -thread-info output. 1027 return '[]' 1028 #out = '[' 1029 #oldthread = gdb.selected_thread() 1030 #if oldthread: 1031 # try: 1032 # objectPrivateType = gdb.lookup_type(ns + 'QObjectPrivate').pointer() 1033 # inferior = self.selectedInferior() 1034 # for thread in inferior.threads(): 1035 # thread.switch() 1036 # out += self.threadname(maximalStackDepth, objectPrivateType) 1037 # except: 1038 # pass 1039 # oldthread.switch() 1040 #return out + ']' 1041 1042 def importPlainDumper(self, printer): 1043 name = printer.name.replace('::', '__') 1044 self.qqDumpers[name] = PlainDumper(printer) 1045 self.qqFormats[name] = '' 1046 1047 def importPlainDumpersForObj(self, obj): 1048 for printers in obj.pretty_printers + gdb.pretty_printers: 1049 for printer in printers.subprinters: 1050 self.importPlainDumper(printer) 1051 1052 def importPlainDumpers(self): 1053 for obj in gdb.objfiles(): 1054 self.importPlainDumpersForObj(obj) 1055 1056 def qtNamespace(self): 1057 # This function is replaced by handleQtCoreLoaded() 1058 return '' 1059 1060 def findSymbol(self, symbolName): 1061 try: 1062 return toInteger(gdb.parse_and_eval("(size_t)&'%s'" % symbolName)) 1063 except: 1064 return 0 1065 1066 def handleNewObjectFile(self, objfile): 1067 name = objfile.filename 1068 if self.isWindowsTarget(): 1069 qtCoreMatch = re.match(r'.*Qt[56]?Core[^/.]*d?\.dll', name) 1070 else: 1071 qtCoreMatch = re.match(r'.*/libQt[56]?Core[^/.]*\.so', name) 1072 1073 if qtCoreMatch is not None: 1074 self.addDebugLibs(objfile) 1075 self.handleQtCoreLoaded(objfile) 1076 1077 if self.usePlainDumpers: 1078 self.importPlainDumpersForObj(objfile) 1079 1080 def addDebugLibs(self, objfile): 1081 # The directory where separate debug symbols are searched for 1082 # is "/usr/lib/debug". 1083 try: 1084 cooked = gdb.execute('show debug-file-directory', to_string=True) 1085 clean = cooked.split('"')[1] 1086 newdir = '/'.join(objfile.filename.split('/')[:-1]) 1087 gdb.execute('set debug-file-directory %s:%s' % (clean, newdir)) 1088 except: 1089 pass 1090 1091 def handleQtCoreLoaded(self, objfile): 1092 fd, tmppath = tempfile.mkstemp() 1093 os.close(fd) 1094 try: 1095 cmd = 'maint print msymbols -objfile "%s" -- %s' % (objfile.filename, tmppath) 1096 symbols = gdb.execute(cmd, to_string=True) 1097 except: 1098 try: 1099 # command syntax depends on gdb version - below is gdb < 8 1100 cmd = 'maint print msymbols %s "%s"' % (tmppath, objfile.filename) 1101 symbols = gdb.execute(cmd, to_string=True) 1102 except: 1103 pass 1104 ns = '' 1105 with open(tmppath) as f: 1106 for line in f: 1107 if line.find('msgHandlerGrabbed ') >= 0: 1108 # [11] b 0x7ffff683c000 _ZN4MynsL17msgHandlerGrabbedE 1109 # section .tbss Myns::msgHandlerGrabbed qlogging.cpp 1110 ns = re.split(r'_ZN?(\d*)(\w*)L17msgHandlerGrabbedE? ', line)[2] 1111 if len(ns): 1112 ns += '::' 1113 break 1114 if line.find('currentThreadData ') >= 0: 1115 # [ 0] b 0x7ffff67d3000 _ZN2UUL17currentThreadDataE 1116 # section .tbss UU::currentThreadData qthread_unix.cpp\\n 1117 ns = re.split(r'_ZN?(\d*)(\w*)L17currentThreadDataE? ', line)[2] 1118 if len(ns): 1119 ns += '::' 1120 break 1121 os.remove(tmppath) 1122 1123 lenns = len(ns) 1124 strns = ('%d%s' % (lenns - 2, ns[:lenns - 2])) if lenns else '' 1125 1126 if lenns: 1127 # This might be wrong, but we can't do better: We found 1128 # a libQt5Core and could not extract a namespace. 1129 # The best guess is that there isn't any. 1130 self.qtNamespaceToReport = ns 1131 self.qtNamespace = lambda: ns 1132 1133 sym = '_ZN%s7QObject11customEventEPNS_6QEventE' % strns 1134 else: 1135 sym = '_ZN7QObject11customEventEP6QEvent' 1136 self.qtCustomEventFunc = self.findSymbol(sym) 1137 1138 sym += '@plt' 1139 self.qtCustomEventPltFunc = self.findSymbol(sym) 1140 1141 sym = '_ZNK%s7QObject8propertyEPKc' % strns 1142 if not self.isWindowsTarget(): # prevent calling the property function on windows 1143 self.qtPropertyFunc = self.findSymbol(sym) 1144 1145 def assignValue(self, args): 1146 typeName = self.hexdecode(args['type']) 1147 expr = self.hexdecode(args['expr']) 1148 value = self.hexdecode(args['value']) 1149 simpleType = int(args['simpleType']) 1150 ns = self.qtNamespace() 1151 if typeName.startswith(ns): 1152 typeName = typeName[len(ns):] 1153 typeName = typeName.replace('::', '__') 1154 pos = typeName.find('<') 1155 if pos != -1: 1156 typeName = typeName[0:pos] 1157 if typeName in self.qqEditable and not simpleType: 1158 #self.qqEditable[typeName](self, expr, value) 1159 expr = self.parseAndEvaluate(expr) 1160 self.qqEditable[typeName](self, expr, value) 1161 else: 1162 cmd = 'set variable (%s)=%s' % (expr, value) 1163 gdb.execute(cmd) 1164 1165 def appendSolibSearchPath(self, args): 1166 new = list(map(self.hexdecode, args['path'])) 1167 old = [gdb.parameter('solib-search-path')] 1168 joined = os.pathsep.join([item for item in old + new if item != '']) 1169 gdb.execute('set solib-search-path %s' % joined) 1170 1171 def watchPoint(self, args): 1172 self.reportToken(args) 1173 ns = self.qtNamespace() 1174 lenns = len(ns) 1175 strns = ('%d%s' % (lenns - 2, ns[:lenns - 2])) if lenns else '' 1176 sym = '_ZN%s12QApplication8widgetAtEii' % strns 1177 expr = '%s(%s,%s)' % (sym, args['x'], args['y']) 1178 res = self.parseAndEvaluate(expr) 1179 p = 0 if res is None else res.pointer() 1180 n = ("'%sQWidget'" % ns) if lenns else 'QWidget' 1181 self.reportResult('selected="0x%x",expr="(%s*)0x%x"' % (p, n, p), args) 1182 1183 def nativeValueDereferencePointer(self, value): 1184 # This is actually pretty expensive, up to 100ms. 1185 deref = value.nativeValue.dereference() 1186 if self.useDynamicType: 1187 deref = deref.cast(deref.dynamic_type) 1188 return self.fromNativeValue(deref) 1189 1190 def nativeValueDereferenceReference(self, value): 1191 nativeValue = value.nativeValue 1192 return self.fromNativeValue(nativeValue.cast(nativeValue.type.target())) 1193 1194 def nativeDynamicTypeName(self, address, baseType): 1195 # Needed for Gdb13393 test. 1196 nativeType = self.lookupNativeType(baseType.name) 1197 if nativeType is None: 1198 return None 1199 nativeTypePointer = nativeType.pointer() 1200 nativeValue = gdb.Value(address).cast(nativeTypePointer).dereference() 1201 val = nativeValue.cast(nativeValue.dynamic_type) 1202 return str(val.type) 1203 #try: 1204 # vtbl = gdb.execute('info symbol {%s*}0x%x' % (baseType.name, address), to_string = True) 1205 #except: 1206 # return None 1207 #pos1 = vtbl.find('vtable ') 1208 #if pos1 == -1: 1209 # return None 1210 #pos1 += 11 1211 #pos2 = vtbl.find(' +', pos1) 1212 #if pos2 == -1: 1213 # return None 1214 #return vtbl[pos1 : pos2] 1215 1216 def nativeDynamicType(self, address, baseType): 1217 # Needed for Gdb13393 test. 1218 nativeType = self.lookupNativeType(baseType.name) 1219 if nativeType is None: 1220 return baseType 1221 nativeTypePointer = nativeType.pointer() 1222 nativeValue = gdb.Value(address).cast(nativeTypePointer).dereference() 1223 return self.fromNativeType(nativeValue.dynamic_type) 1224 1225 def enumExpression(self, enumType, enumValue): 1226 return self.qtNamespace() + 'Qt::' + enumValue 1227 1228 def lookupNativeType(self, typeName): 1229 nativeType = self.lookupNativeTypeHelper(typeName) 1230 if nativeType is not None: 1231 self.check(isinstance(nativeType, gdb.Type)) 1232 return nativeType 1233 1234 def lookupNativeTypeHelper(self, typeName): 1235 typeobj = self.typeCache.get(typeName) 1236 #DumperBase.warn('LOOKUP 1: %s -> %s' % (typeName, typeobj)) 1237 if typeobj is not None: 1238 return typeobj 1239 1240 if typeName == 'void': 1241 typeobj = gdb.lookup_type(typeName) 1242 self.typeCache[typeName] = typeobj 1243 self.typesToReport[typeName] = typeobj 1244 return typeobj 1245 1246 #try: 1247 # typeobj = gdb.parse_and_eval('{%s}&main' % typeName).typeobj 1248 # if not typeobj is None: 1249 # self.typeCache[typeName] = typeobj 1250 # self.typesToReport[typeName] = typeobj 1251 # return typeobj 1252 #except: 1253 # pass 1254 1255 # See http://sourceware.org/bugzilla/show_bug.cgi?id=13269 1256 # gcc produces '{anonymous}', gdb '(anonymous namespace)' 1257 # '<unnamed>' has been seen too. The only thing gdb 1258 # understands when reading things back is '(anonymous namespace)' 1259 if typeName.find('{anonymous}') != -1: 1260 ts = typeName 1261 ts = ts.replace('{anonymous}', '(anonymous namespace)') 1262 typeobj = self.lookupNativeType(ts) 1263 if typeobj is not None: 1264 self.typeCache[typeName] = typeobj 1265 self.typesToReport[typeName] = typeobj 1266 return typeobj 1267 1268 #DumperBase.warn(" RESULT FOR 7.2: '%s': %s" % (typeName, typeobj)) 1269 1270 # This part should only trigger for 1271 # gdb 7.1 for types with namespace separators. 1272 # And anonymous namespaces. 1273 1274 ts = typeName 1275 while True: 1276 if ts.startswith('class '): 1277 ts = ts[6:] 1278 elif ts.startswith('struct '): 1279 ts = ts[7:] 1280 elif ts.startswith('const '): 1281 ts = ts[6:] 1282 elif ts.startswith('volatile '): 1283 ts = ts[9:] 1284 elif ts.startswith('enum '): 1285 ts = ts[5:] 1286 elif ts.endswith(' const'): 1287 ts = ts[:-6] 1288 elif ts.endswith(' volatile'): 1289 ts = ts[:-9] 1290 elif ts.endswith('*const'): 1291 ts = ts[:-5] 1292 elif ts.endswith('*volatile'): 1293 ts = ts[:-8] 1294 else: 1295 break 1296 1297 if ts.endswith('*'): 1298 typeobj = self.lookupNativeType(ts[0:-1]) 1299 if typeobj is not None: 1300 typeobj = typeobj.pointer() 1301 self.typeCache[typeName] = typeobj 1302 self.typesToReport[typeName] = typeobj 1303 return typeobj 1304 1305 try: 1306 #DumperBase.warn("LOOKING UP 1 '%s'" % ts) 1307 typeobj = gdb.lookup_type(ts) 1308 except RuntimeError as error: 1309 #DumperBase.warn("LOOKING UP 2 '%s' ERROR %s" % (ts, error)) 1310 # See http://sourceware.org/bugzilla/show_bug.cgi?id=11912 1311 exp = "(class '%s'*)0" % ts 1312 try: 1313 typeobj = self.parse_and_eval(exp).type.target() 1314 #DumperBase.warn("LOOKING UP 3 '%s'" % typeobj) 1315 except: 1316 # Can throw 'RuntimeError: No type named class Foo.' 1317 pass 1318 except: 1319 #DumperBase.warn("LOOKING UP '%s' FAILED" % ts) 1320 pass 1321 1322 if typeobj is not None: 1323 #DumperBase.warn('CACHING: %s' % typeobj) 1324 self.typeCache[typeName] = typeobj 1325 self.typesToReport[typeName] = typeobj 1326 1327 # This could still be None as gdb.lookup_type('char[3]') generates 1328 # 'RuntimeError: No type named char[3]' 1329 #self.typeCache[typeName] = typeobj 1330 #self.typesToReport[typeName] = typeobj 1331 return typeobj 1332 1333 def doContinue(self): 1334 gdb.execute('continue') 1335 1336 def fetchStack(self, args): 1337 1338 def fromNativePath(string): 1339 return string.replace('\\', '/') 1340 1341 extraQml = int(args.get('extraqml', '0')) 1342 limit = int(args['limit']) 1343 if limit <= 0: 1344 limit = 10000 1345 1346 self.prepare(args) 1347 self.output = '' 1348 1349 i = 0 1350 if extraQml: 1351 frame = gdb.newest_frame() 1352 ns = self.qtNamespace() 1353 needle = self.qtNamespace() + 'QV4::ExecutionEngine' 1354 pats = [ 1355 '{0}qt_v4StackTraceForEngine((void*)0x{1:x})', 1356 '{0}qt_v4StackTrace((({0}QV4::ExecutionEngine *)0x{1:x})->currentContext())', 1357 '{0}qt_v4StackTrace((({0}QV4::ExecutionEngine *)0x{1:x})->currentContext)', 1358 ] 1359 done = False 1360 while i < limit and frame and not done: 1361 block = None 1362 try: 1363 block = frame.block() 1364 except: 1365 pass 1366 if block is not None: 1367 for symbol in block: 1368 if symbol.is_variable or symbol.is_argument: 1369 value = symbol.value(frame) 1370 typeobj = value.type 1371 if typeobj.code == gdb.TYPE_CODE_PTR: 1372 dereftype = typeobj.target().unqualified() 1373 if dereftype.name == needle: 1374 addr = toInteger(value) 1375 res = None 1376 for pat in pats: 1377 try: 1378 expr = pat.format(ns, addr) 1379 res = str(gdb.parse_and_eval(expr)) 1380 break 1381 except: 1382 continue 1383 1384 if res is None: 1385 done = True 1386 break 1387 1388 pos = res.find('"stack=[') 1389 if pos != -1: 1390 res = res[pos + 8:-2] 1391 res = res.replace('\\\"', '\"') 1392 res = res.replace('func=', 'function=') 1393 self.put(res) 1394 done = True 1395 break 1396 frame = frame.older() 1397 i += 1 1398 1399 frame = gdb.newest_frame() 1400 self.currentCallContext = None 1401 while i < limit and frame: 1402 with OutputSaver(self): 1403 name = frame.name() 1404 functionName = '??' if name is None else name 1405 fileName = '' 1406 objfile = '' 1407 symtab = '' 1408 pc = frame.pc() 1409 sal = frame.find_sal() 1410 line = -1 1411 if sal: 1412 line = sal.line 1413 symtab = sal.symtab 1414 if symtab is not None: 1415 objfile = fromNativePath(symtab.objfile.filename) 1416 fullname = symtab.fullname() 1417 if fullname is None: 1418 fileName = '' 1419 else: 1420 fileName = fromNativePath(fullname) 1421 1422 if self.nativeMixed and functionName == 'qt_qmlDebugMessageAvailable': 1423 interpreterStack = self.extractInterpreterStack() 1424 #print('EXTRACTED INTEPRETER STACK: %s' % interpreterStack) 1425 for interpreterFrame in interpreterStack.get('frames', []): 1426 function = interpreterFrame.get('function', '') 1427 fileName = interpreterFrame.get('file', '') 1428 language = interpreterFrame.get('language', '') 1429 lineNumber = interpreterFrame.get('line', 0) 1430 context = interpreterFrame.get('context', 0) 1431 1432 self.put(('frame={function="%s",file="%s",' 1433 'line="%s",language="%s",context="%s"}') 1434 % (function, self.hexencode(fileName), lineNumber, language, context)) 1435 1436 if False and self.isInternalInterpreterFrame(functionName): 1437 frame = frame.older() 1438 self.put(('frame={address="0x%x",function="%s",' 1439 'file="%s",line="%s",' 1440 'module="%s",language="c",usable="0"}') % 1441 (pc, functionName, fileName, line, objfile)) 1442 i += 1 1443 frame = frame.older() 1444 continue 1445 1446 self.put(('frame={level="%s",address="0x%x",function="%s",' 1447 'file="%s",line="%s",module="%s",language="c"}') % 1448 (i, pc, functionName, fileName, line, objfile)) 1449 1450 frame = frame.older() 1451 i += 1 1452 self.reportResult('stack={frames=[' + self.output + ']}') 1453 1454 def createResolvePendingBreakpointsHookBreakpoint(self, args): 1455 class Resolver(gdb.Breakpoint): 1456 def __init__(self, dumper, args): 1457 self.dumper = dumper 1458 self.args = args 1459 spec = 'qt_qmlDebugConnectorOpen' 1460 super(Resolver, self).\ 1461 __init__(spec, gdb.BP_BREAKPOINT, internal=True, temporary=False) 1462 1463 def stop(self): 1464 self.dumper.resolvePendingInterpreterBreakpoint(args) 1465 self.enabled = False 1466 return False 1467 1468 self.interpreterBreakpointResolvers.append(Resolver(self, args)) 1469 1470 def exitGdb(self, _): 1471 gdb.execute('quit') 1472 1473 def reportResult(self, result, args={}): 1474 print('result={token="%s",%s}' % (args.get("token", 0), result)) 1475 1476 def profile1(self, args): 1477 '''Internal profiling''' 1478 import cProfile 1479 tempDir = tempfile.gettempdir() + '/bbprof' 1480 cProfile.run('theDumper.fetchVariables(%s)' % args, tempDir) 1481 import pstats 1482 pstats.Stats(tempDir).sort_stats('time').print_stats() 1483 1484 def profile2(self, args): 1485 import timeit 1486 print(timeit.repeat('theDumper.fetchVariables(%s)' % args, 1487 'from __main__ import theDumper', number=10)) 1488 1489 def tracepointModified(self, tp): 1490 self.tpExpressions = {} 1491 self.tpExpressionWarnings = [] 1492 s = self.resultToMi(tp.dicts()) 1493 def handler(): 1494 print("tracepointmodified=%s" % s) 1495 gdb.post_event(handler) 1496 1497 def tracepointHit(self, tp, result): 1498 expressions = '{' + ','.join(["%s=%s" % (k,v) for k,v in self.tpExpressions.items()]) + '}' 1499 warnings = [] 1500 if 'warning' in result.keys(): 1501 warnings.append(result.pop('warning')) 1502 warnings += self.tpExpressionWarnings 1503 r = self.resultToMi(result) 1504 w = self.resultToMi(warnings) 1505 def handler(): 1506 print("tracepointhit={result=%s,expressions=%s,warnings=%s}" % (r, expressions, w)) 1507 gdb.post_event(handler) 1508 1509 def tracepointExpression(self, tp, expression, value, args): 1510 key = "x" + str(len(self.tpExpressions)) 1511 if (isinstance(value, gdb.Value)): 1512 try: 1513 val = self.fromNativeValue(value) 1514 self.prepare(args) 1515 with TopLevelItem(self, expression): 1516 self.putItem(val) 1517 self.tpExpressions[key] = self.output 1518 except Exception as e: 1519 self.tpExpressions[key] = '"<N/A>"' 1520 self.tpExpressionWarnings.append(str(e)) 1521 elif (isinstance(value, Exception)): 1522 self.tpExpressions[key] = '"<N/A>"' 1523 self.tpExpressionWarnings.append(str(value)) 1524 else: 1525 self.tpExpressions[key] = '"<N/A>"' 1526 self.tpExpressionWarnings.append('Unknown expression value type') 1527 return key 1528 1529 def createTracepoint(self, args): 1530 """ 1531 Creates a tracepoint 1532 """ 1533 tp = GDBTracepoint.create(args, 1534 onModified=self.tracepointModified, 1535 onHit=self.tracepointHit, 1536 onExpression=lambda tp, expr, val: self.tracepointExpression(tp, expr, val, args)) 1537 self.reportResult("tracepoint=%s" % self.resultToMi(tp.dicts()), args) 1538class CliDumper(Dumper): 1539 def __init__(self): 1540 Dumper.__init__(self) 1541 self.childrenPrefix = '[' 1542 self.chidrenSuffix = '] ' 1543 self.indent = 0 1544 self.isCli = True 1545 self.setupDumpers({}) 1546 1547 def put(self, line): 1548 if self.output.endswith('\n'): 1549 self.output = self.output[0:-1] 1550 self.output += line 1551 1552 def putNumChild(self, numchild): 1553 pass 1554 1555 def putOriginalAddress(self, address): 1556 pass 1557 1558 def fetchVariable(self, line): 1559 # HACK: Currently, the response to the QtCore loading is completely 1560 # eaten by theDumper, so copy the results here. Better would be 1561 # some shared component. 1562 self.qtCustomEventFunc = theDumper.qtCustomEventFunc 1563 self.qtCustomEventPltFunc = theDumper.qtCustomEventPltFunc 1564 self.qtPropertyFunc = theDumper.qtPropertyFunc 1565 1566 names = line.split(' ') 1567 name = names[0] 1568 1569 toExpand = set() 1570 for n in names: 1571 while n: 1572 toExpand.add(n) 1573 n = n[0:n.rfind('.')] 1574 1575 args = {} 1576 args['fancy'] = 1 1577 args['passexceptions'] = 1 1578 args['autoderef'] = 1 1579 args['qobjectnames'] = 1 1580 args['varlist'] = name 1581 args['expanded'] = toExpand 1582 self.expandableINames = set() 1583 self.prepare(args) 1584 1585 self.output = name + ' = ' 1586 value = self.parseAndEvaluate(name) 1587 with TopLevelItem(self, name): 1588 self.putItem(value) 1589 1590 if not self.expandableINames: 1591 return self.output + '\n\nNo drill down available.\n' 1592 1593 pattern = ' pp ' + name + ' ' + '%s' 1594 return (self.output 1595 + '\n\nDrill down:\n ' 1596 + '\n '.join(pattern % x for x in self.expandableINames) 1597 + '\n') 1598 1599 1600# Global instances. 1601theDumper = Dumper() 1602theCliDumper = CliDumper() 1603 1604 1605###################################################################### 1606# 1607# ThreadNames Command 1608# 1609####################################################################### 1610 1611def threadnames(arg): 1612 return theDumper.threadnames(int(arg)) 1613 1614 1615registerCommand('threadnames', threadnames) 1616 1617####################################################################### 1618# 1619# Native Mixed 1620# 1621####################################################################### 1622 1623 1624class InterpreterMessageBreakpoint(gdb.Breakpoint): 1625 def __init__(self): 1626 spec = 'qt_qmlDebugMessageAvailable' 1627 super(InterpreterMessageBreakpoint, self).\ 1628 __init__(spec, gdb.BP_BREAKPOINT, internal=True) 1629 1630 def stop(self): 1631 print('Interpreter event received.') 1632 return theDumper.handleInterpreterMessage() 1633 1634 1635####################################################################### 1636# 1637# Shared objects 1638# 1639####################################################################### 1640 1641def new_objfile_handler(event): 1642 return theDumper.handleNewObjectFile(event.new_objfile) 1643 1644 1645gdb.events.new_objfile.connect(new_objfile_handler) 1646 1647 1648#InterpreterMessageBreakpoint() 1649