Files
qt-creator/share/qtcreator/debugger/lldbbridge.py
hjk 5309e217e4 Debugger: Consolidate GDB and LLDB "updateLocals" code paths
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>
2015-06-10 07:40:27 +00:00

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)