forked from qt-creator/qt-creator
This splits the bool setToolTipExpression() operation into a bool canHandleToolTip(), and the actual processing of the request, which is mostly identical to the handling of a watcher. Handling a watcher is now mostly the same as a full Locals update, except for the 'partial' flag. Pushing the handling of that down to the bridges gives identical code paths in the gdb and lldbengine. Move that to the DebuggerEngine base class. Change-Id: I3861b43e8630c7e7bd57fcd549b2a2387e3d4869 Reviewed-by: hjk <hjk@theqtcompany.com> Reviewed-by: Christian Stenger <christian.stenger@theqtcompany.com>
1822 lines
69 KiB
Python
1822 lines
69 KiB
Python
############################################################################
|
|
#
|
|
# Copyright (C) 2015 The Qt Company Ltd.
|
|
# Contact: http://www.qt.io/licensing
|
|
#
|
|
# This file is part of Qt Creator.
|
|
#
|
|
# Commercial License Usage
|
|
# Licensees holding valid commercial Qt licenses may use this file in
|
|
# accordance with the commercial license agreement provided with the
|
|
# Software or, alternatively, in accordance with the terms contained in
|
|
# a written agreement between you and The Qt Company. For licensing terms and
|
|
# conditions see http://www.qt.io/terms-conditions. For further information
|
|
# use the contact form at http://www.qt.io/contact-us.
|
|
#
|
|
# GNU Lesser General Public License Usage
|
|
# Alternatively, this file may be used under the terms of the GNU Lesser
|
|
# General Public License version 2.1 or version 3 as published by the Free
|
|
# Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
# LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
# following information to ensure the GNU Lesser General Public License
|
|
# requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
# http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
#
|
|
# In addition, as a special exception, The Qt Company gives you certain additional
|
|
# rights. These rights are described in The Qt Company LGPL Exception
|
|
# version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
#
|
|
#############################################################################
|
|
|
|
import inspect
|
|
import os
|
|
import platform
|
|
import re
|
|
import sys
|
|
import threading
|
|
import lldb
|
|
|
|
sys.path.insert(1, os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))))
|
|
|
|
from dumper import *
|
|
|
|
#######################################################################
|
|
#
|
|
# Helpers
|
|
#
|
|
#######################################################################
|
|
|
|
qqWatchpointOffset = 10000
|
|
|
|
def warn(message):
|
|
print('\n\nWARNING="%s",\n' % message.encode("latin1").replace('"', "'"))
|
|
|
|
def showException(msg, exType, exValue, exTraceback):
|
|
warn("**** CAUGHT EXCEPTION: %s ****" % msg)
|
|
import traceback
|
|
lines = [line for line in traceback.format_exception(exType, exValue, exTraceback)]
|
|
warn('\n'.join(lines))
|
|
|
|
def fileName(file):
|
|
return str(file) if file.IsValid() else ''
|
|
|
|
|
|
def check(exp):
|
|
if not exp:
|
|
raise RuntimeError("Check failed")
|
|
|
|
Value = lldb.SBValue
|
|
|
|
def impl_SBValue__add__(self, offset):
|
|
if self.GetType().IsPointerType():
|
|
if isinstance(offset, int) or isinstance(offset, long):
|
|
pass
|
|
else:
|
|
offset = offset.GetValueAsSigned()
|
|
itemsize = self.GetType().GetPointeeType().GetByteSize()
|
|
address = self.GetValueAsUnsigned() + offset * itemsize
|
|
address = address & 0xFFFFFFFFFFFFFFFF # Force unsigned
|
|
return self.CreateValueFromAddress(None, address,
|
|
self.GetType().GetPointeeType()).AddressOf()
|
|
|
|
raise RuntimeError("SBValue.__add__ not implemented: %s" % self.GetType())
|
|
return NotImplemented
|
|
|
|
def impl_SBValue__sub__(self, other):
|
|
if self.GetType().IsPointerType():
|
|
if isinstance(other, int) or isinstance(other, long):
|
|
address = self.GetValueAsUnsigned() - other
|
|
address = address & 0xFFFFFFFFFFFFFFFF # Force unsigned
|
|
return self.CreateValueFromAddress(None, address, self.GetType())
|
|
if other.GetType().IsPointerType():
|
|
itemsize = self.GetType().GetPointeeType().GetByteSize()
|
|
return (self.GetValueAsUnsigned() - other.GetValueAsUnsigned()) / itemsize
|
|
raise RuntimeError("SBValue.__sub__ not implemented: %s" % self.GetType())
|
|
return NotImplemented
|
|
|
|
def impl_SBValue__le__(self, other):
|
|
if self.GetType().IsPointerType() and other.GetType().IsPointerType():
|
|
return int(self) <= int(other)
|
|
raise RuntimeError("SBValue.__le__ not implemented")
|
|
return NotImplemented
|
|
|
|
def impl_SBValue__int__(self):
|
|
return self.GetValueAsSigned()
|
|
|
|
def impl_SBValue__float__(self):
|
|
error = lldb.SBError()
|
|
if self.GetType().GetByteSize() == 4:
|
|
result = self.GetData().GetFloat(error, 0)
|
|
else:
|
|
result = self.GetData().GetDouble(error, 0)
|
|
if error.Success():
|
|
return result
|
|
return NotImplemented
|
|
|
|
def impl_SBValue__long__(self):
|
|
return int(self.GetValue(), 0)
|
|
|
|
def impl_SBValue__getitem__(value, index):
|
|
if isinstance(index, int) or isinstance(index, long):
|
|
type = value.GetType()
|
|
if type.IsPointerType():
|
|
innertype = value.Dereference().GetType()
|
|
address = value.GetValueAsUnsigned() + index * innertype.GetByteSize()
|
|
address = address & 0xFFFFFFFFFFFFFFFF # Force unsigned
|
|
return value.CreateValueFromAddress(None, address, innertype)
|
|
return value.GetChildAtIndex(index)
|
|
item = value.GetChildMemberWithName(index)
|
|
if item.IsValid():
|
|
return item
|
|
raise RuntimeError("SBValue.__getitem__: No such member '%s'" % index)
|
|
|
|
def impl_SBValue__deref(value):
|
|
result = value.Dereference()
|
|
if result.IsValid():
|
|
return result
|
|
exp = "*(class %s*)0x%x" % (value.GetType().GetPointeeType(), value.GetValueAsUnsigned())
|
|
return value.CreateValueFromExpression(None, exp)
|
|
|
|
lldb.SBValue.__add__ = impl_SBValue__add__
|
|
lldb.SBValue.__sub__ = impl_SBValue__sub__
|
|
lldb.SBValue.__le__ = impl_SBValue__le__
|
|
|
|
lldb.SBValue.__getitem__ = impl_SBValue__getitem__
|
|
lldb.SBValue.__int__ = impl_SBValue__int__
|
|
lldb.SBValue.__float__ = impl_SBValue__float__
|
|
lldb.SBValue.__long__ = lambda self: long(self.GetValue(), 0)
|
|
|
|
lldb.SBValue.code = lambda self: self.GetTypeClass()
|
|
lldb.SBValue.cast = lambda self, typeObj: self.Cast(typeObj)
|
|
lldb.SBValue.dereference = impl_SBValue__deref
|
|
lldb.SBValue.address = property(lambda self: self.GetLoadAddress())
|
|
|
|
lldb.SBType.pointer = lambda self: self.GetPointerType()
|
|
lldb.SBType.target = lambda self: self.GetPointeeType()
|
|
lldb.SBType.code = lambda self: self.GetTypeClass()
|
|
lldb.SBType.sizeof = property(lambda self: self.GetByteSize())
|
|
|
|
|
|
lldb.SBType.unqualified = \
|
|
lambda self: self.GetUnqualifiedType() if hasattr(self, 'GetUnqualifiedType') else self
|
|
lldb.SBType.strip_typedefs = \
|
|
lambda self: self.GetCanonicalType() if hasattr(self, 'GetCanonicalType') else self
|
|
|
|
lldb.SBType.__orig__str__ = lldb.SBType.__str__
|
|
lldb.SBType.__str__ = lldb.SBType.GetName
|
|
|
|
class Dumper(DumperBase):
|
|
def __init__(self):
|
|
DumperBase.__init__(self)
|
|
|
|
self.outputLock = threading.Lock()
|
|
self.debugger = lldb.SBDebugger.Create()
|
|
#self.debugger.SetLoggingCallback(loggingCallback)
|
|
#def loggingCallback(args):
|
|
# s = args.strip()
|
|
# s = s.replace('"', "'")
|
|
# sys.stdout.write('log="%s"@\n' % s)
|
|
#Same as: self.debugger.HandleCommand("log enable lldb dyld step")
|
|
#self.debugger.EnableLog("lldb", ["dyld", "step", "process", "state",
|
|
# "thread", "events",
|
|
# "communication", "unwind", "commands"])
|
|
#self.debugger.EnableLog("lldb", ["all"])
|
|
self.debugger.Initialize()
|
|
self.debugger.HandleCommand("settings set auto-confirm on")
|
|
|
|
# FIXME: warn("DISABLING DEFAULT FORMATTERS")
|
|
# It doesn't work at all with 179.5 and we have some bad
|
|
# interactonn in 3000
|
|
# if not hasattr(lldb.SBType, 'GetCanonicalType'): # "Test" for 179.5
|
|
self.debugger.HandleCommand('type category delete gnu-libstdc++')
|
|
self.debugger.HandleCommand('type category delete libcxx')
|
|
#for i in range(self.debugger.GetNumCategories()):
|
|
# self.debugger.GetCategoryAtIndex(i).SetEnabled(False)
|
|
|
|
self.isLldb = True
|
|
self.isGoodLldb = hasattr(lldb.SBValue, "SetPreferDynamicValue")
|
|
self.process = None
|
|
self.target = None
|
|
self.eventState = lldb.eStateInvalid
|
|
self.expandedINames = {}
|
|
self.passExceptions = False
|
|
self.useLldbDumpers = False
|
|
self.autoDerefPointers = True
|
|
self.useDynamicType = True
|
|
self.useFancy = True
|
|
self.formats = {}
|
|
self.typeformats = {}
|
|
|
|
self.currentIName = None
|
|
self.currentValue = ReportItem()
|
|
self.currentType = ReportItem()
|
|
self.currentNumChild = None
|
|
self.currentMaxNumChild = None
|
|
self.currentPrintsAddress = None
|
|
self.currentChildType = None
|
|
self.currentChildNumChild = -1
|
|
self.currentWatchers = {}
|
|
|
|
self.executable_ = None
|
|
self.startMode_ = None
|
|
self.processArgs_ = None
|
|
self.attachPid_ = None
|
|
|
|
self.charType_ = None
|
|
self.intType_ = None
|
|
self.int64Type_ = None
|
|
self.sizetType_ = None
|
|
self.charPtrType_ = None
|
|
self.voidPtrType_ = None
|
|
self.isShuttingDown_ = False
|
|
self.isInterrupting_ = False
|
|
self.dummyValue = None
|
|
self.qmlBreakpointResolvers = {}
|
|
self.qmlTriggeredBreakpoint = None
|
|
|
|
self.report('lldbversion=\"%s\"' % lldb.SBDebugger.GetVersionString())
|
|
self.reportState("enginesetupok")
|
|
|
|
def enterSubItem(self, item):
|
|
if isinstance(item.name, lldb.SBValue):
|
|
# Avoid $$__synth__ suffix on Mac.
|
|
value = item.name
|
|
if self.isGoodLldb:
|
|
value.SetPreferSyntheticValue(False)
|
|
item.name = value.GetName()
|
|
if item.name is None:
|
|
self.anonNumber += 1
|
|
item.name = "#%d" % self.anonNumber
|
|
if not item.iname:
|
|
item.iname = "%s.%s" % (self.currentIName, item.name)
|
|
self.put('{')
|
|
#if not item.name is None:
|
|
if isinstance(item.name, str):
|
|
if item.name == '**&':
|
|
item.name = '*'
|
|
self.put('name="%s",' % item.name)
|
|
item.savedIName = self.currentIName
|
|
item.savedValue = self.currentValue
|
|
item.savedType = self.currentType
|
|
self.currentIName = item.iname
|
|
self.currentValue = ReportItem()
|
|
self.currentType = ReportItem()
|
|
|
|
def exitSubItem(self, item, exType, exValue, exTraceBack):
|
|
if not exType is None:
|
|
if self.passExceptions:
|
|
showException("SUBITEM", exType, exValue, exTraceBack)
|
|
self.putNumChild(0)
|
|
self.putValue("<not accessible>")
|
|
try:
|
|
if self.currentType.value:
|
|
typeName = self.currentType.value
|
|
if len(typeName) > 0 and typeName != self.currentChildType:
|
|
self.put('type="%s",' % typeName) # str(type.unqualified()) ?
|
|
if self.currentValue.value is None:
|
|
self.put('value="<not accessible>",numchild="0",')
|
|
else:
|
|
if not self.currentValue.encoding is None:
|
|
self.put('valueencoded="%s",' % self.currentValue.encoding)
|
|
if self.currentValue.elided:
|
|
self.put('valueelided="%s",' % self.currentValue.elided)
|
|
self.put('value="%s",' % self.currentValue.value)
|
|
except:
|
|
pass
|
|
self.put('},')
|
|
self.currentIName = item.savedIName
|
|
self.currentValue = item.savedValue
|
|
self.currentType = item.savedType
|
|
return True
|
|
|
|
def stateName(self, s):
|
|
try:
|
|
# See db.StateType
|
|
return (
|
|
'invalid',
|
|
'unloaded', # Process is object is valid, but not currently loaded
|
|
'connected', # Process is connected to remote debug services,
|
|
# but not launched or attached to anything yet
|
|
'attaching', # Process is currently trying to attach
|
|
'launching', # Process is in the process of launching
|
|
'stopped', # Process or thread is stopped and can be examined.
|
|
'running', # Process or thread is running and can't be examined.
|
|
'stepping', # Process or thread is in the process of stepping
|
|
# and can not be examined.
|
|
'crashed', # Process or thread has crashed and can be examined.
|
|
'detached', # Process has been detached and can't be examined.
|
|
'exited', # Process has exited and can't be examined.
|
|
'suspended' # Process or thread is in a suspended state as far
|
|
)[s]
|
|
except:
|
|
return 'unknown(%s)' % s
|
|
|
|
def stopReason(self, s):
|
|
try:
|
|
return (
|
|
'invalid',
|
|
'none',
|
|
'trace',
|
|
'breakpoint',
|
|
'watchpoint',
|
|
'signal',
|
|
'exception',
|
|
'exec',
|
|
'plancomplete',
|
|
'threadexiting',
|
|
'instrumentation',
|
|
)[s]
|
|
except:
|
|
return 'unknown(%s)' % s
|
|
|
|
def isSimpleType(self, typeobj):
|
|
typeClass = typeobj.GetTypeClass()
|
|
return typeClass == lldb.eTypeClassBuiltin
|
|
|
|
def childWithName(self, value, name):
|
|
child = value.GetChildMemberWithName(name)
|
|
return child if child.IsValid() else None
|
|
|
|
def simpleValue(self, value):
|
|
return str(value.value)
|
|
|
|
def childAt(self, value, index):
|
|
return value.GetChildAtIndex(index)
|
|
|
|
def fieldAt(self, type, index):
|
|
return type.GetFieldAtIndex(index)
|
|
|
|
def pointerValue(self, value):
|
|
return value.GetValueAsUnsigned()
|
|
|
|
def enumExpression(self, enumType, enumValue):
|
|
ns = self.qtNamespace()
|
|
return ns + "Qt::" + enumType + "(" \
|
|
+ ns + "Qt::" + enumType + "::" + enumValue + ")"
|
|
|
|
def callHelper(self, value, func, args):
|
|
# args is a tuple.
|
|
arg = ','.join(args)
|
|
#self.warn("CALL: %s -> %s(%s)" % (value, func, arg))
|
|
type = value.type.name
|
|
exp = "((%s*)%s)->%s(%s)" % (type, value.address, func, arg)
|
|
#self.warn("CALL: %s" % exp)
|
|
result = value.CreateValueFromExpression('', exp)
|
|
#self.warn(" -> %s" % result)
|
|
return result
|
|
|
|
def isBadPointer(self, value):
|
|
target = value.dereference()
|
|
return target.GetError().Fail()
|
|
|
|
def makeValue(self, type, *args):
|
|
thread = self.currentThread()
|
|
frame = thread.GetFrameAtIndex(0)
|
|
inner = ','.join(args)
|
|
value = frame.EvaluateExpression(type + '{' + inner + '}')
|
|
#self.warn(" TYPE: %s" % value.type)
|
|
#self.warn(" ADDR: 0x%x" % value.address)
|
|
#self.warn(" VALUE: %s" % value)
|
|
return value
|
|
|
|
def parseAndEvaluate(self, expr):
|
|
thread = self.currentThread()
|
|
frame = thread.GetFrameAtIndex(0)
|
|
return frame.EvaluateExpression(expr)
|
|
|
|
def checkPointer(self, p, align = 1):
|
|
if not self.isNull(p):
|
|
p.Dereference()
|
|
|
|
def isNull(self, p):
|
|
return p.GetValueAsUnsigned() == 0
|
|
|
|
def directBaseClass(self, typeobj, index = 0):
|
|
result = typeobj.GetDirectBaseClassAtIndex(index).GetType()
|
|
return result if result.IsValid() else None
|
|
|
|
def templateArgument(self, typeobj, index):
|
|
type = typeobj.GetTemplateArgumentType(index)
|
|
if type.IsValid():
|
|
return type
|
|
inner = self.extractTemplateArgument(typeobj.GetName(), index)
|
|
return self.lookupType(inner)
|
|
|
|
def numericTemplateArgument(self, typeobj, index):
|
|
# There seems no API to extract the numeric value.
|
|
inner = self.extractTemplateArgument(typeobj.GetName(), index)
|
|
innerType = typeobj.GetTemplateArgumentType(index)
|
|
basicType = innerType.GetBasicType()
|
|
value = toInteger(inner)
|
|
# Clang writes 'int' and '0xfffffff' into the debug info
|
|
# LLDB manages to read a value of 0xfffffff...
|
|
if basicType == lldb.eBasicTypeInt and value >= 0x8000000:
|
|
value -= 0x100000000
|
|
return value
|
|
|
|
def isReferenceType(self, typeobj):
|
|
return typeobj.IsReferenceType()
|
|
|
|
def isStructType(self, typeobj):
|
|
return typeobj.GetTypeClass() in (lldb.eTypeClassStruct, lldb.eTypeClassClass)
|
|
|
|
def isWindowsTarget(self):
|
|
return False
|
|
|
|
def isQnxTarget(self):
|
|
return False
|
|
|
|
def isArmArchitecture(self):
|
|
return False
|
|
|
|
def qtVersionAndNamespace(self):
|
|
for func in self.target.FindFunctions('qVersion'):
|
|
name = func.GetSymbol().GetName()
|
|
if name.endswith('()'):
|
|
name = name[:-2]
|
|
if name.count(':') > 2:
|
|
continue
|
|
|
|
qtNamespace = name[:name.find('qVersion')]
|
|
self.qtNamespace = lambda: qtNamespace
|
|
|
|
options = lldb.SBExpressionOptions()
|
|
res = self.target.EvaluateExpression(name + '()', options)
|
|
|
|
if not res.IsValid() or not res.GetType().IsPointerType():
|
|
exp = '((const char*())%s)()' % name
|
|
res = self.target.EvaluateExpression(exp, options)
|
|
|
|
if not res.IsValid() or not res.GetType().IsPointerType():
|
|
exp = '((const char*())_Z8qVersionv)()'
|
|
res = self.target.EvaluateExpression(exp, options)
|
|
|
|
if not res.IsValid() or not res.GetType().IsPointerType():
|
|
continue
|
|
|
|
version = str(res)
|
|
if version.count('.') != 2:
|
|
continue
|
|
|
|
version.replace("'", '"') # Both seem possible
|
|
version = version[version.find('"')+1:version.rfind('"')]
|
|
|
|
(major, minor, patch) = version.split('.')
|
|
qtVersion = 0x10000 * int(major) + 0x100 * int(minor) + int(patch)
|
|
self.qtVersion = lambda: qtVersion
|
|
|
|
return (qtNamespace, qtVersion)
|
|
|
|
return ('', 0x50200)
|
|
|
|
def qtNamespace(self):
|
|
return self.qtVersionAndNamespace()[0]
|
|
|
|
def qtVersion(self):
|
|
self.qtVersionAndNamespace()
|
|
return self.qtVersionAndNamespace()[1]
|
|
|
|
def intSize(self):
|
|
return 4
|
|
|
|
def ptrSize(self):
|
|
return self.target.GetAddressByteSize()
|
|
|
|
def intType(self):
|
|
return self.target.GetBasicType(lldb.eBasicTypeInt)
|
|
|
|
def int64Type(self):
|
|
return self.target.GetBasicType(lldb.eBasicTypeLongLong)
|
|
|
|
def charType(self):
|
|
return self.target.GetBasicType(lldb.eBasicTypeChar)
|
|
|
|
def charPtrType(self):
|
|
return self.target.GetBasicType(lldb.eBasicTypeChar).GetPointerType()
|
|
|
|
def voidPtrType(self):
|
|
return self.target.GetBasicType(lldb.eBasicVoid).GetPointerType()
|
|
|
|
def sizetType(self):
|
|
if self.sizetType_ is None:
|
|
self.sizetType_ = self.lookupType('size_t')
|
|
return self.sizetType_
|
|
|
|
def addressOf(self, value):
|
|
return int(value.GetLoadAddress())
|
|
|
|
def extractUInt(self, address):
|
|
error = lldb.SBError()
|
|
return int(self.process.ReadUnsignedFromMemory(address, 4, error))
|
|
|
|
def extractInt(self, address):
|
|
i = self.extractUInt(address)
|
|
if i >= 0x80000000:
|
|
i -= 0x100000000
|
|
return i
|
|
|
|
def extractUInt64(self, address):
|
|
error = lldb.SBError()
|
|
return int(self.process.ReadUnsignedFromMemory(address, 8, error))
|
|
|
|
def extractInt64(self, address):
|
|
i = self.extractUInt64(address)
|
|
if i >= 0x8000000000000000:
|
|
i -= 0x10000000000000000
|
|
return i
|
|
|
|
def extractByte(self, address):
|
|
error = lldb.SBError()
|
|
return int(self.process.ReadUnsignedFromMemory(address, 1, error) & 0xFF)
|
|
|
|
def handleCommand(self, command):
|
|
result = lldb.SBCommandReturnObject()
|
|
self.debugger.GetCommandInterpreter().HandleCommand(command, result)
|
|
success = result.Succeeded()
|
|
if success:
|
|
self.report('output="%s"' % result.GetOutput())
|
|
else:
|
|
self.report('error="%s"' % result.GetError())
|
|
self.reportData()
|
|
|
|
def put(self, stuff):
|
|
sys.stdout.write(stuff)
|
|
|
|
def isMovableType(self, type):
|
|
if type.GetTypeClass() in (lldb.eTypeClassBuiltin, lldb.eTypeClassPointer):
|
|
return True
|
|
return self.isKnownMovableType(self.stripNamespaceFromType(type.GetName()))
|
|
|
|
def putPointerValue(self, value):
|
|
# Use a lower priority
|
|
if value is None:
|
|
self.putEmptyValue(-1)
|
|
else:
|
|
self.putValue("0x%x" % value.Dereference())
|
|
|
|
def putSimpleValue(self, value, encoding = None, priority = 0):
|
|
self.putValue(value.GetValue(), encoding, priority)
|
|
|
|
def simpleEncoding(self, typeobj):
|
|
code = typeobj.GetTypeClass()
|
|
size = typeobj.sizeof
|
|
if code == lldb.eTypeClassBuiltin:
|
|
name = str(typeobj)
|
|
if name == "float":
|
|
return Hex2EncodedFloat4
|
|
if name == "double":
|
|
return Hex2EncodedFloat8
|
|
if name.find("unsigned") >= 0:
|
|
if size == 1:
|
|
return Hex2EncodedUInt1
|
|
if size == 2:
|
|
return Hex2EncodedUInt2
|
|
if size == 4:
|
|
return Hex2EncodedUInt4
|
|
if size == 8:
|
|
return Hex2EncodedUInt8
|
|
else:
|
|
if size == 1:
|
|
return Hex2EncodedInt1
|
|
if size == 2:
|
|
return Hex2EncodedInt2
|
|
if size == 4:
|
|
return Hex2EncodedInt4
|
|
if size == 8:
|
|
return Hex2EncodedInt8
|
|
return None
|
|
|
|
def createPointerValue(self, address, pointeeType):
|
|
addr = int(address) & 0xFFFFFFFFFFFFFFFF
|
|
sbaddr = lldb.SBAddress(addr, self.target)
|
|
# Any type.
|
|
# FIXME: This can be replaced with self.target.CreateValueFromExpression
|
|
# as soon as we drop support for lldb builds not having that (~Xcode 6.1)
|
|
dummy = self.target.CreateValueFromAddress('@', sbaddr, self.target.FindFirstType('char'))
|
|
return dummy.CreateValueFromExpression('', '(%s*)%s' % (pointeeType, addr))
|
|
|
|
def createValue(self, address, referencedType):
|
|
addr = int(address) & 0xFFFFFFFFFFFFFFFF
|
|
sbaddr = lldb.SBAddress(addr, self.target)
|
|
return self.target.CreateValueFromAddress('@', sbaddr, referencedType)
|
|
|
|
def childRange(self):
|
|
if self.currentMaxNumChild is None:
|
|
return xrange(0, self.currentNumChild)
|
|
return xrange(min(self.currentMaxNumChild, self.currentNumChild))
|
|
|
|
def canonicalTypeName(self, name):
|
|
return re.sub('\\bconst\\b', '', name).replace(' ', '')
|
|
|
|
def lookupType(self, name):
|
|
#self.warn("LOOKUP TYPE NAME: %s" % name)
|
|
typeobj = self.target.FindFirstType(name)
|
|
if typeobj.IsValid():
|
|
return typeobj
|
|
typeobj = self.target.FindFirstType(name + '*')
|
|
if typeobj.IsValid():
|
|
return typeob.GetPointeeType()
|
|
typeobj = self.target.FindFirstType(name + '&')
|
|
if typeobj.IsValid():
|
|
return typeob.GetReferencedType()
|
|
if name.endswith('*'):
|
|
typeobj = self.target.FindFirstType(name[:-1].strip())
|
|
if typeobj.IsValid():
|
|
return typeobj.GetPointerType()
|
|
#self.warn("LOOKUP RESULT: %s" % typeobj.name)
|
|
#self.warn("LOOKUP VALID: %s" % typeobj.IsValid())
|
|
needle = self.canonicalTypeName(name)
|
|
#self.warn("NEEDLE: %s " % needle)
|
|
for i in xrange(self.target.GetNumModules()):
|
|
module = self.target.GetModuleAtIndex(i)
|
|
# SBModule.GetType is new somewhere after early 300.x
|
|
# So this may fail.
|
|
for t in module.GetTypes():
|
|
n = self.canonicalTypeName(t.GetName())
|
|
if n == needle:
|
|
#self.warn("FOUND TYPE DIRECT 2: %s " % t)
|
|
return t
|
|
if n == needle + '*':
|
|
#self.warn("FOUND TYPE BY POINTER 2: %s " % t.GetPointeeType())
|
|
return t.GetPointeeType()
|
|
if n == needle + '&':
|
|
#self.warn("FOUND TYPE BY REFERENCE 2: %s " % t)
|
|
return t.GetDereferencedType()
|
|
#self.warn("NOT FOUND: %s " % needle)
|
|
return None
|
|
|
|
def setupInferior(self, args):
|
|
self.reportToken(args)
|
|
error = lldb.SBError()
|
|
|
|
self.executable_ = args['executable']
|
|
self.startMode_ = args.get('startMode', 1)
|
|
self.breakOnMain_ = args.get('breakOnMain', 0)
|
|
self.useTerminal_ = args.get('useTerminal', 0)
|
|
self.processArgs_ = args.get('processArgs', [])
|
|
self.processArgs_ = map(lambda x: self.hexdecode(x), self.processArgs_)
|
|
self.attachPid_ = args.get('attachPid', 0)
|
|
self.sysRoot_ = args.get('sysRoot', '')
|
|
self.remoteChannel_ = args.get('remoteChannel', '')
|
|
self.platform_ = args.get('platform', '')
|
|
|
|
self.ignoreStops = 0
|
|
self.silentStops = 0
|
|
if platform.system() == "Linux":
|
|
if self.startMode_ == AttachCore:
|
|
pass
|
|
else:
|
|
if self.useTerminal_:
|
|
self.ignoreStops = 2
|
|
else:
|
|
self.silentStops = 1
|
|
|
|
else:
|
|
if self.useTerminal_:
|
|
self.ignoreStops = 1
|
|
|
|
if self.platform_:
|
|
self.debugger.SetCurrentPlatform(self.platform_)
|
|
# sysroot has to be set *after* the platform
|
|
if self.sysRoot_:
|
|
self.debugger.SetCurrentPlatformSDKRoot(self.sysRoot_)
|
|
|
|
|
|
if os.path.isfile(self.executable_):
|
|
self.target = self.debugger.CreateTarget(self.executable_, None, None, True, error)
|
|
else:
|
|
self.target = self.debugger.CreateTarget(None, None, None, True, error)
|
|
|
|
if self.target.IsValid():
|
|
for bp in args['bkpts']:
|
|
self.insertBreakpoint(bp)
|
|
|
|
state = "inferiorsetupok" if self.target.IsValid() else "inferiorsetupfailed"
|
|
self.report('state="%s",msg="%s",exe="%s"' % (state, error, self.executable_))
|
|
|
|
def runEngine(self, args):
|
|
self.prepare(args)
|
|
s = threading.Thread(target=self.loop, args=[])
|
|
s.start()
|
|
|
|
def prepare(self, args):
|
|
self.reportToken(args)
|
|
error = lldb.SBError()
|
|
listener = self.debugger.GetListener()
|
|
|
|
if self.attachPid_ > 0:
|
|
attachInfo = lldb.SBAttachInfo(self.attachPid_)
|
|
self.process = self.target.Attach(attachInfo, error)
|
|
if not error.Success():
|
|
self.reportState("inferiorrunfailed")
|
|
return
|
|
self.report('pid="%s"' % self.process.GetProcessID())
|
|
# Even if it stops it seems that LLDB assumes it is running
|
|
# and later detects that it did stop after all, so it is be
|
|
# better to mirror that and wait for the spontaneous stop.
|
|
self.reportState("enginerunandinferiorrunok")
|
|
elif self.startMode_ == AttachToRemoteServer or self.startMode_ == AttachToRemoteProcess:
|
|
self.process = self.target.ConnectRemote(
|
|
self.debugger.GetListener(),
|
|
self.remoteChannel_, None, error)
|
|
if not error.Success():
|
|
self.reportError(error)
|
|
self.reportState("enginerunfailed")
|
|
return
|
|
# Even if it stops it seems that LLDB assumes it is running
|
|
# and later detects that it did stop after all, so it is be
|
|
# better to mirror that and wait for the spontaneous stop.
|
|
self.reportState("enginerunandinferiorrunok")
|
|
elif self.startMode_ == AttachCore:
|
|
coreFile = args.get('coreFile', '');
|
|
self.process = self.target.LoadCore(coreFile)
|
|
self.reportState("enginerunokandinferiorunrunnable")
|
|
#self.reportContinuation(args)
|
|
else:
|
|
launchInfo = lldb.SBLaunchInfo(self.processArgs_)
|
|
launchInfo.SetWorkingDirectory(os.getcwd())
|
|
environmentList = [key + "=" + value for key,value in os.environ.items()]
|
|
launchInfo.SetEnvironmentEntries(environmentList, False)
|
|
if self.breakOnMain_:
|
|
self.createBreakpointAtMain()
|
|
self.process = self.target.Launch(launchInfo, error)
|
|
if not error.Success():
|
|
self.reportError(error)
|
|
self.reportState("enginerunfailed")
|
|
return
|
|
self.report('pid="%s"' % self.process.GetProcessID())
|
|
self.reportState("enginerunandinferiorrunok")
|
|
|
|
def loop(self):
|
|
event = lldb.SBEvent()
|
|
listener = self.debugger.GetListener()
|
|
while True:
|
|
if listener.WaitForEvent(10000000, event):
|
|
self.handleEvent(event)
|
|
else:
|
|
warn('TIMEOUT')
|
|
|
|
def describeError(self, error):
|
|
desc = lldb.SBStream()
|
|
error.GetDescription(desc)
|
|
result = 'error={type="%s"' % error.GetType()
|
|
result += ',code="%s"' % error.GetError()
|
|
result += ',desc="%s"}' % desc.GetData()
|
|
return result
|
|
|
|
def reportError(self, error):
|
|
self.report(self.describeError(error))
|
|
if error.GetType():
|
|
self.reportStatus(error.GetCString())
|
|
|
|
def currentThread(self):
|
|
return None if self.process is None else self.process.GetSelectedThread()
|
|
|
|
def currentFrame(self):
|
|
thread = self.currentThread()
|
|
return None if thread is None else thread.GetSelectedFrame()
|
|
|
|
def reportLocation(self, frame):
|
|
if int(frame.pc) != 0xffffffffffffffff:
|
|
file = fileName(frame.line_entry.file)
|
|
line = frame.line_entry.line
|
|
self.report('location={file="%s",line="%s",addr="%s"}'
|
|
% (file, line, frame.pc))
|
|
|
|
def firstStoppedThread(self):
|
|
for i in xrange(0, self.process.GetNumThreads()):
|
|
thread = self.process.GetThreadAtIndex(i)
|
|
reason = thread.GetStopReason()
|
|
if (reason == lldb.eStopReasonBreakpoint or
|
|
reason == lldb.eStopReasonException or
|
|
reason == lldb.eStopReasonPlanComplete or
|
|
reason == lldb.eStopReasonSignal or
|
|
reason == lldb.eStopReasonWatchpoint):
|
|
return thread
|
|
return None
|
|
|
|
def reportThreads(self, args):
|
|
self.reportToken(args)
|
|
result = 'threads={threads=['
|
|
for i in xrange(0, self.process.GetNumThreads()):
|
|
thread = self.process.GetThreadAtIndex(i)
|
|
if thread.is_stopped:
|
|
state = "stopped"
|
|
elif thread.is_suspended:
|
|
state = "suspended"
|
|
else:
|
|
state = "unknown"
|
|
reason = thread.GetStopReason()
|
|
result += '{id="%d"' % thread.GetThreadID()
|
|
result += ',index="%s"' % i
|
|
result += ',details="%s"' % thread.GetQueueName()
|
|
result += ',stop-reason="%s"' % self.stopReason(thread.GetStopReason())
|
|
result += ',state="%s"' % state
|
|
result += ',name="%s"' % thread.GetName()
|
|
result += ',frame={'
|
|
frame = thread.GetFrameAtIndex(0)
|
|
result += 'pc="0x%x"' % frame.pc
|
|
result += ',addr="0x%x"' % frame.pc
|
|
result += ',fp="0x%x"' % frame.fp
|
|
result += ',func="%s"' % frame.GetFunctionName()
|
|
result += ',line="%s"' % frame.line_entry.line
|
|
result += ',fullname="%s"' % fileName(frame.line_entry.file)
|
|
result += ',file="%s"' % fileName(frame.line_entry.file)
|
|
result += '}},'
|
|
|
|
result += ']},'
|
|
self.report(result)
|
|
|
|
def reportCurrentThread(self, args):
|
|
self.reportToken(args)
|
|
self.report('current-thread={id="%s"}' % self.currentThread().id)
|
|
|
|
def firstUsableFrame(self, thread):
|
|
for i in xrange(10):
|
|
frame = thread.GetFrameAtIndex(i)
|
|
lineEntry = frame.GetLineEntry()
|
|
line = lineEntry.GetLine()
|
|
if line != 0:
|
|
return i
|
|
return None
|
|
|
|
def reportStack(self, args):
|
|
self.reportToken(args)
|
|
if not self.process:
|
|
self.report('msg="No process"')
|
|
return
|
|
thread = self.currentThread()
|
|
if not thread:
|
|
self.report('msg="No thread"')
|
|
return
|
|
|
|
self.reportLocation(thread.GetFrameAtIndex(0)) # FIXME
|
|
|
|
isNativeMixed = int(args.get('nativeMixed', 0))
|
|
|
|
limit = args.get('stacklimit', -1)
|
|
(n, isLimited) = (limit, True) if limit > 0 else (thread.GetNumFrames(), False)
|
|
self.currentCallContext = None
|
|
result = 'stack={current-thread="%s"' % thread.GetThreadID()
|
|
result += ',frames=['
|
|
for i in xrange(n):
|
|
frame = thread.GetFrameAtIndex(i)
|
|
if not frame.IsValid():
|
|
isLimited = False
|
|
break
|
|
|
|
lineEntry = frame.GetLineEntry()
|
|
lineNumber = lineEntry.GetLine()
|
|
|
|
pc = frame.GetPC()
|
|
level = frame.idx
|
|
addr = frame.GetPCAddress().GetLoadAddress(self.target)
|
|
functionName = frame.GetFunctionName()
|
|
fullname = fileName(lineEntry.file)
|
|
usable = None
|
|
language = None
|
|
|
|
if isNativeMixed:
|
|
if self.isReportableQmlFrame(functionName):
|
|
engine = frame.FindVariable("engine")
|
|
self.context = engine
|
|
h = self.extractQmlLocation(engine)
|
|
pc = 0
|
|
functionName = h['functionName']
|
|
fullname = h['fileName']
|
|
lineNumber = h['lineNumber']
|
|
addr = h['context']
|
|
language = 'js'
|
|
|
|
elif not functionName is None:
|
|
if functionName.startswith("qt_v4"):
|
|
usable = 0
|
|
elif functionName.find("QV4::") >= 0:
|
|
usable = 0
|
|
|
|
result += '{pc="0x%x"' % pc
|
|
result += ',level="%d"' % level
|
|
result += ',addr="0x%x"' % addr
|
|
if not usable is None:
|
|
result += ',usable="%s"' % usable
|
|
result += ',func="%s"' % functionName
|
|
result += ',line="%d"' % lineNumber
|
|
if not language is None:
|
|
result += ',language="%s"' % language
|
|
result += ',fullname="%s"' % fullname
|
|
result += ',file="%s"},' % fullname
|
|
result += ']'
|
|
result += ',hasmore="%d"' % isLimited
|
|
result += ',limit="%d"' % limit
|
|
result += '}'
|
|
self.report(result)
|
|
self.reportContinuation(args)
|
|
|
|
def reportToken(self, args):
|
|
if "token" in args:
|
|
# Unusual syntax intended, to support the double-click in left
|
|
# logview pane feature.
|
|
self.report('token(\"%s\")' % args["token"])
|
|
|
|
def reportContinuation(self, args):
|
|
if not self.isShuttingDown_ and "continuation" in args:
|
|
self.report('continuation=\"%s\"' % args["continuation"])
|
|
|
|
def extractBlob(self, base, size):
|
|
if size == 0:
|
|
return Blob("")
|
|
base = int(base) & 0xFFFFFFFFFFFFFFFF
|
|
size = int(size) & 0xFFFFFFFF
|
|
error = lldb.SBError()
|
|
return Blob(self.process.ReadMemory(base, size, error))
|
|
|
|
def toBlob(self, value):
|
|
data = value.GetData()
|
|
size = int(data.GetByteSize())
|
|
buf = bytearray(struct.pack('x' * size))
|
|
error = lldb.SBError()
|
|
#data.ReadRawData(error, 0, buf)
|
|
for i in range(size):
|
|
buf[i] = data.GetUnsignedInt8(error, i)
|
|
return Blob(bytes(buf))
|
|
|
|
def mangleName(self, typeName):
|
|
return '_ZN%sE' % ''.join(map(lambda x: "%d%s" % (len(x), x), typeName.split('::')))
|
|
|
|
def findStaticMetaObject(self, typeName):
|
|
symbolName = self.mangleName(typeName + '::staticMetaObject')
|
|
return self.target.FindFirstGlobalVariable(symbolName)
|
|
|
|
def findSymbol(self, symbolName):
|
|
return self.target.FindFirstGlobalVariable(symbolName)
|
|
|
|
def stripNamespaceFromType(self, typeName):
|
|
#type = self.stripClassTag(typeName)
|
|
type = typeName
|
|
ns = self.qtNamespace()
|
|
if len(ns) > 0 and type.startswith(ns):
|
|
type = type[len(ns):]
|
|
pos = type.find("<")
|
|
# FIXME: make it recognize foo<A>::bar<B>::iterator?
|
|
while pos != -1:
|
|
pos1 = type.rfind(">", pos)
|
|
type = type[0:pos] + type[pos1+1:]
|
|
pos = type.find("<")
|
|
if type.startswith("const "):
|
|
type = type[6:]
|
|
if type.startswith("volatile "):
|
|
type = type[9:]
|
|
return type
|
|
|
|
def putSubItem(self, component, value, tryDynamic=True):
|
|
if not value.IsValid():
|
|
self.warn("INVALID SUBITEM: %s" % value.GetName())
|
|
return
|
|
with SubItem(self, component):
|
|
self.putItem(value, tryDynamic)
|
|
|
|
def putAddress(self, addr):
|
|
#if int(addr) == 0xffffffffffffffff:
|
|
# raise RuntimeError("Illegal address")
|
|
if self.currentPrintsAddress and not addr is None:
|
|
self.put('addr="0x%x",' % int(addr))
|
|
|
|
def isFunctionType(self, typeobj):
|
|
if self.isGoodLldb:
|
|
return typeobj.IsFunctionType()
|
|
#warn("TYPE: %s" % typeobj)
|
|
return False
|
|
|
|
def putItem(self, value, tryDynamic=True):
|
|
typeName = value.GetType().GetUnqualifiedType().GetName()
|
|
if self.isGoodLldb:
|
|
value.SetPreferDynamicValue(tryDynamic)
|
|
typeClass = value.GetType().GetTypeClass()
|
|
|
|
if tryDynamic:
|
|
self.putAddress(value.GetLoadAddress())
|
|
|
|
# Handle build-in LLDB visualizers if wanted.
|
|
if False and self.useLldbDumpers and value.GetTypeSynthetic().IsValid():
|
|
# FIXME: print "official" summary?
|
|
summary = value.GetTypeSummary()
|
|
if summary.IsValid():
|
|
warn("DATA: %s" % summary.GetData())
|
|
if self.isGoodLldb:
|
|
value.SetPreferSyntheticValue(False)
|
|
provider = value.GetTypeSynthetic()
|
|
data = provider.GetData()
|
|
formatter = eval(data)(value, {})
|
|
formatter.update()
|
|
numchild = formatter.num_children()
|
|
self.put('iname="%s",' % self.currentIName)
|
|
self.putType(typeName)
|
|
self.put('numchild="%s",' % numchild)
|
|
self.put('addr="0x%x",' % value.GetLoadAddress())
|
|
self.putItemCount(numchild)
|
|
if self.currentIName in self.expandedINames:
|
|
with Children(self):
|
|
for i in xrange(numchild):
|
|
child = formatter.get_child_at_index(i)
|
|
with SubItem(self, i):
|
|
self.putItem(child)
|
|
return
|
|
|
|
# Typedefs
|
|
if typeClass == lldb.eTypeClassTypedef:
|
|
if typeName in self.qqDumpers:
|
|
self.putType(typeName)
|
|
self.context = value
|
|
self.qqDumpers[typeName](self, value)
|
|
return
|
|
realType = value.GetType()
|
|
if hasattr(realType, 'GetCanonicalType'):
|
|
baseType = realType.GetCanonicalType()
|
|
if baseType != realType:
|
|
baseValue = value.Cast(baseType.unqualified())
|
|
self.putItem(baseValue)
|
|
self.putBetterType(realType)
|
|
return
|
|
|
|
# Our turf now.
|
|
if self.isGoodLldb:
|
|
value.SetPreferSyntheticValue(False)
|
|
|
|
# Arrays
|
|
if typeClass == lldb.eTypeClassArray:
|
|
self.putCStyleArray(value)
|
|
return
|
|
|
|
# Vectors like char __attribute__ ((vector_size (8)))
|
|
if typeClass == lldb.eTypeClassVector:
|
|
self.putCStyleArray(value)
|
|
return
|
|
|
|
# References
|
|
if value.GetType().IsReferenceType():
|
|
type = value.GetType().GetDereferencedType().unqualified()
|
|
addr = value.GetValueAsUnsigned()
|
|
#warn("FROM: %s" % value)
|
|
#warn("ADDR: 0x%x" % addr)
|
|
#warn("TYPE: %s" % type)
|
|
# Works:
|
|
#item = self.currentThread().GetSelectedFrame().EvaluateExpression(
|
|
# "(%s*)0x%x" % (type, addr)).Dereference()
|
|
# Works:
|
|
item = value.CreateValueFromExpression(None,
|
|
"(%s*)0x%x" % (type, addr), lldb.SBExpressionOptions()).Dereference()
|
|
# Does not work:
|
|
#item = value.CreateValueFromAddress(None, addr, type)
|
|
# Does not work:
|
|
#item = value.Cast(type.GetPointerType()).Dereference()
|
|
#warn("TOOO: %s" % item)
|
|
self.putItem(item)
|
|
self.putBetterType(value.GetTypeName())
|
|
return
|
|
|
|
# Pointers
|
|
if value.GetType().IsPointerType():
|
|
self.putFormattedPointer(value)
|
|
return
|
|
|
|
# Chars
|
|
if typeClass == lldb.eTypeClassBuiltin:
|
|
basicType = value.GetType().GetBasicType()
|
|
if basicType == lldb.eBasicTypeChar:
|
|
self.putValue(value.GetValueAsUnsigned())
|
|
self.putType(typeName)
|
|
self.putNumChild(0)
|
|
return
|
|
if basicType == lldb.eBasicTypeSignedChar:
|
|
self.putValue(value.GetValueAsSigned())
|
|
self.putType(typeName)
|
|
self.putNumChild(0)
|
|
return
|
|
|
|
#warn("VALUE: %s" % value)
|
|
#warn("FANCY: %s" % self.useFancy)
|
|
if self.tryPutPrettyItem(typeName, value):
|
|
return
|
|
|
|
# Normal value
|
|
#numchild = 1 if value.MightHaveChildren() else 0
|
|
numchild = value.GetNumChildren()
|
|
self.putType(typeName)
|
|
self.putEmptyValue(-1)
|
|
staticMetaObject = self.extractStaticMetaObject(value.GetType())
|
|
if staticMetaObject:
|
|
self.context = value
|
|
self.putQObjectNameValue(value)
|
|
else:
|
|
v = value.GetValue()
|
|
if v:
|
|
self.putValue(v)
|
|
|
|
self.put('numchild="%s",' % numchild)
|
|
|
|
if self.currentIName in self.expandedINames:
|
|
with Children(self):
|
|
self.putFields(value)
|
|
if staticMetaObject:
|
|
self.putQObjectGuts(value, staticMetaObject)
|
|
|
|
def warn(self, msg):
|
|
self.put('{name="%s",value="",type="",numchild="0"},' % msg)
|
|
|
|
def putFields(self, value):
|
|
# Suppress printing of 'name' field for arrays.
|
|
if value.GetType().GetTypeClass() == lldb.eTypeClassArray:
|
|
for i in xrange(value.GetNumChildren()):
|
|
child = value.GetChildAtIndex(i)
|
|
with UnnamedSubItem(self, str(i)):
|
|
self.putItem(child)
|
|
return
|
|
|
|
n = value.GetNumChildren()
|
|
m = value.GetType().GetNumberOfDirectBaseClasses()
|
|
if n > 10000:
|
|
n = 10000
|
|
# seems to happen in the 'inheritance' autotest
|
|
if m > n:
|
|
m = n
|
|
for i in xrange(m):
|
|
child = value.GetChildAtIndex(i)
|
|
with UnnamedSubItem(self, "@%d" % (i + 1)):
|
|
self.put('iname="%s",' % self.currentIName)
|
|
self.put('name="[%s]",' % child.name)
|
|
self.putItem(child)
|
|
for i in xrange(m, n):
|
|
#for i in range(n):
|
|
child = value.GetChildAtIndex(i)
|
|
# Only needed in the QVariant4 test.
|
|
if int(child.GetLoadAddress()) == 0xffffffffffffffff:
|
|
typeClass = child.GetType().GetTypeClass()
|
|
if typeClass != lldb.eTypeClassBuiltin:
|
|
field = value.GetType().GetFieldAtIndex(i)
|
|
addr = value.GetLoadAddress() + field.GetOffsetInBytes()
|
|
child = value.CreateValueFromAddress(child.GetName(), addr, child.GetType())
|
|
if child.IsValid(): # FIXME: Anon members?
|
|
with SubItem(self, child):
|
|
self.putItem(child)
|
|
|
|
def reportVariables(self, args = {}):
|
|
with self.outputLock:
|
|
sys.stdout.write("@\n")
|
|
self.reportVariablesHelper(args)
|
|
sys.stdout.write("@\n")
|
|
|
|
def reportVariablesHelper(self, args = {}):
|
|
frame = self.currentFrame()
|
|
if frame is None:
|
|
return
|
|
|
|
partialVariable = args.get("partialVariable", "")
|
|
isPartial = len(partialVariable) > 0
|
|
|
|
self.currentIName = 'local'
|
|
self.put('all={data=[')
|
|
self.anonNumber = 0
|
|
shadowed = {}
|
|
ids = {} # Filter out duplicates entries at the same address.
|
|
|
|
# FIXME: Implement shortcut for partial updates.
|
|
#if isPartial:
|
|
# values = [frame.FindVariable(partialVariable)]
|
|
#else:
|
|
if True:
|
|
values = list(frame.GetVariables(True, True, False, False))
|
|
values.reverse() # To get shadowed vars numbered backwards.
|
|
|
|
for value in values:
|
|
if not value.IsValid():
|
|
continue
|
|
name = value.GetName()
|
|
id = "%s:0x%x" % (name, value.GetLoadAddress())
|
|
if id in ids:
|
|
continue
|
|
ids[id] = True
|
|
#if self.dummyValue is None:
|
|
# self.dummyValue = value
|
|
if name is None:
|
|
warn("NO NAME FOR VALUE: %s" % value)
|
|
continue
|
|
if name in shadowed:
|
|
level = shadowed[name]
|
|
shadowed[name] = level + 1
|
|
name += "@%s" % level
|
|
else:
|
|
shadowed[name] = 1
|
|
|
|
if name == "argv" and value.GetType().GetName() == "char **":
|
|
self.putSpecialArgv(value)
|
|
else:
|
|
with SubItem(self, name):
|
|
self.put('iname="%s",' % self.currentIName)
|
|
self.putItem(value)
|
|
|
|
with SubItem(self, '[statics]'):
|
|
self.put('iname="%s",' % self.currentIName)
|
|
self.putEmptyValue()
|
|
self.putNumChild(1)
|
|
if self.isExpanded():
|
|
with Children(self):
|
|
statics = frame.GetVariables(False, False, True, False)
|
|
if len(statics):
|
|
for i in xrange(len(statics)):
|
|
staticVar = statics[i]
|
|
typename = staticVar.GetType().GetName()
|
|
name = staticVar.GetName()
|
|
with SubItem(self, i):
|
|
self.put('name="%s",' % name)
|
|
self.put('iname="%s",' % self.currentIName)
|
|
self.putItem(staticVar)
|
|
else:
|
|
with SubItem(self, "None"):
|
|
self.putEmptyValue()
|
|
self.putNumChild(0)
|
|
|
|
self.handleWatches(args)
|
|
|
|
self.put('],partial="%d"}' % isPartial)
|
|
|
|
def reportData(self, _ = None):
|
|
if self.process is None:
|
|
self.report('process="none"')
|
|
else:
|
|
state = self.process.GetState()
|
|
if state == lldb.eStateStopped:
|
|
self.reportVariables()
|
|
|
|
def reportRegisters(self, _ = None):
|
|
if self.process is None:
|
|
self.report('process="none"')
|
|
else:
|
|
frame = self.currentFrame()
|
|
if frame:
|
|
result = 'registers=['
|
|
for group in frame.GetRegisters():
|
|
for reg in group:
|
|
result += '{name="%s"' % reg.GetName()
|
|
value = reg.GetValue()
|
|
if value is None:
|
|
result += ',value=""'
|
|
else:
|
|
result += ',value="%s"' % value
|
|
result += ',size="%s"' % reg.GetByteSize()
|
|
result += ',type="%s"},' % reg.GetType()
|
|
result += ']'
|
|
self.report(result)
|
|
|
|
def report(self, stuff):
|
|
with self.outputLock:
|
|
sys.stdout.write("@\n" + stuff + "@\n")
|
|
|
|
def reportStatus(self, msg):
|
|
self.report('statusmessage="%s"' % msg)
|
|
|
|
def interruptInferior(self, _ = None):
|
|
if self.process is None:
|
|
self.reportStatus("No process to interrupt.")
|
|
return
|
|
self.isInterrupting_ = True
|
|
error = self.process.Stop()
|
|
self.reportError(error)
|
|
|
|
def detachInferior(self, _ = None):
|
|
if self.process is None:
|
|
self.reportStatus("No process to detach from.")
|
|
else:
|
|
error = self.process.Detach()
|
|
self.reportError(error)
|
|
self.reportData()
|
|
|
|
def continueInferior(self, _ = None):
|
|
if self.process is None:
|
|
self.reportStatus("No process to continue.")
|
|
else:
|
|
error = self.process.Continue()
|
|
self.reportError(error)
|
|
# Happens when attaching to GDBserver.
|
|
if not error.Success():
|
|
self.reportState("inferiorill")
|
|
|
|
def quitDebugger(self, _ = None):
|
|
self.reportState("inferiorshutdownrequested")
|
|
self.process.Kill()
|
|
|
|
def handleEvent(self, event):
|
|
out = lldb.SBStream()
|
|
event.GetDescription(out)
|
|
#warn("EVENT: %s" % event)
|
|
eventType = event.GetType()
|
|
msg = lldb.SBEvent.GetCStringFromEvent(event)
|
|
flavor = event.GetDataFlavor()
|
|
state = lldb.SBProcess.GetStateFromEvent(event)
|
|
bp = lldb.SBBreakpoint.GetBreakpointFromEvent(event)
|
|
self.report('event={type="%s",data="%s",msg="%s",flavor="%s",state="%s",bp="%s"}'
|
|
% (eventType, out.GetData(), msg, flavor, self.stateName(state), bp))
|
|
if state != self.eventState:
|
|
self.eventState = state
|
|
if state == lldb.eStateExited:
|
|
if self.isShuttingDown_:
|
|
self.reportState("inferiorshutdownok")
|
|
else:
|
|
self.reportState("inferiorexited")
|
|
self.report('exited={status="%s",desc="%s"}'
|
|
% (self.process.GetExitStatus(), self.process.GetExitDescription()))
|
|
elif state == lldb.eStateStopped:
|
|
stoppedThread = self.firstStoppedThread()
|
|
if stoppedThread:
|
|
#self.report("STOPPED THREAD: %s" % stoppedThread)
|
|
frame = stoppedThread.GetFrameAtIndex(0)
|
|
#self.report("FRAME: %s" % frame)
|
|
function = frame.GetFunction()
|
|
#self.report("FUNCTION: %s" % function)
|
|
if function.GetName() == "qt_v4ResolvePendingBreakpointsHook":
|
|
#self.report("RESOLVER HIT")
|
|
for bp in self.qmlBreakpointResolvers:
|
|
self.qmlBreakpointResolvers[bp]()
|
|
self.target.BreakpointDelete(bp.GetID())
|
|
self.qmlBreakpointResolvers = {}
|
|
self.process.Continue();
|
|
return
|
|
|
|
if self.isInterrupting_:
|
|
self.isInterrupting_ = False
|
|
self.reportState("inferiorstopok")
|
|
elif self.ignoreStops > 0:
|
|
self.ignoreStops -= 1
|
|
self.process.Continue()
|
|
elif self.silentStops > 0:
|
|
self.silentStops -= 1
|
|
#elif bp and bp in self.qmlBreakpointResolvers:
|
|
# self.report("RESOLVER HIT")
|
|
# self.qmlBreakpointResolvers[bp]()
|
|
# self.process.Continue();
|
|
else:
|
|
self.reportState("stopped")
|
|
else:
|
|
self.reportState(self.stateName(state))
|
|
if eventType == lldb.SBProcess.eBroadcastBitStateChanged: # 1
|
|
state = self.process.GetState()
|
|
if state == lldb.eStateStopped:
|
|
stoppedThread = self.firstStoppedThread()
|
|
if stoppedThread:
|
|
self.process.SetSelectedThread(stoppedThread)
|
|
elif eventType == lldb.SBProcess.eBroadcastBitInterrupt: # 2
|
|
pass
|
|
elif eventType == lldb.SBProcess.eBroadcastBitSTDOUT:
|
|
# FIXME: Size?
|
|
msg = self.process.GetSTDOUT(1024)
|
|
self.report('output={channel="stdout",data="%s"}'
|
|
% self.hexencode(msg))
|
|
elif eventType == lldb.SBProcess.eBroadcastBitSTDERR:
|
|
msg = self.process.GetSTDERR(1024)
|
|
self.report('output={channel="stderr",data="%s"}'
|
|
% self.hexencode(msg))
|
|
elif eventType == lldb.SBProcess.eBroadcastBitProfileData:
|
|
pass
|
|
|
|
def reportState(self, state):
|
|
self.report('state="%s"' % state)
|
|
|
|
def describeBreakpoint(self, bp):
|
|
isWatch = isinstance(bp, lldb.SBWatchpoint)
|
|
if isWatch:
|
|
result = 'lldbid="%s"' % (qqWatchpointOffset + bp.GetID())
|
|
else:
|
|
result = 'lldbid="%s"' % bp.GetID()
|
|
if not bp.IsValid():
|
|
return
|
|
result += ',hitcount="%s"' % bp.GetHitCount()
|
|
if hasattr(bp, 'GetThreadID'):
|
|
result += ',threadid="%s"' % bp.GetThreadID()
|
|
if hasattr(bp, 'IsOneShot'):
|
|
result += ',oneshot="%s"' % (1 if bp.IsOneShot() else 0)
|
|
if hasattr(bp, 'GetCondition'):
|
|
cond = bp.GetCondition()
|
|
result += ',condition="%s"' % self.hexencode("" if cond is None else cond)
|
|
result += ',enabled="%s"' % (1 if bp.IsEnabled() else 0)
|
|
result += ',valid="%s"' % (1 if bp.IsValid() else 0)
|
|
result += ',ignorecount="%s"' % bp.GetIgnoreCount()
|
|
result += ',locations=['
|
|
lineEntry = None
|
|
if hasattr(bp, 'GetNumLocations'):
|
|
for i in xrange(bp.GetNumLocations()):
|
|
loc = bp.GetLocationAtIndex(i)
|
|
addr = loc.GetAddress()
|
|
lineEntry = addr.GetLineEntry()
|
|
result += '{locid="%s"' % loc.GetID()
|
|
result += ',func="%s"' % addr.GetFunction().GetName()
|
|
result += ',enabled="%s"' % (1 if loc.IsEnabled() else 0)
|
|
result += ',resolved="%s"' % (1 if loc.IsResolved() else 0)
|
|
result += ',valid="%s"' % (1 if loc.IsValid() else 0)
|
|
result += ',ignorecount="%s"' % loc.GetIgnoreCount()
|
|
result += ',file="%s"' % lineEntry.GetFileSpec()
|
|
result += ',line="%s"' % lineEntry.GetLine()
|
|
result += ',addr="%s"},' % addr.GetFileAddress()
|
|
result += ']'
|
|
if lineEntry is not None:
|
|
result += ',file="%s"' % lineEntry.GetFileSpec()
|
|
result += ',line="%s"' % lineEntry.GetLine()
|
|
return result
|
|
|
|
def createBreakpointAtMain(self):
|
|
return self.target.BreakpointCreateByName(
|
|
"main", self.target.GetExecutable().GetFilename())
|
|
|
|
def insertBreakpoint(self, args):
|
|
bpType = args["type"]
|
|
if bpType == BreakpointByFileAndLine:
|
|
fileName = args["fileName"]
|
|
if fileName.endswith(".js") or fileName.endswith(".qml"):
|
|
self.insertQmlBreakpoint(args)
|
|
return
|
|
|
|
more = True
|
|
modelId = args['modelid']
|
|
if bpType == BreakpointByFileAndLine:
|
|
bp = self.target.BreakpointCreateByLocation(
|
|
str(args["fileName"]), int(args["lineNumber"]))
|
|
elif bpType == BreakpointByFunction:
|
|
bp = self.target.BreakpointCreateByName(args["function"])
|
|
elif bpType == BreakpointByAddress:
|
|
bp = self.target.BreakpointCreateByAddress(args["address"])
|
|
elif bpType == BreakpointAtMain:
|
|
bp = self.createBreakpointAtMain()
|
|
elif bpType == BreakpointAtThrow:
|
|
bp = self.target.BreakpointCreateForException(
|
|
lldb.eLanguageTypeC_plus_plus, False, True)
|
|
elif bpType == BreakpointAtCatch:
|
|
bp = self.target.BreakpointCreateForException(
|
|
lldb.eLanguageTypeC_plus_plus, True, False)
|
|
elif bpType == WatchpointAtAddress:
|
|
error = lldb.SBError()
|
|
bp = self.target.WatchAddress(args["address"], 4, False, True, error)
|
|
#warn("BPNEW: %s" % bp)
|
|
self.reportError(error)
|
|
elif bpType == WatchpointAtExpression:
|
|
# FIXME: Top level-only for now.
|
|
try:
|
|
frame = self.currentFrame()
|
|
value = frame.FindVariable(args["expression"])
|
|
error = lldb.SBError()
|
|
bp = self.target.WatchAddress(value.GetLoadAddress(),
|
|
value.GetByteSize(), False, True, error)
|
|
except:
|
|
bp = self.target.BreakpointCreateByName(None)
|
|
else:
|
|
# This leaves the unhandled breakpoint in a (harmless)
|
|
# "pending" state.
|
|
bp = self.target.BreakpointCreateByName(None)
|
|
more = False
|
|
|
|
if more:
|
|
bp.SetIgnoreCount(int(args["ignorecount"]))
|
|
if hasattr(bp, 'SetCondition'):
|
|
bp.SetCondition(self.hexdecode(args["condition"]))
|
|
bp.SetEnabled(bool(args["enabled"]))
|
|
if hasattr(bp, 'SetOneShot'):
|
|
bp.SetOneShot(bool(args["oneshot"]))
|
|
self.report('breakpoint-added={%s,modelid="%s"}' % (self.describeBreakpoint(bp), modelId))
|
|
|
|
def changeBreakpoint(self, args):
|
|
lldbId = int(args["lldbid"])
|
|
modelId = args['modelid']
|
|
if lldbId > qqWatchpointOffset:
|
|
bp = self.target.FindWatchpointByID(lldbId)
|
|
else:
|
|
bp = self.target.FindBreakpointByID(lldbId)
|
|
bp.SetIgnoreCount(int(args["ignorecount"]))
|
|
bp.SetCondition(self.hexdecode(args["condition"]))
|
|
bp.SetEnabled(bool(args["enabled"]))
|
|
if hasattr(bp, 'SetOneShot'):
|
|
bp.SetOneShot(bool(args["oneshot"]))
|
|
self.report('breakpoint-changed={%s,modelid="%s"}'
|
|
% (self.describeBreakpoint(bp), modelId))
|
|
|
|
def removeBreakpoint(self, args):
|
|
lldbId = int(args['lldbid'])
|
|
modelId = args['modelid']
|
|
if lldbId > qqWatchpointOffset:
|
|
res = self.target.DeleteWatchpoint(lldbId - qqWatchpointOffset)
|
|
res = self.target.BreakpointDelete(lldbId)
|
|
self.report('breakpoint-removed={modelid="%s"}' % modelId)
|
|
|
|
def listModules(self, args):
|
|
result = 'modules=['
|
|
for i in xrange(self.target.GetNumModules()):
|
|
module = self.target.GetModuleAtIndex(i)
|
|
result += '{file="%s"' % module.file.fullpath
|
|
result += ',name="%s"' % module.file.basename
|
|
result += ',addrsize="%s"' % module.addr_size
|
|
result += ',triple="%s"' % module.triple
|
|
#result += ',sections={'
|
|
#for section in module.sections:
|
|
# result += '[name="%s"' % section.name
|
|
# result += ',addr="%s"' % section.addr
|
|
# result += ',size="%s"],' % section.size
|
|
#result += '}'
|
|
result += '},'
|
|
result += ']'
|
|
self.report(result)
|
|
|
|
def listSymbols(self, args):
|
|
moduleName = args['module']
|
|
#file = lldb.SBFileSpec(moduleName)
|
|
#module = self.target.FindModule(file)
|
|
for i in xrange(self.target.GetNumModules()):
|
|
module = self.target.GetModuleAtIndex(i)
|
|
if module.file.fullpath == moduleName:
|
|
break
|
|
result = 'symbols={module="%s"' % moduleName
|
|
result += ',valid="%s"' % module.IsValid()
|
|
result += ',sections="%s"' % module.GetNumSections()
|
|
result += ',symbols=['
|
|
for symbol in module.symbols:
|
|
startAddress = symbol.GetStartAddress().GetLoadAddress(self.target)
|
|
endAddress = symbol.GetEndAddress().GetLoadAddress(self.target)
|
|
result += '{type="%s"' % symbol.GetType()
|
|
result += ',name="%s"' % symbol.GetName()
|
|
result += ',address="0x%x"' % startAddress
|
|
result += ',demangled="%s"' % symbol.GetMangledName()
|
|
result += ',size="%s"' % (endAddress - startAddress)
|
|
result += '},'
|
|
result += ']}'
|
|
self.report(result)
|
|
|
|
def executeNext(self, _ = None):
|
|
self.currentThread().StepOver()
|
|
|
|
def executeNextI(self, _ = None):
|
|
self.currentThread().StepInstruction(lldb.eOnlyThisThread)
|
|
|
|
def executeStep(self, _ = None):
|
|
self.currentThread().StepInto()
|
|
|
|
def shutdownInferior(self, _ = None):
|
|
self.isShuttingDown_ = True
|
|
if self.process is None:
|
|
self.reportState("inferiorshutdownok")
|
|
else:
|
|
state = self.process.GetState()
|
|
if state == lldb.eStateStopped:
|
|
self.process.Kill()
|
|
self.reportState("inferiorshutdownok")
|
|
|
|
def quit(self, args = {}):
|
|
self.reportToken(args)
|
|
self.reportState("engineshutdownok")
|
|
self.process.Kill()
|
|
|
|
def executeStepI(self, args = {}):
|
|
self.reportToken(args)
|
|
self.currentThread().StepInstruction(lldb.eOnlyThisThread)
|
|
|
|
def executeStepOut(self, args = {}):
|
|
self.reportToken(args)
|
|
self.currentThread().StepOut()
|
|
|
|
def executeRunToLocation(self, args):
|
|
self.reportToken(args)
|
|
addr = args.get('address', 0)
|
|
if addr:
|
|
error = self.currentThread().RunToAddress(addr)
|
|
else:
|
|
frame = self.currentFrame()
|
|
file = args['file']
|
|
line = int(args['line'])
|
|
error = self.currentThread().StepOverUntil(frame, lldb.SBFileSpec(file), line)
|
|
if error.GetType():
|
|
self.reportState("running")
|
|
self.reportState("stopped")
|
|
self.reportError(error)
|
|
else:
|
|
self.reportData()
|
|
|
|
def executeJumpToLocation(self, args):
|
|
self.reportToken(args)
|
|
frame = self.currentFrame()
|
|
if not frame:
|
|
self.reportStatus("No frame available.")
|
|
return
|
|
addr = args.get('address', 0)
|
|
if addr:
|
|
bp = self.target.BreakpointCreateByAddress(addr)
|
|
else:
|
|
bp = self.target.BreakpointCreateByLocation(
|
|
str(args['file']), int(args['line']))
|
|
if bp.GetNumLocations() == 0:
|
|
self.target.BreakpointDelete(bp.GetID())
|
|
self.reportStatus("No target location found.")
|
|
self.reportLocation(frame)
|
|
return
|
|
loc = bp.GetLocationAtIndex(0)
|
|
self.target.BreakpointDelete(bp.GetID())
|
|
if frame.SetPC(loc.GetLoadAddress()):
|
|
self.report("Jumped.")
|
|
else:
|
|
self.report("Cannot jump.")
|
|
self.reportLocation(frame)
|
|
|
|
def breakList(self):
|
|
result = lldb.SBCommandReturnObject()
|
|
self.debugger.GetCommandInterpreter().HandleCommand("break list", result)
|
|
self.report('success="%d",output="%s",error="%s"'
|
|
% (result.Succeeded(), result.GetOutput(), result.GetError()))
|
|
|
|
def activateFrame(self, args):
|
|
thread = args['thread']
|
|
self.currentThread().SetSelectedFrame(args['index'])
|
|
self.reportContinuation(args)
|
|
|
|
def selectThread(self, args):
|
|
self.process.SetSelectedThreadByID(args['id'])
|
|
|
|
def requestModuleSymbols(self, frame):
|
|
self.handleCommand("target module list " + frame)
|
|
|
|
def createFullBacktrace(self, _ = None):
|
|
command = "thread backtrace all"
|
|
result = lldb.SBCommandReturnObject()
|
|
self.debugger.GetCommandInterpreter().HandleCommand(command, result)
|
|
self.report('full-backtrace="%s"' % self.hexencode(result.GetOutput()))
|
|
|
|
def executeDebuggerCommand(self, args):
|
|
result = lldb.SBCommandReturnObject()
|
|
command = args['command']
|
|
self.debugger.GetCommandInterpreter().HandleCommand(command, result)
|
|
success = result.Succeeded()
|
|
output = result.GetOutput()
|
|
error = str(result.GetError())
|
|
self.report('success="%d",output="%s",error="%s"' % (success, output, error))
|
|
|
|
def updateData(self, args):
|
|
self.reportToken(args)
|
|
self.expandedINames = set(args.get('expanded', []))
|
|
self.autoDerefPointers = int(args.get('autoderef', '0'))
|
|
self.useDynamicType = int(args.get('dyntype', '0'))
|
|
self.useFancy = int(args.get('fancy', '0'))
|
|
self.passExceptions = int(args.get('passexceptions', '0'))
|
|
self.currentWatchers = args.get('watchers', {})
|
|
self.typeformats = args.get("typeformats", {})
|
|
self.formats = args.get("formats", {})
|
|
self.reportVariables(args)
|
|
|
|
def disassemble(self, args):
|
|
functionName = args.get('function', '')
|
|
flavor = args.get('flavor', '')
|
|
function = None
|
|
if len(functionName):
|
|
functions = self.target.FindFunctions(functionName).functions
|
|
if len(functions):
|
|
function = functions[0]
|
|
if function:
|
|
base = function.GetStartAddress().GetLoadAddress(self.target)
|
|
instructions = function.GetInstructions(self.target)
|
|
else:
|
|
base = args.get('address', 0)
|
|
if int(base) == 0xffffffffffffffff:
|
|
warn("INVALID DISASSEMBLER BASE")
|
|
return
|
|
addr = lldb.SBAddress(base, self.target)
|
|
instructions = self.target.ReadInstructions(addr, 100)
|
|
|
|
result = 'disassembly={cookie="%s",' % args['cookie']
|
|
result += ',lines=['
|
|
for insn in instructions:
|
|
comment = insn.GetComment(self.target)
|
|
addr = insn.GetAddress().GetLoadAddress(self.target)
|
|
result += '{address="%s"' % addr
|
|
result += ',inst="%s %s"' % (insn.GetMnemonic(self.target),
|
|
insn.GetOperands(self.target))
|
|
result += ',func_name="%s"' % functionName
|
|
if comment:
|
|
result += ',comment="%s"' % comment
|
|
result += ',offset="%s"},' % (addr - base)
|
|
self.report(result + ']')
|
|
|
|
def loadDumpers(self, args):
|
|
self.reportToken(args)
|
|
self.setupDumpers()
|
|
|
|
def reportDumpers(self, msg):
|
|
self.report(msg)
|
|
|
|
def fetchMemory(self, args):
|
|
address = args['address']
|
|
length = args['length']
|
|
error = lldb.SBError()
|
|
contents = self.process.ReadMemory(address, length, error)
|
|
result = 'memory={cookie="%s",' % args['cookie']
|
|
result += ',address="%s",' % address
|
|
result += self.describeError(error)
|
|
result += ',contents="%s"}' % self.hexencode(contents)
|
|
self.report(result)
|
|
|
|
def findValueByExpression(self, exp):
|
|
# FIXME: Top level-only for now.
|
|
frame = self.currentFrame()
|
|
value = frame.FindVariable(exp)
|
|
return value
|
|
|
|
def assignValue(self, args):
|
|
error = lldb.SBError()
|
|
exp = self.hexdecode(args['exp'])
|
|
value = self.hexdecode(args['value'])
|
|
lhs = self.findValueByExpression(exp)
|
|
lhs.SetValueFromCString(value, error)
|
|
self.reportError(error)
|
|
self.reportVariables()
|
|
|
|
def createResolvePendingBreakpointsHookBreakpoint(self, args):
|
|
if self.qmlTriggeredBreakpoint is None:
|
|
self.qmlTriggeredBreakpoint = \
|
|
self.target.BreakpointCreateByName("qt_v4TriggeredBreakpointHook")
|
|
|
|
bp = self.target.BreakpointCreateByName("qt_v4ResolvePendingBreakpointsHook")
|
|
bp.SetOneShot(True)
|
|
self.qmlBreakpointResolvers[bp] = lambda: \
|
|
self.doInsertQmlBreakpoint(args)
|
|
|
|
|
|
# Used in dumper auto test.
|
|
class Tester(Dumper):
|
|
def __init__(self, binary, expandedINames):
|
|
Dumper.__init__(self)
|
|
lldb.theDumper = self
|
|
|
|
self.expandedINames = set(expandedINames)
|
|
self.passExceptions = True
|
|
|
|
self.loadDumpers({})
|
|
error = lldb.SBError()
|
|
self.target = self.debugger.CreateTarget(binary, None, None, True, error)
|
|
|
|
if error.GetType():
|
|
warn("ERROR: %s" % error)
|
|
return
|
|
|
|
s = threading.Thread(target=self.testLoop, args=[])
|
|
s.start()
|
|
s.join(30)
|
|
|
|
def reportDumpers(self, msg):
|
|
pass
|
|
|
|
def testLoop(self):
|
|
# Disable intermediate reporting.
|
|
savedReport = self.report
|
|
self.report = lambda stuff: 0
|
|
|
|
error = lldb.SBError()
|
|
launchInfo = lldb.SBLaunchInfo([])
|
|
launchInfo.SetWorkingDirectory(os.getcwd())
|
|
environmentList = [key + "=" + value for key,value in os.environ.items()]
|
|
launchInfo.SetEnvironmentEntries(environmentList, False)
|
|
|
|
self.process = self.target.Launch(launchInfo, error)
|
|
if error.GetType():
|
|
warn("ERROR: %s" % error)
|
|
|
|
event = lldb.SBEvent()
|
|
listener = self.debugger.GetListener()
|
|
while True:
|
|
state = self.process.GetState()
|
|
if listener.WaitForEvent(100, event):
|
|
#warn("EVENT: %s" % event)
|
|
state = lldb.SBProcess.GetStateFromEvent(event)
|
|
if state == lldb.eStateExited: # 10
|
|
break
|
|
if state == lldb.eStateStopped: # 5
|
|
stoppedThread = None
|
|
for i in xrange(0, self.process.GetNumThreads()):
|
|
thread = self.process.GetThreadAtIndex(i)
|
|
reason = thread.GetStopReason()
|
|
#warn("THREAD: %s REASON: %s" % (thread, reason))
|
|
if (reason == lldb.eStopReasonBreakpoint or
|
|
reason == lldb.eStopReasonException or
|
|
reason == lldb.eStopReasonSignal):
|
|
stoppedThread = thread
|
|
|
|
if stoppedThread:
|
|
# This seems highly fragile and depending on the "No-ops" in the
|
|
# event handling above.
|
|
frame = stoppedThread.GetFrameAtIndex(0)
|
|
line = frame.line_entry.line
|
|
if line != 0:
|
|
self.report = savedReport
|
|
self.process.SetSelectedThread(stoppedThread)
|
|
self.reportVariables()
|
|
#self.reportLocation(frame)
|
|
self.report("@NS@%s@" % self.qtNamespace())
|
|
#self.report("ENV=%s" % os.environ.items())
|
|
#self.report("DUMPER=%s" % self.qqDumpers)
|
|
break
|
|
|
|
else:
|
|
warn('TIMEOUT')
|
|
warn("Cannot determined stopped thread")
|
|
|
|
lldb.SBDebugger.Destroy(self.debugger)
|