Files
qt-creator/share/qtcreator/debugger/lldbbridge.py
hjk 27e03e6fb6 Debugger: Do not run engines twice
There seems to be race in the LLDB setup on Linux. Work around
by not doing serious work twice to be able to use the test at least.

Change-Id: I387a57d3c66e6c4209bd424af9be16f17acb98fa
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
2016-10-07 13:06:27 +00:00

1540 lines
64 KiB
Python

############################################################################
#
# Copyright (C) 2016 The Qt Company Ltd.
# Contact: https://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 https://www.qt.io/terms-conditions. For further
# information use the contact form at https://www.qt.io/contact-us.
#
# GNU General Public License Usage
# Alternatively, this file may be used under the terms of the GNU
# General Public License version 3 as published by the Free Software
# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
# included in the packaging of this file. Please review the following
# information to ensure the GNU General Public License requirements will
# be met: https://www.gnu.org/licenses/gpl-3.0.html.
#
############################################################################
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 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 fileNameAsString(file):
return str(file) if file.IsValid() else ''
def check(exp):
if not exp:
raise RuntimeError("Check failed")
class Dumper(DumperBase):
def __init__(self):
DumperBase.__init__(self)
lldb.theDumper = self
self.isLldb = True
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
# interaction in 300
# 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')
#self.debugger.HandleCommand('type category delete default')
self.debugger.DeleteCategory('gnu-libstdc++')
self.debugger.DeleteCategory('libcxx')
self.debugger.DeleteCategory('default')
self.debugger.DeleteCategory('cplusplus')
#for i in range(self.debugger.GetNumCategories()):
# self.debugger.GetCategoryAtIndex(i).SetEnabled(False)
self.process = None
self.target = None
self.eventState = lldb.eStateInvalid
self.runEngineAttempted = False
self.executable_ = None
self.startMode_ = None
self.processArgs_ = None
self.attachPid_ = None
self.dyldImageSuffix = None
self.dyldLibraryPath = None
self.dyldFrameworkPath = None
self.isShuttingDown_ = False
self.isInterrupting_ = False
self.interpreterBreakpointResolvers = []
self.report('lldbversion=\"%s\"' % lldb.SBDebugger.GetVersionString())
self.reportState("enginesetupok")
self.debuggerCommandInProgress = False
def fromNativeValue(self, nativeValue):
nativeValue.SetPreferSyntheticValue(False)
nativeType = nativeValue.GetType()
val = self.Value(self)
val.type = self.fromNativeType(nativeType)
val.lIsInScope = nativeValue.IsInScope()
#val.name = nativeValue.GetName()
data = nativeValue.GetData()
error = lldb.SBError()
size = nativeValue.GetType().GetByteSize()
if size > 0: # Happens regularly e.g. for cross-shared-object types.
val.ldata = data.ReadRawData(error, 0, size)
code = nativeType.GetTypeClass()
if code not in (lldb.eTypeClassPointer, lldb.eTypeClassReference):
val.laddress = int(nativeValue.GetLoadAddress())
if code == lldb.eTypeClassEnumeration:
intval = nativeValue.GetValueAsSigned()
if hasattr(nativeType, 'get_enum_members_array'):
for enumMember in nativeType.get_enum_members_array():
# Even when asking for signed we get unsigned with LLDB 3.8.
diff = enumMember.GetValueAsSigned() - intval
mask = (1 << nativeType.GetByteSize() * 8) - 1
if diff & mask == 0:
path = nativeType.GetName().split('::')
path[-1] = enumMember.GetName()
val.ldisplay = "%s (%d)" % ('::'.join(path), intval)
val.ldisplay = "%d" % intval
elif code in (lldb.eTypeClassComplexInteger, lldb.eTypeClassComplexFloat):
val.ldisplay = str(nativeValue.GetValue())
elif code == lldb.eTypeClassArray:
if hasattr(nativeType, "GetArrayElementType"): # New in 3.8(?) / 350.x
val.type.ltarget = self.fromNativeType(nativeType.GetArrayElementType())
else:
fields = nativeType.get_fields_array()
if len(fields):
val.type.ltarget = self.fromNativeType(fields[0])
elif code == lldb.eTypeClassVector:
val.type.ltarget = self.fromNativeType(nativeType.GetVectorElementType())
return val
def fromNativeType(self, nativeType):
typeobj = self.Type(self)
if nativeType.IsPointerType():
typeobj.nativeType = nativeType
else:
# This strips typedefs for pointers. We don't want that.
typeobj.nativeType = nativeType.GetUnqualifiedType()
if hasattr(typeobj.nativeType, "GetDisplayTypeName"):
typeobj.name = typeobj.nativeType.GetDisplayTypeName() # Xcode 6 (lldb-320)
else:
typeobj.name = typeobj.nativeType.GetName() # Xcode 5 (lldb-310)
typeobj.lbitsize = nativeType.GetByteSize() * 8
code = nativeType.GetTypeClass()
try:
typeobj.code = {
lldb.eTypeClassArray : TypeCodeArray,
lldb.eTypeClassVector : TypeCodeArray,
lldb.eTypeClassComplexInteger : TypeCodeComplex,
lldb.eTypeClassComplexFloat : TypeCodeComplex,
lldb.eTypeClassClass : TypeCodeStruct,
lldb.eTypeClassStruct : TypeCodeStruct,
lldb.eTypeClassUnion : TypeCodeStruct,
lldb.eTypeClassEnumeration : TypeCodeEnum,
lldb.eTypeClassTypedef : TypeCodeTypedef,
lldb.eTypeClassReference : TypeCodeReference,
lldb.eTypeClassPointer : TypeCodePointer,
lldb.eTypeClassFunction : TypeCodeFunction,
lldb.eTypeClassMemberPointer : TypeCodeMemberPointer
}[code]
except KeyError:
if code == lldb.eTypeClassBuiltin:
if isFloatingPointTypeName(typeobj.name):
typeobj.code = TypeCodeFloat
elif isIntegralTypeName(typeobj.name):
typeobj.code = TypeCodeIntegral
else:
warn("UNKNOWN TYPE KEY: %s: %s" % (typeobj.name, code))
else:
warn("UNKNOWN TYPE KEY: %s: %s" % (typeobj.name, code))
#warn("CREATE TYPE: %s CODE: %s" % (typeobj.name, typeobj.code))
return typeobj
def nativeTypeFields(self, nativeType):
fields = []
if self.currentContextValue is not None:
addr = self.currentContextValue.AddressOf().GetValueAsUnsigned()
else:
warn("CREATING DUMMY CONTEXT")
addr = 0 # FIXME: 0 doesn't produce valid member offsets.
addr = 0x7fffffffe0a0
sbaddr = lldb.SBAddress(addr, self.target)
dummyValue = self.target.CreateValueFromAddress('x', sbaddr, nativeType)
dummyValue.SetPreferSyntheticValue(False)
anonNumber = 0
baseNames = {}
virtualNames = {}
# baseNames = set(base.GetName() for base in nativeType.get_bases_array())
for i in range(nativeType.GetNumberOfDirectBaseClasses()):
base = nativeType.GetDirectBaseClassAtIndex(i)
baseNames[base.GetName()] = i
for i in range(nativeType.GetNumberOfVirtualBaseClasses()):
base = nativeType.GetVirtualBaseClassAtIndex(i)
virtualNames[base.GetName()] = i
fieldBits = dict((field.name, (field.GetBitfieldSizeInBits(), field.GetOffsetInBits()))
for field in nativeType.get_fields_array())
#warn("BASE NAMES: %s" % baseNames)
#warn("VIRTUAL NAMES: %s" % virtualNames)
#warn("FIELD BITS: %s" % fieldBits)
fieldParentType = self.fromNativeType(nativeType).stripTypedefs()
# This does not list empty base entries.
for i in xrange(dummyValue.GetNumChildren()):
dummyChild = dummyValue.GetChildAtIndex(i)
fieldName = dummyChild.GetName()
if fieldName is None:
anonNumber += 1
fieldName = "#%s" % anonNumber
fieldType = dummyChild.GetType()
#warn("CHILD AT: %s: %s %s" % (i, fieldName, fieldType.GetName()))
#warn(" AT: %s: %s %s" % (i, fieldName, fieldType.GetName()))
caddr = dummyChild.AddressOf().GetValueAsUnsigned()
child = self.Value(self)
child.type = self.fromNativeType(fieldType)
child.name = fieldName
field = self.Field(self)
field.value = child
field.parentType = fieldParentType
field.ltype = self.fromNativeType(fieldType)
field.nativeIndex = i
field.name = fieldName
if fieldName in fieldBits:
(field.lbitsize, field.lbitpos) = fieldBits[fieldName]
else:
field.lbitsize = fieldType.GetByteSize() * 8
field.lbitpos = (caddr - addr) * 8
if fieldName in baseNames:
#warn("BASE: %s P0S: 0x%x - 0x%x = %s" % (fieldName, caddr, addr, caddr - addr))
field.isBaseClass = True
field.baseIndex = baseNames[fieldName]
if fieldName in virtualNames:
field.isVirtualBase = True
#warn("ADDING VRITUAL BASE: %s" % fieldName)
fields.append(field)
# Add empty bases.
for i in range(nativeType.GetNumberOfDirectBaseClasses()):
fieldObj = nativeType.GetDirectBaseClassAtIndex(i)
fieldType = fieldObj.GetType()
if fieldType.GetNumberOfFields() == 0:
if fieldType.GetNumberOfDirectBaseClasses() == 0:
fieldName = fieldObj.GetName()
child = self.Value(self)
child.type = self.fromNativeType(fieldType)
child.name = fieldName
child.ldata = bytes()
field = self.Field(self)
field.value = child
field.isBaseClass = True
field.baseIndex = baseNames[fieldName]
field.parentType = fieldParentType
field.ltype = self.fromNativeType(fieldType)
field.name = fieldName
field.lbitsize = 0
field.lbitpos = 0
fields.append(field)
#warn("FIELD NAMES: %s" % [field.name for field in fields])
#warn("FIELDS: %s" % fields)
return fields
def nativeTypeUnqualified(self, nativeType):
return self.fromNativeType(nativeType.GetUnqualifiedType())
def nativeTypePointer(self, nativeType):
return self.fromNativeType(nativeType.GetPointerType())
def nativeTypeStripTypedefs(self, typeobj):
if hasattr(typeobj, 'GetCanonicalType'):
return self.fromNativeType(typeobj.GetCanonicalType())
return self.fromNativeType(typeobj)
def nativeTypeFirstBase(self, nativeType):
#warn("FIRST BASE FROM: %s" % nativeType)
if nativeType.GetNumberOfDirectBaseClasses() == 0:
return None
t = nativeType.GetDirectBaseClassAtIndex(0)
#warn(" GOT BASE FROM: %s" % t)
return self.fromNativeType(nativeType.GetDirectBaseClassAtIndex(0).GetType())
def nativeTypeEnumDisplay(self, nativeType, intval):
if hasattr(nativeType, 'get_enum_members_array'):
for enumMember in nativeType.get_enum_members_array():
# Even when asking for signed we get unsigned with LLDB 3.8.
diff = enumMember.GetValueAsSigned() - intval
mask = (1 << nativeType.GetByteSize() * 8) - 1
if diff & mask == 0:
path = nativeType.GetName().split('::')
path[-1] = enumMember.GetName()
return "%s (%d)" % ('::'.join(path), intval)
return "%d" % intval
def nativeDynamicTypeName(self, address, baseType):
return None # FIXME: Seems sufficient, no idea why.
addr = self.target.ResolveLoadAddress(address)
ctx = self.target.ResolveSymbolContextForAddress(addr, 0)
sym = ctx.GetSymbol()
return sym.GetName()
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 enumExpression(self, enumType, enumValue):
ns = self.qtNamespace()
return ns + "Qt::" + enumType + "(" \
+ ns + "Qt::" + enumType + "::" + enumValue + ")"
def callHelper(self, rettype, value, func, args):
# args is a tuple.
arg = ','.join(args)
#warn("PRECALL: %s -> %s(%s)" % (value.address(), func, arg))
typename = value.type.name
exp = "((%s*)0x%x)->%s(%s)" % (typename, value.address(), func, arg)
#warn("CALL: %s" % exp)
result = self.currentContextValue.CreateValueFromExpression('', exp)
#warn(" -> %s" % result)
return self.fromNativeValue(result)
def pokeValue(self, typeName, *args):
thread = self.currentThread()
frame = thread.GetFrameAtIndex(0)
inner = ','.join(args)
value = frame.EvaluateExpression(typeName + '{' + inner + '}')
#self.warn(" TYPE: %s" % value.type)
#self.warn(" ADDR: 0x%x" % value.address)
#self.warn(" VALUE: %s" % value)
return value
def parseAndEvaluate(self, exp):
thread = self.currentThread()
frame = thread.GetFrameAtIndex(0)
val = frame.EvaluateExpression(exp)
#options = lldb.SBExpressionOptions()
#val = self.target.EvaluateExpression(exp, options)
err = val.GetError()
if err.Fail():
#warn("FAILING TO EVAL: %s" % exp)
return None
#warn("NO ERROR.")
#warn("EVAL: %s -> %s" % (exp, val.IsValid()))
return self.fromNativeValue(val)
def nativeTypeTemplateArgument(self, nativeType, position, numeric = False):
if numeric:
# There seems no API to extract the numeric value.
inner = self.extractTemplateArgument(nativeType.GetName(), position)
innerType = nativeType.GetTemplateArgumentType(position)
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
else:
#warn("nativeTypeTemplateArgument: %s: pos %s" % (nativeType, position))
typeobj = nativeType.GetTemplateArgumentType(position).GetUnqualifiedType()
if typeobj.IsValid():
# warn("TYPE: %s" % typeobj)
return self.fromNativeType(typeobj)
inner = self.extractTemplateArgument(nativeType.GetName(), position)
#warn("INNER: %s" % inner)
return self.lookupType(inner)
def nativeTypeDereference(self, nativeType):
return self.fromNativeType(nativeType.GetPointeeType())
def nativeTypeTarget(self, nativeType):
code = nativeType.GetTypeClass()
if code == lldb.eTypeClassPointer:
return self.fromNativeType(nativeType.GetPointeeType())
if code == lldb.eTypeClassReference:
return self.fromNativeType(nativeType.GetDereferencedType())
if code == lldb.eTypeClassArray:
if hasattr(nativeType, "GetArrayElementType"): # New in 3.8(?) / 350.x
return self.fromNativeType(nativeType.GetArrayElementType())
fields = nativeType.get_fields_array()
return None if not len(fields) else self.nativeTypeTarget(fields[0])
if code == lldb.eTypeClassVector:
return self.fromNativeType(nativeType.GetVectorElementType())
return self.fromNativeType(nativeType)
def isWindowsTarget(self):
return False
def isQnxTarget(self):
return False
def isArmArchitecture(self):
return False
def isMsvcTarget(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 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())
def put(self, stuff):
self.output += stuff
def canonicalTypeName(self, name):
return re.sub('\\bconst\\b', '', name).replace(' ', '')
def lookupNativeType(self, name):
#warn("LOOKUP TYPE NAME: %s" % name)
typeobj = self.target.FindFirstType(name)
if typeobj.IsValid():
#warn("VALID FIRST : %s" % dir(typeobj))
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()
#warn("LOOKUP RESULT: %s" % typeobj.name)
#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()
#warn("NOT FOUND: %s " % needle)
return None
def setupInferior(self, 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.dyldImageSuffix = args.get('dyldimagesuffix', '')
self.dyldLibraryPath = args.get('dyldlibrarypath', '')
self.dyldFrameworkPath = args.get('dyldframeworkpath', '')
self.processArgs_ = list(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.nativeMixed = int(args.get('nativemixed', 0))
self.workingDirectory_ = args.get('workingdirectory', '')
if self.workingDirectory_ == '':
self.workingDirectory_ = os.getcwd()
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.nativeMixed:
self.interpreterEventBreakpoint = \
self.target.BreakpointCreateByName("qt_qmlDebugMessageAvailable")
state = 1 if self.target.IsValid() else 0
self.reportResult('success="%s",msg="%s",exe="%s"'
% (state, error, self.executable_), args)
def runEngine(self, args):
if self.runEngineAttempted:
return
self.runEngineAttempted = True
self.prepare(args)
s = threading.Thread(target=self.loop, args=[])
s.start()
def prepare(self, 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
if self.process and self.process.GetState() == lldb.eStateStopped:
# lldb stops the process after attaching. This happens before the
# eventloop starts. Relay the correct state back.
self.reportState("enginerunandinferiorstopok")
else:
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.report(self.describeError(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")
else:
launchInfo = lldb.SBLaunchInfo(self.processArgs_)
launchInfo.SetWorkingDirectory(self.workingDirectory_)
environmentList = [key + "=" + value for key,value in os.environ.items()]
if self.dyldImageSuffix:
environmentList.append('DYLD_IMAGE_SUFFIX=' + self.dyldImageSuffix)
if self.dyldLibraryPath:
environmentList.append('DYLD_LIBRARY_PATH=' + self.dyldLibraryPath)
if self.dyldFrameworkPath:
environmentList.append('DYLD_FRAMEWORK_PATH=' + self.dyldFrameworkPath)
launchInfo.SetEnvironmentEntries(environmentList, False)
if self.breakOnMain_:
self.createBreakpointAtMain()
self.process = self.target.Launch(launchInfo, error)
if not error.Success():
self.report(self.describeError(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 = 'success="%s",' % int(error.Success())
result += 'error={type="%s"' % error.GetType()
if error.GetType():
result += ',status="%s"' % error.GetCString()
result += ',code="%s"' % error.GetError()
result += ',desc="%s"}' % desc.GetData()
return result
def describeStatus(self, status):
return 'status="%s",' % status
def describeLocation(self, frame):
if int(frame.pc) == 0xffffffffffffffff:
return ''
fileName = fileNameAsString(frame.line_entry.file)
function = frame.GetFunctionName()
line = frame.line_entry.line
return 'location={file="%s",line="%s",address="%s",function="%s"}' \
% (fileName, line, frame.pc, function)
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 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 fetchThreads(self, args):
result = '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"' % fileNameAsString(frame.line_entry.file)
result += ',file="%s"' % fileNameAsString(frame.line_entry.file)
result += '}},'
result += '],current-thread-id="%s"' % self.currentThread().id
self.reportResult(result, args)
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 fetchStack(self, args):
if not self.process:
self.reportResult('msg="No process"', args)
return
thread = self.currentThread()
if not thread:
self.reportResult('msg="No thread"', args)
return
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()
if isNativeMixed and functionName == "::qt_qmlDebugMessageAvailable()":
interpreterStack = self.extractInterpreterStack()
for interpreterFrame in interpreterStack.get('frames', []):
function = interpreterFrame.get('function', '')
fileName = interpreterFrame.get('file', '')
language = interpreterFrame.get('language', '')
lineNumber = interpreterFrame.get('line', 0)
context = interpreterFrame.get('context', 0)
result += ('frame={function="%s",file="%s",'
'line="%s",language="%s",context="%s"}'
% (function, fileName, lineNumber, language, context))
fileName = fileNameAsString(lineEntry.file)
result += '{pc="0x%x"' % pc
result += ',level="%d"' % level
result += ',address="0x%x"' % addr
result += ',function="%s"' % functionName
result += ',line="%d"' % lineNumber
result += ',file="%s"},' % fileName
result += ']'
result += ',hasmore="%d"' % isLimited
result += ',limit="%d"' % limit
result += '}'
self.reportResult(result, args)
def reportResult(self, result, args):
self.report('result={token="%s",%s}' % (args.get("token", 0), result))
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 readRawMemory(self, address, size):
if size == 0:
return bytes()
error = lldb.SBError()
#warn("READ: %s %s" % (address, size))
res = self.process.ReadMemory(address, size, error)
if res is None:
return bytes()
return res
def findStaticMetaObject(self, typeName):
symbolName = self.mangleName(typeName + '::staticMetaObject')
symbol = self.target.FindFirstGlobalVariable(symbolName)
return symbol.AddressOf().GetValueAsUnsigned() if symbol.IsValid() else 0
def findSymbol(self, symbolName):
return self.target.FindFirstGlobalVariable(symbolName)
def warn(self, msg):
self.put('{name="%s",value="",type="",numchild="0"},' % msg)
def fetchVariables(self, args):
(ok, res) = self.tryFetchInterpreterVariables(args)
if ok:
self.reportResult(res, args)
return
self.setVariableFetchingOptions(args)
frame = self.currentFrame()
if frame is None:
self.reportResult('error="No frame"', args)
return
self.output = ''
isPartial = len(self.partialVariable) > 0
self.currentIName = 'local'
self.put('data=[')
self.anonNumber = 0
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]
staticVar.SetPreferSyntheticValue(False)
typename = staticVar.GetType().GetName()
name = staticVar.GetName()
with SubItem(self, i):
self.put('name="%s",' % name)
self.put('iname="%s",' % self.currentIName)
self.putItem(self.fromNativeValue(staticVar))
else:
with SubItem(self, "None"):
self.putEmptyValue()
self.putNumChild(0)
# 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.
variables = []
for val in values:
val.SetPreferSyntheticValue(False)
if not val.IsValid():
continue
self.currentContextValue = val
name = val.GetName()
if name is None:
# This can happen for unnamed function parameters with
# default values: void foo(int = 0)
continue
value = self.fromNativeValue(val)
value.name = name
variables.append(value)
self.handleLocals(variables)
self.handleWatches(args)
self.put('],partial="%d"' % isPartial)
self.reportResult(self.output, args)
def fetchRegisters(self, args = None):
if self.process is None:
result = 'process="none"'
else:
frame = self.currentFrame()
if frame:
result = 'registers=['
for group in frame.GetRegisters():
for reg in group:
value = ''.join(["%02x" % x for x in reg.GetData().uint8s])
result += '{name="%s"' % reg.GetName()
result += ',value="0x%s"' % value
result += ',size="%s"' % reg.GetByteSize()
result += ',type="%s"},' % reg.GetType()
result += ']'
self.reportResult(result, args)
def setRegister(self, args):
name = args["name"]
value = args["value"]
result = lldb.SBCommandReturnObject()
interp = self.debugger.GetCommandInterpreter()
interp.HandleCommand("register write %s %s" % (name, value), result)
success = result.Succeeded()
if success:
self.reportResult('output="%s"' % result.GetOutput(), args)
return
# Try again with register write xmm0 "{0x00 ... 0x02}" syntax:
vec = ' '.join(["0x" + value[i:i+2] for i in range(2, len(value), 2)])
success = interp.HandleCommand('register write %s "{%s}"' % (name, vec), result)
if success:
self.reportResult('output="%s"' % result.GetOutput(), args)
else:
self.reportResult('error="%s"' % result.GetError(), args)
def report(self, stuff):
with self.outputLock:
sys.stdout.write("@\n" + stuff + "@\n")
def reportState(self, state):
self.report('state="%s"' % state)
def interruptInferior(self, args):
if self.process is None:
self.reportResult('status="No process to interrupt",success="0"', args)
else:
self.isInterrupting_ = True
error = self.process.Stop()
self.reportResult(self.describeError(error), args)
def detachInferior(self, args):
if self.process is None:
self.reportResult('status="No process to detach from."', args)
else:
error = self.process.Detach()
self.reportResult(self.describeError(error), args)
def continueInferior(self, args):
if self.process is None:
self.reportResult('status="No process to continue."', args)
else:
# Can fail when attaching to GDBserver.
error = self.process.Continue()
self.reportResult(self.describeError(error), args)
def quitDebugger(self, args):
self.reportState("inferiorshutdownrequested")
self.process.Kill()
self.reportResult('', args)
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)
skipEventReporting = self.debuggerCommandInProgress \
and eventType in (lldb.SBProcess.eBroadcastBitSTDOUT, lldb.SBProcess.eBroadcastBitSTDERR)
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:
if not skipEventReporting:
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()
functionName = function.GetName()
if functionName == "::qt_qmlDebugConnectorOpen()":
self.report("RESOLVER HIT")
for resolver in self.interpreterBreakpointResolvers:
resolver()
self.report("AUTO-CONTINUE AFTER RESOLVING")
self.reportState("inferiorstopok")
self.process.Continue();
return
if functionName == "::qt_qmlDebugMessageAvailable()":
self.report("ASYNC MESSAGE FROM SERVICE")
res = self.handleInterpreterMessage()
if not res:
self.report("EVENT NEEDS NO STOP")
self.reportState("stopped")
self.process.Continue();
return
if self.isInterrupting_:
self.isInterrupting_ = False
self.reportState("stopped")
elif self.ignoreStops > 0:
self.ignoreStops -= 1
self.process.Continue()
elif self.silentStops > 0:
self.silentStops -= 1
else:
self.reportState("stopped")
else:
if not skipEventReporting:
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)
if msg is not None:
self.report('output={channel="stdout",data="%s"}' % self.hexencode(msg))
elif eventType == lldb.SBProcess.eBroadcastBitSTDERR:
msg = self.process.GetSTDERR(1024)
if msg is not None:
self.report('output={channel="stderr",data="%s"}' % self.hexencode(msg))
elif eventType == lldb.SBProcess.eBroadcastBitProfileData:
pass
def describeBreakpoint(self, bp):
isWatch = isinstance(bp, lldb.SBWatchpoint)
if isWatch:
result = 'lldbid="%s"' % (qqWatchpointOffset + bp.GetID())
else:
result = 'lldbid="%s"' % bp.GetID()
result += ',valid="%s"' % (1 if bp.IsValid() else 0)
result += ',hitcount="%s"' % bp.GetHitCount()
if bp.IsValid():
if isinstance(bp, lldb.SBBreakpoint):
result += ',threadid="%s"' % bp.GetThreadID()
result += ',oneshot="%s"' % (1 if bp.IsOneShot() else 0)
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()
if bp.IsValid() and isinstance(bp, lldb.SBBreakpoint):
result += ',locations=['
lineEntry = None
for i in xrange(bp.GetNumLocations()):
loc = bp.GetLocationAtIndex(i)
addr = loc.GetAddress()
lineEntry = addr.GetLineEntry()
result += '{locid="%s"' % loc.GetID()
result += ',function="%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["file"]
if fileName.endswith(".js") or fileName.endswith(".qml"):
self.insertInterpreterBreakpoint(args)
return
extra = ''
more = True
if bpType == BreakpointByFileAndLine:
bp = self.target.BreakpointCreateByLocation(
str(args["file"]), int(args["line"]))
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()
# This might yield bp.IsValid() == False and
# error.desc == "process is not alive".
bp = self.target.WatchAddress(args["address"], 4, False, True, error)
extra = self.describeError(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 and bp.IsValid():
bp.SetIgnoreCount(int(args["ignorecount"]))
bp.SetCondition(self.hexdecode(args["condition"]))
bp.SetEnabled(bool(args["enabled"]))
bp.SetScriptCallbackBody('\n'.join([
"def foo(frame = frame, bp_loc = bp_loc, dict = internal_dict):",
" " + self.hexdecode(args["command"]).replace('\n', '\n '),
"from cStringIO import StringIO",
"origout = sys.stdout",
"sys.stdout = StringIO()",
"result = foo()",
"d = lldb.theDumper",
"output = d.hexencode(sys.stdout.getvalue())",
"sys.stdout = origout",
"d.report('output={channel=\"stderr\",data=\"' + output + '\"}')",
"if result is False:",
" d.reportState('continueafternextstop')",
"return True"
]))
if isinstance(bp, lldb.SBBreakpoint):
bp.SetOneShot(bool(args["oneshot"]))
self.reportResult(self.describeBreakpoint(bp) + extra, args)
def changeBreakpoint(self, args):
lldbId = int(args["lldbid"])
if lldbId > qqWatchpointOffset:
bp = self.target.FindWatchpointByID(lldbId)
else:
bp = self.target.FindBreakpointByID(lldbId)
if bp.IsValid():
bp.SetIgnoreCount(int(args["ignorecount"]))
bp.SetCondition(self.hexdecode(args["condition"]))
bp.SetEnabled(bool(args["enabled"]))
if isinstance(bp, lldb.SBBreakpoint):
bp.SetOneShot(bool(args["oneshot"]))
self.reportResult(self.describeBreakpoint(bp), args)
def removeBreakpoint(self, args):
lldbId = int(args['lldbid'])
if lldbId > qqWatchpointOffset:
res = self.target.DeleteWatchpoint(lldbId - qqWatchpointOffset)
res = self.target.BreakpointDelete(lldbId)
self.reportResult('success="%s"' % int(res), args)
def fetchModules(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.reportResult(result, args)
def fetchSymbols(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={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.reportResult(result, args)
def executeNext(self, args):
self.currentThread().StepOver()
self.reportResult('', args)
def executeNextI(self, args):
self.currentThread().StepInstruction(lldb.eOnlyThisThread)
self.reportResult('', args)
def executeStep(self, args):
self.currentThread().StepInto()
self.reportResult('', args)
def shutdownInferior(self, args):
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")
self.reportResult('', args)
def quit(self, args):
self.reportState("engineshutdownok")
self.process.Kill()
self.reportResult('', args)
def executeStepI(self, args):
self.currentThread().StepInstruction(lldb.eOnlyThisThread)
self.reportResult('', args)
def executeStepOut(self, args = {}):
self.currentThread().StepOut()
self.reportResult('', args)
def executeRunToLocation(self, args):
self.reportToken(args)
addr = args.get('address', 0)
if addr:
# Does not seem to hit anything on Linux:
# self.currentThread().RunToAddress(addr)
bp = self.target.BreakpointCreateByAddress(addr)
if bp.GetNumLocations() == 0:
self.target.BreakpointDelete(bp.GetID())
self.reportResult(self.describeStatus("No target location found.")
+ self.describeLocation(frame), args)
return
bp.SetOneShot(True)
self.reportResult('', args)
self.process.Continue()
else:
frame = self.currentFrame()
file = args['file']
line = int(args['line'])
error = self.currentThread().StepOverUntil(frame, lldb.SBFileSpec(file), line)
self.reportResult(self.describeError(error), args)
self.reportState("running")
self.reportState("stopped")
def executeJumpToLocation(self, args):
self.reportToken(args)
frame = self.currentFrame()
if not frame:
self.reportResult(self.describeStatus("No frame available."), args)
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())
status = "No target location found."
else:
loc = bp.GetLocationAtIndex(0)
self.target.BreakpointDelete(bp.GetID())
res = frame.SetPC(loc.GetLoadAddress())
status = "Jumped." if res else "Cannot jump."
self.reportResult(self.describeStatus(status) + self.describeLocation(frame), args)
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):
self.reportToken(args)
thread = args['thread']
self.currentThread().SetSelectedFrame(args['index'])
self.reportResult('', args)
def selectThread(self, args):
self.reportToken(args)
self.process.SetSelectedThreadByID(args['id'])
self.reportResult('', args)
def fetchFullBacktrace(self, _ = None):
command = "thread backtrace all"
result = lldb.SBCommandReturnObject()
self.debugger.GetCommandInterpreter().HandleCommand(command, result)
self.reportResult(self.hexencode(result.GetOutput()), {})
def executeDebuggerCommand(self, args):
self.debuggerCommandInProgress = True
self.reportToken(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))
self.debuggerCommandInProgress = False
def fetchDisassembler(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)
currentFile = None
currentLine = None
hunks = dict()
sources = dict()
result = 'lines=['
for insn in instructions:
comment = insn.GetComment(self.target)
addr = insn.GetAddress()
loadAddr = addr.GetLoadAddress(self.target)
lineEntry = addr.GetLineEntry()
if lineEntry:
lineNumber = lineEntry.GetLine()
fileName = str(lineEntry.GetFileSpec())
if lineNumber != currentLine or fileName != currentFile:
currentLine = lineNumber
currentFile = fileName
key = "%s:%s" % (fileName, lineNumber)
hunk = hunks.get(key, 0) + 1
hunks[key] = hunk
source = sources.get(fileName, None)
if source is None:
try:
with open(fileName, 'r') as f:
source = f.read().splitlines()
sources[fileName] = source
except IOError as error:
# With lldb-3.8 files like /data/dev/creator-3.6/tests/
# auto/debugger/qt_tst_dumpers_StdVector_bfNWZa/main.cpp
# with non-existent directories appear.
warn("FILE: %s ERROR: %s" % (fileName, error))
source = ""
result += '{line="%s"' % lineNumber
result += ',file="%s"' % fileName
if 0 < lineNumber and lineNumber <= len(source):
result += ',data="%s"' % source[lineNumber - 1]
result += ',hunk="%s"}' % hunk
result += '{address="%s"' % loadAddr
result += ',data="%s %s"' % (insn.GetMnemonic(self.target),
insn.GetOperands(self.target))
result += ',function="%s"' % functionName
rawData = insn.GetData(lldb.target).uint8s
result += ',rawdata="%s"' % ' '.join(["%02x" % x for x in rawData])
if comment:
result += ',comment="%s"' % self.hexencode(comment)
result += ',offset="%s"}' % (loadAddr - base)
self.reportResult(result + ']', args)
def fetchMemory(self, args):
address = args['address']
length = args['length']
error = lldb.SBError()
contents = self.process.ReadMemory(address, length, error)
result = 'address="%s",' % address
result += self.describeError(error)
result += ',contents="%s"' % self.hexencode(contents)
self.reportResult(result, args)
def findValueByExpression(self, exp):
# FIXME: Top level-only for now.
frame = self.currentFrame()
value = frame.FindVariable(exp)
return value
def assignValue(self, args):
self.reportToken(args)
error = lldb.SBError()
exp = self.hexdecode(args['exp'])
value = self.hexdecode(args['value'])
lhs = self.findValueByExpression(exp)
lhs.SetValueFromCString(value, error)
self.reportResult(self.describeError(error), args)
def createResolvePendingBreakpointsHookBreakpoint(self, args):
bp = self.target.BreakpointCreateByName("qt_qmlDebugConnectorOpen")
bp.SetOneShot(True)
self.interpreterBreakpointResolvers.append(
lambda: self.resolvePendingInterpreterBreakpoint(args))
# Used in dumper auto test.
class Tester(Dumper):
def __init__(self, binary, args):
Dumper.__init__(self)
lldb.theDumper = self
self.loadDumpers({'token': 1})
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=(args,))
s.start()
s.join(30)
def testLoop(self, args):
# 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.fetchVariables(args)
#self.describeLocation(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)