lldbbridge.py 69.6 KB
Newer Older
1
2
############################################################################
#
3
# Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4
5
6
7
8
9
10
11
12
# Contact: http://www.qt-project.org/legal
#
# 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 Digia.  For licensing terms and
Eike Ziller's avatar
Eike Ziller committed
13
14
# conditions see http://www.qt.io/licensing.  For further information
# use the contact form at http://www.qt.io/contact-us.
15
16
17
#
# GNU Lesser General Public License Usage
# Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18
19
20
21
22
23
# 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.
24
25
26
27
28
29
#
# In addition, as a special exception, Digia gives you certain additional
# rights.  These rights are described in the Digia Qt LGPL Exception
# version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
#
#############################################################################
30

31
import atexit
32
33
import inspect
import os
34
import platform
35
import re
36
import sys
37
import subprocess
38
import threading
39
import lldb
40

41
42
43
44
45
currentDir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
sys.path.insert(1, currentDir)

from dumper import *

46
47
48
49
50
51
#######################################################################
#
# Helpers
#
#######################################################################

52
qqWatchpointOffset = 10000
53

54
lldb.theDumper = None
55
56

def warn(message):
57
    print('\n\nWARNING="%s",\n' % message.encode("latin1").replace('"', "'"))
58
59
60

def showException(msg, exType, exValue, exTraceback):
    warn("**** CAUGHT EXCEPTION: %s ****" % msg)
61
62
63
    import traceback
    lines = [line for line in traceback.format_exception(exType, exValue, exTraceback)]
    warn('\n'.join(lines))
64
65
66
67

def fileName(file):
    return str(file) if file.IsValid() else ''

68

69
70
71
72
73
74
75
76
77
78
# Breakpoints. Keep synchronized with BreakpointType in breakpoint.h
UnknownType = 0
BreakpointByFileAndLine = 1
BreakpointByFunction = 2
BreakpointByAddress = 3
BreakpointAtThrow = 4
BreakpointAtCatch = 5
BreakpointAtMain = 6
BreakpointAtFork = 7
BreakpointAtExec = 8
79
80
81
82
83
BreakpointAtSysCall = 9
WatchpointAtAddress = 10
WatchpointAtExpression = 11
BreakpointOnQmlSignalEmit = 12
BreakpointAtJavaScriptThrow = 13
84

hjk's avatar
hjk committed
85
86
87
88
def check(exp):
    if not exp:
        raise RuntimeError("Check failed")

89
90
Value = lldb.SBValue

91
92
def impl_SBValue__add__(self, offset):
    if self.GetType().IsPointerType():
93
        if isinstance(offset, int):
94
95
96
            pass
        else:
            offset = offset.GetValueAsSigned()
97
        itemsize = self.GetType().GetPointeeType().GetByteSize()
98
        address = self.GetValueAsUnsigned() + offset * itemsize
99
        address = address & 0xFFFFFFFFFFFFFFFF  # Force unsigned
100
101
        return self.CreateValueFromAddress(None, address,
                self.GetType().GetPointeeType()).AddressOf()
hjk's avatar
hjk committed
102

103
    raise RuntimeError("SBValue.__add__ not implemented: %s" % self.GetType())
104
105
    return NotImplemented

106
def impl_SBValue__sub__(self, other):
107
    if self.GetType().IsPointerType():
108
        if isinstance(other, int) or isinstance(other, long):
109
            address = self.GetValueAsUnsigned() - other
110
111
            address = address & 0xFFFFFFFFFFFFFFFF  # Force unsigned
            return self.CreateValueFromAddress(None, address, self.GetType())
112
        if other.GetType().IsPointerType():
113
114
            itemsize = self.GetType().GetPointeeType().GetByteSize()
            return (self.GetValueAsUnsigned() - other.GetValueAsUnsigned()) / itemsize
115
    raise RuntimeError("SBValue.__sub__ not implemented: %s" % self.GetType())
116
117
    return NotImplemented

118
119
120
121
122
123
124
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):
125
    return self.GetValueAsSigned()
126
127
128
129
130
131
132
133
134
135

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
136

137
138
139
def impl_SBValue__long__(self):
    return int(self.GetValue(), 0)

140
def impl_SBValue__getitem__(value, index):
141
    if isinstance(index, int) or isinstance(index, long):
142
143
144
145
146
147
148
        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)
149
    return value.GetChildMemberWithName(index)
150

151
152
153
154
def impl_SBValue__deref(value):
    result = value.Dereference()
    if result.IsValid():
        return result
155
156
    exp = "*(class %s*)0x%x" % (value.GetType().GetPointeeType(), value.GetValueAsUnsigned())
    return value.CreateValueFromExpression(None, exp)
157

158
lldb.SBValue.__add__ = impl_SBValue__add__
159
lldb.SBValue.__sub__ = impl_SBValue__sub__
160
lldb.SBValue.__le__ = impl_SBValue__le__
161

162
163
lldb.SBValue.__getitem__ = impl_SBValue__getitem__
lldb.SBValue.__int__ = impl_SBValue__int__
164
lldb.SBValue.__float__ = impl_SBValue__float__
165
166
lldb.SBValue.__long__ = lambda self: long(self.GetValue(), 0)

167
lldb.SBValue.code = lambda self: self.GetTypeClass()
168
lldb.SBValue.cast = lambda self, typeObj: self.Cast(typeObj)
169
lldb.SBValue.dereference = impl_SBValue__deref
170
lldb.SBValue.address = property(lambda self: self.GetLoadAddress())
171
172

lldb.SBType.pointer = lambda self: self.GetPointerType()
hjk's avatar
hjk committed
173
lldb.SBType.target = lambda self: self.GetPointeeType()
174
lldb.SBType.code = lambda self: self.GetTypeClass()
175
lldb.SBType.sizeof = property(lambda self: self.GetByteSize())
176
177


hjk's avatar
hjk committed
178
179
lldb.SBType.unqualified = \
    lambda self: self.GetUnqualifiedType() if hasattr(self, 'GetUnqualifiedType') else self
180
181
lldb.SBType.strip_typedefs = \
    lambda self: self.GetCanonicalType() if hasattr(self, 'GetCanonicalType') else self
182

hjk's avatar
hjk committed
183
184
185
lldb.SBType.__orig__str__ = lldb.SBType.__str__
lldb.SBType.__str__ = lldb.SBType.GetName

186
class Dumper(DumperBase):
187
    def __init__(self):
188
189
        DumperBase.__init__(self)

190
191
        lldb.theDumper = self

hjk's avatar
hjk committed
192
        self.outputLock = threading.Lock()
193
        self.debugger = lldb.SBDebugger.Create()
194
        #self.debugger.SetLoggingCallback(loggingCallback)
hjk's avatar
hjk committed
195
196
197
198
        #def loggingCallback(args):
        #    s = args.strip()
        #    s = s.replace('"', "'")
        #    sys.stdout.write('log="%s"@\n' % s)
199
        #Same as: self.debugger.HandleCommand("log enable lldb dyld step")
hjk's avatar
hjk committed
200
201
        #self.debugger.EnableLog("lldb", ["dyld", "step", "process", "state",
        #    "thread", "events",
202
203
204
205
        #    "communication", "unwind", "commands"])
        #self.debugger.EnableLog("lldb", ["all"])
        self.debugger.Initialize()
        self.debugger.HandleCommand("settings set auto-confirm on")
206
207
208
209
210
211
212
213
214
215

        # 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)

216
        self.isLldb = True
hjk's avatar
hjk committed
217
        self.isGoodLldb = hasattr(lldb.SBValue, "SetPreferDynamicValue")
218
219
220
221
        self.process = None
        self.target = None
        self.eventState = lldb.eStateInvalid
        self.expandedINames = {}
222
        self.passExceptions = False
223
        self.useLldbDumpers = False
hjk's avatar
hjk committed
224
        self.autoDerefPointers = True
225
        self.useDynamicType = True
hjk's avatar
hjk committed
226
        self.useFancy = True
hjk's avatar
hjk committed
227
228
        self.formats = {}
        self.typeformats = {}
229
230

        self.currentIName = None
231
232
        self.currentValue = ReportItem()
        self.currentType = ReportItem()
233
234
235
236
237
        self.currentNumChild = None
        self.currentMaxNumChild = None
        self.currentPrintsAddress = None
        self.currentChildType = None
        self.currentChildNumChild = None
238
        self.currentWatchers = {}
239

hjk's avatar
hjk committed
240
        self.executable_ = None
241
242
243
244
        self.startMode_ = None
        self.processArgs_ = None
        self.attachPid_ = None

245
246
        self.charType_ = None
        self.intType_ = None
247
        self.int64Type_ = None
248
249
        self.sizetType_ = None
        self.charPtrType_ = None
hjk's avatar
hjk committed
250
        self.voidPtrType_ = None
251
        self.isShuttingDown_ = False
252
        self.isInterrupting_ = False
253
        self.dummyValue = None
254
        self.breakpointsToCheck = set([])
255
256
257
        self.qmlBreakpointResolvers = {}
        self.qmlTriggeredBreakpoint = None
        self.nativeMixed = False
258

259
260
261
262
    def enterSubItem(self, item):
        if isinstance(item.name, lldb.SBValue):
            # Avoid $$__synth__ suffix on Mac.
            value = item.name
hjk's avatar
hjk committed
263
264
            if self.isGoodLldb:
                value.SetPreferSyntheticValue(False)
265
266
267
268
            item.name = value.GetName()
            if item.name is None:
                self.anonNumber += 1
                item.name = "#%d" % self.anonNumber
269
270
        if not item.iname:
            item.iname = "%s.%s" % (self.currentIName, item.name)
271
272
273
274
275
276
277
278
279
280
        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
281
282
        self.currentValue = ReportItem()
        self.currentType = ReportItem()
283
284
285
286
287
288
289
290

    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:
hjk's avatar
hjk committed
291
292
293
294
            if self.currentType.value:
                typeName = self.currentType.value
                if len(typeName) > 0 and typeName != self.currentChildType:
                    self.put('type="%s",' % typeName) # str(type.unqualified()) ?
295
            if  self.currentValue.value is None:
296
297
                self.put('value="<not accessible>",numchild="0",')
            else:
298
299
300
301
302
                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)
303
304
305
306
307
308
309
310
        except:
            pass
        self.put('},')
        self.currentIName = item.savedIName
        self.currentValue = item.savedValue
        self.currentType = item.savedType
        return True

hjk's avatar
hjk committed
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
    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

351
352
353
354
    def isSimpleType(self, typeobj):
        typeClass = typeobj.GetTypeClass()
        return typeClass == lldb.eTypeClassBuiltin

hjk's avatar
hjk committed
355
356
357
    def childWithName(self, value, name):
        child = value.GetChildMemberWithName(name)
        return child if child.IsValid() else None
358

359
360
361
    def simpleValue(self, value):
        return str(value.value)

362
363
364
365
366
367
368
369
370
    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()

371
372
373
374
375
    def enumExpression(self, enumType, enumValue):
        ns = self.qtNamespace()
        return ns + "Qt::" + enumType + "(" \
            + ns + "Qt::" + enumType + "::" + enumValue + ")"

376
    def callHelper(self, value, func, args):
377
378
        # args is a tuple.
        arg = ','.join(args)
379
        #self.warn("CALL: %s -> %s(%s)" % (value, func, arg))
380
381
        type = value.type.name
        exp = "((%s*)%s)->%s(%s)" % (type, value.address, func, arg)
382
        #self.warn("CALL: %s" % exp)
383
        result = value.CreateValueFromExpression('', exp)
384
        #self.warn("  -> %s" % result)
385
386
        return result

387
388
389
390
391
    def makeValue(self, type, *args):
        thread = self.currentThread()
        frame = thread.GetFrameAtIndex(0)
        inner = ','.join(args)
        value = frame.EvaluateExpression(type + '{' + inner + '}')
392
393
394
        #self.warn("  TYPE: %s" % value.type)
        #self.warn("  ADDR: 0x%x" % value.address)
        #self.warn("  VALUE: %s" % value)
395
396
        return value

397
398
399
400
401
    def parseAndEvaluate(self, expr):
        thread = self.currentThread()
        frame = thread.GetFrameAtIndex(0)
        return frame.EvaluateExpression(expr)

402
403
404
405
406
407
408
409
    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):
410
        result = typeobj.GetDirectBaseClassAtIndex(index).GetType()
411
        return result if result.IsValid() else None
412

413
    def templateArgument(self, typeobj, index):
414
        type = typeobj.GetTemplateArgumentType(index)
hjk's avatar
hjk committed
415
        if type.IsValid():
416
417
418
419
420
            return type
        inner = self.extractTemplateArgument(typeobj.GetName(), index)
        return self.lookupType(inner)

    def numericTemplateArgument(self, typeobj, index):
421
        # There seems no API to extract the numeric value.
422
        inner = self.extractTemplateArgument(typeobj.GetName(), index)
423
424
425
426
427
428
429
430
        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
431

hjk's avatar
hjk committed
432
433
434
435
436
437
    def isReferenceType(self, typeobj):
        return typeobj.IsReferenceType()

    def isStructType(self, typeobj):
        return typeobj.GetTypeClass() in (lldb.eTypeClassStruct, lldb.eTypeClassClass)

438
439
440
441
442
443
444
445
446
    def isWindowsTarget(self):
        return False

    def isQnxTarget(self):
        return False

    def isArmArchitecture(self):
        return False

447
    def qtVersionAndNamespace(self):
448
449
        for func in self.target.FindFunctions('qVersion'):
            name = func.GetSymbol().GetName()
450
451
            if name.endswith('()'):
                name = name[:-2]
452
453
454
            if name.count(':') > 2:
                continue

455
456
457
            qtNamespace = name[:name.find('qVersion')]
            self.qtNamespace = lambda: qtNamespace

458
459
460
461
462
463
464
465
466
467
            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)
468

469
470
471
472
            if not res.IsValid() or not res.GetType().IsPointerType():
                continue

            version = str(res)
473
474
475
            if version.count('.') != 2:
                continue

476
477
            version.replace("'", '"') # Both seem possible
            version = version[version.find('"')+1:version.rfind('"')]
478
479
480
481
482
483
484
485

            (major, minor, patch) = version.split('.')
            qtVersion = 0x10000 * int(major) + 0x100 * int(minor) + int(patch)
            self.qtVersion = lambda: qtVersion

            return (qtNamespace, qtVersion)

        return ('', 0x50200)
486
487

    def qtNamespace(self):
488
        return self.qtVersionAndNamespace()[0]
489
490

    def qtVersion(self):
491
        self.qtVersionAndNamespace()
492
        return self.qtVersionAndNamespace()[1]
493

hjk's avatar
hjk committed
494
495
496
    def intSize(self):
        return 4

497
498
    def intType(self):
        if self.intType_ is None:
hjk's avatar
hjk committed
499
             self.intType_ = self.target.FindFirstType('int')
500
501
        return self.intType_

502
503
504
505
506
    def int64Type(self):
        if self.int64Type_ is None:
             self.int64Type_ = self.target.FindFirstType('long long int')
        return self.int64Type_

507
508
    def charType(self):
        if self.charType_ is None:
hjk's avatar
hjk committed
509
             self.charType_ = self.target.FindFirstType('char')
510
511
512
513
514
515
516
517
        return self.charType_

    def charPtrType(self):
        if self.charPtrType_ is None:
             self.charPtrType_ = self.charType().GetPointerType()
        return self.charPtrType_

    def voidPtrType(self):
hjk's avatar
hjk committed
518
519
520
        if self.voidPtrType_ is None:
             self.voidPtrType_ = self.target.FindFirstType('void').GetPointerType()
        return self.voidPtrType_
521

hjk's avatar
hjk committed
522
    def ptrSize(self):
523
524
525
526
527
528
529
        return self.charPtrType().GetByteSize()

    def sizetType(self):
        if self.sizetType_ is None:
             self.sizetType_ = self.lookupType('size_t')
        return self.sizetType_

530
531
532
    def addressOf(self, value):
        return int(value.GetLoadAddress())

hjk's avatar
hjk committed
533
534
535
    def extractInt(self, address):
        return int(self.createValue(address, self.intType()))

536
537
538
    def extractInt64(self, address):
        return int(self.createValue(address, self.int64Type()))

539
540
541
    def extractByte(self, address):
        return int(self.createValue(address, self.charType())) & 0xFF

542
543
544
545
546
547
    def handleCommand(self, command):
        result = lldb.SBCommandReturnObject()
        self.debugger.GetCommandInterpreter().HandleCommand(command, result)
        success = result.Succeeded()
        if success:
            self.report('output="%s"' % result.GetOutput())
548
        else:
549
550
551
552
553
554
            self.report('error="%s"' % result.GetError())
        self.reportData()

    def put(self, stuff):
        sys.stdout.write(stuff)

555
    def isMovableType(self, type):
556
        if type.GetTypeClass() in (lldb.eTypeClassBuiltin, lldb.eTypeClassPointer):
557
            return True
558
        return self.isKnownMovableType(self.stripNamespaceFromType(type.GetName()))
559

560
    def putNumChild(self, numchild):
561
        #self.warn("NUM CHILD: '%s' '%s'" % (numchild, self.currentChildNumChild))
hjk's avatar
hjk committed
562
563
        #if numchild != self.currentChildNumChild:
        self.put('numchild="%s",' % numchild)
564

565
566
567
568
569
570
571
    def putPointerValue(self, value):
        # Use a lower priority
        if value is None:
            self.putEmptyValue(-1)
        else:
            self.putValue("0x%x" % value.Dereference())

hjk's avatar
hjk committed
572
573
574
    def putSimpleValue(self, value, encoding = None, priority = 0):
        self.putValue(value.GetValue(), encoding, priority)

575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
    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
603

604
605
606
607
608
609
610
611
    def createPointerValue(self, address, pointeeType):
        addr = int(address) & 0xFFFFFFFFFFFFFFFF
        return self.context.CreateValueFromAddress(None, addr, pointeeType).AddressOf()

    def createValue(self, address, referencedType):
        addr = int(address) & 0xFFFFFFFFFFFFFFFF
        return self.context.CreateValueFromAddress(None, addr, referencedType)

612
613
614
615
616
    def childRange(self):
        if self.currentMaxNumChild is None:
            return xrange(0, self.currentNumChild)
        return xrange(min(self.currentMaxNumChild, self.currentNumChild))

617
618
619
    def canonicalTypeName(self, name):
        return re.sub('\\bconst\\b', '', name).replace(' ', '')

620
    def lookupType(self, name):
621
        #self.warn("LOOKUP TYPE NAME: %s" % name)
622
623
624
        typeobj = self.target.FindFirstType(name)
        if typeobj.IsValid():
            return typeobj
hjk's avatar
hjk committed
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
        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)
655
        return None
656

657
    def setupInferior(self, args):
658
        error = lldb.SBError()
659
660

        self.executable_ = args['executable']
661
        self.startMode_ = args.get('startMode', 1)
662
        self.breakOnMain_ = args.get('breakOnMain', 0)
hjk's avatar
hjk committed
663
        self.useTerminal_ = args.get('useTerminal', 0)
664
665
        self.processArgs_ = args.get('processArgs', [])
        self.processArgs_ = map(lambda x: self.hexdecode(x), self.processArgs_)
666
        self.attachPid_ = args.get('attachPid', 0)
Fawzi Mohamed's avatar
Fawzi Mohamed committed
667
        self.sysRoot_ = args.get('sysRoot', '')
Fawzi Mohamed's avatar
Fawzi Mohamed committed
668
        self.remoteChannel_ = args.get('remoteChannel', '')
669
        self.platform_ = args.get('platform', '')
670

hjk's avatar
hjk committed
671
672
673
        self.ignoreStops = 0
        if self.useTerminal_ and platform.system() == "Linux":
            self.ignoreStops = 2
hjk's avatar
hjk committed
674

675
676
        if self.platform_:
            self.debugger.SetCurrentPlatform(self.platform_)
Fawzi Mohamed's avatar
Fawzi Mohamed committed
677
678
679
        # sysroot has to be set *after* the platform
        if self.sysRoot_:
            self.debugger.SetCurrentPlatformSDKRoot(self.sysRoot_)
680
681
682
683
684

        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)
685

686
        if self.target.IsValid():
687
688
            for bp in args['bkpts']:
                self.insertBreakpoint(bp)
689

690
691
        state = "inferiorsetupok" if self.target.IsValid() else "inferiorsetupfailed"
        self.report('state="%s",msg="%s",exe="%s"' % (state, error, self.executable_))
692

693
    def runEngine(self, _):
hjk's avatar
hjk committed
694
        self.prepare()
hjk's avatar
hjk committed
695
696
697
        s = threading.Thread(target=self.loop, args=[])
        s.start()

hjk's avatar
hjk committed
698
    def prepare(self):
699
        error = lldb.SBError()
hjk's avatar
hjk committed
700
701
        listener = self.debugger.GetListener()

702
703
704
        if self.attachPid_ > 0:
            attachInfo = lldb.SBAttachInfo(self.attachPid_)
            self.process = self.target.Attach(attachInfo, error)
705
            if not error.Success():
706
                self.reportState("inferiorrunfailed")
Fawzi Mohamed's avatar
Fawzi Mohamed committed
707
708
                return
            self.report('pid="%s"' % self.process.GetProcessID())
709
710
711
712
            # 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")
hjk's avatar
hjk committed
713
        elif self.startMode_ == AttachToRemoteServer or self.startMode_ == AttachToRemoteProcess:
Fawzi Mohamed's avatar
Fawzi Mohamed committed
714
            self.process = self.target.ConnectRemote(
715
716
                self.debugger.GetListener(),
                self.remoteChannel_, None, error)
717
            if not error.Success():
718
719
                self.reportError(error)
                self.reportState("enginerunfailed")
720
                return
721
722
723
724
            # 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")
725
        else:
726
            launchInfo = lldb.SBLaunchInfo(self.processArgs_)
727
            launchInfo.SetWorkingDirectory(os.getcwd())
728
729
            environmentList = [key + "=" + value for key,value in os.environ.items()]
            launchInfo.SetEnvironmentEntries(environmentList, False)
730
731
            if self.breakOnMain_:
                self.createBreakpointAtMain()
732
            self.process = self.target.Launch(launchInfo, error)
733
            if not error.Success():
734
                self.reportError(error)
735
                self.reportState("enginerunfailed")
Fawzi Mohamed's avatar
Fawzi Mohamed committed
736
737
                return
            self.report('pid="%s"' % self.process.GetProcessID())
738
            self.reportState("enginerunandinferiorrunok")
739

hjk's avatar
hjk committed
740
    def loop(self):
hjk's avatar
hjk committed
741
        event = lldb.SBEvent()
hjk's avatar
hjk committed
742
        listener = self.debugger.GetListener()
hjk's avatar
hjk committed
743
744
745
746
747
        while True:
            if listener.WaitForEvent(10000000, event):
                self.handleEvent(event)
            else:
                warn('TIMEOUT')
748

749
    def describeError(self, error):
750
751
752
753
754
        desc = lldb.SBStream()
        error.GetDescription(desc)
        result = 'error={type="%s"' % error.GetType()
        result += ',code="%s"' % error.GetError()
        result += ',desc="%s"}' % desc.GetData()
755
756
757
758
        return result

    def reportError(self, error):
        self.report(self.describeError(error))
hjk's avatar
hjk committed
759
760
        if error.GetType():
            self.reportStatus(error.GetCString())
761
762

    def currentThread(self):
763
        return None if self.process is None else self.process.GetSelectedThread()
764
765

    def currentFrame(self):
766
767
        thread = self.currentThread()
        return None if thread is None else thread.GetSelectedFrame()
768
769
770

    def reportLocation(self):
        thread = self.currentThread()
771
        frame = thread.GetSelectedFrame()
772
773
        file = fileName(frame.line_entry.file)
        line = frame.line_entry.line
774
775
        self.report('location={file="%s",line="%s",addr="%s"}'
            % (file, line, frame.pc))
776

777
778
779
    def firstStoppedThread(self):
        for i in xrange(0, self.process.GetNumThreads()):
            thread = self.process.GetThreadAtIndex(i)
780
781
782
783
784
785
            reason = thread.GetStopReason()
            if (reason == lldb.eStopReasonBreakpoint or
                    reason == lldb.eStopReasonException or
                    reason == lldb.eStopReasonPlanComplete or
                    reason == lldb.eStopReasonSignal or
                    reason == lldb.eStopReasonWatchpoint):
786
787
788
                return thread
        return None

789
790
    def reportThreads(self):
        result = 'threads={threads=['
791
        for i in xrange(0, self.process.GetNumThreads()):
792
            thread = self.process.GetThreadAtIndex(i)
hjk's avatar
hjk committed
793
794
795
796
797
798
799
            if thread.is_stopped:
                state = "stopped"
            elif thread.is_suspended:
                state = "suspended"
            else:
                state = "unknown"
            reason = thread.GetStopReason()
800
801
            result += '{id="%d"' % thread.GetThreadID()
            result += ',index="%s"' % i
802
            result += ',details="%s"' % thread.GetQueueName()
hjk's avatar
hjk committed
803
804
            result += ',stop-reason="%s"' % self.stopReason(thread.GetStopReason())
            result += ',state="%s"' % state
805
            result += ',name="%s"' % thread.GetName()
806
807
808
809
810
            result += ',frame={'
            frame = thread.GetFrameAtIndex(0)
            result += 'pc="0x%x"' % frame.pc
            result += ',addr="0x%x"' % frame.pc
            result += ',fp="0x%x"' % frame.fp
811
            result += ',func="%s"' % frame.GetFunctionName()
812
813
814
815
816
817
818
819
            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 += '],current-thread-id="%s"},' % self.currentThread().id
        self.report(result)

820
821
822
823
824
825
826
827
    def reportChangedBreakpoints(self):
        for i in xrange(0, self.target.GetNumBreakpoints()):
            bp = self.target.GetBreakpointAtIndex(i)
            if bp.GetID() in self.breakpointsToCheck:
                if bp.GetNumLocations():
                    self.breakpointsToCheck.remove(bp.GetID())
                    self.report('breakpoint-changed={%s}' % self.describeBreakpoint(bp))

828
    def firstUsableFrame(self, thread):
hjk's avatar
hjk committed
829
        for i in xrange(10):
hjk's avatar
hjk committed
830
831
832
833
834
835
836
            frame = thread.GetFrameAtIndex(i)
            lineEntry = frame.GetLineEntry()
            line = lineEntry.GetLine()
            if line != 0:
                return i
        return None

837
    def reportStack(self, args = {}):
838
        if not self.process:
839
            self.report('msg="No process"')
840
841
            return
        thread = self.currentThread()
842
        limit = args.get('stacklimit', -1)
843
844
845
        if not thread:
            self.report('msg="No thread"')
            return
846
847
848

        (n, isLimited) = (limit, True) if limit > 0 else (thread.GetNumFrames(), False)

849
        self.currentCallContext = None
850
        result = 'stack={current-thread="%s"' % thread.GetThreadID()
851
852
853
        result += ',frames=['
        for i in xrange(n):
            frame = thread.GetFrameAtIndex(i)
854
855
856
            if not frame.IsValid():
                isLimited = False
                break
857

858
            lineEntry = frame.GetLineEntry()
859
860
861
862
863
864
865
866
867
868
869
870
            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 self.nativeMixed:
                if self.isReportableQmlFrame(functionName):
871
872
873
874
875
876
877
878
879
                    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'
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897

                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
898
899
900
901
        result += ']'
        result += ',hasmore="%d"' % isLimited
        result += ',limit="%d"' % limit
        result += '}'
902
        self.report(result)
903

904
905
906
907
908
909
910
911
912
913
914
915
916
917
    def reportStackPosition(self):
        thread = self.currentThread()
        if not thread:
            self.report('msg="No thread"')
            return
        frame = thread.GetSelectedFrame()
        if frame:
            self.report('stack-position={id="%s"}' % frame.GetFrameID())
        else:
            self.report('stack-position={id="-1"}')

    def reportStackTop(self):
        self.report('stack-top={}')

hjk's avatar
hjk committed
918
    def extractBlob(self, base, size):
hjk's avatar
hjk committed
919
        if size == 0:
hjk's avatar
hjk committed
920
            return Blob("")
921
922
        base = int(base) & 0xFFFFFFFFFFFFFFFF
        size = int(size) & 0xFFFFFFFF
hjk's avatar
hjk committed
923
        error = lldb.SBError()
hjk's avatar
hjk committed
924
        return Blob(self.process.ReadMemory(base, size, error))
hjk's avatar
hjk committed
925

926
927
928
929
930
931
932
933
934
935
    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))

936
937
938
939
940
941
942
    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)

943
944
    def findSymbol(self, symbolName):
        return self.target.FindFirstGlobalVariable(symbolName)
hjk's avatar
hjk committed
945

946
    def stripNamespaceFromType(self, typeName):
947
        #type = self.stripClassTag(typeName)
948
        type = typeName
949
950
951
        ns = self.qtNamespace()
        if len(ns) > 0 and type.startswith(ns):
            type = type[len(ns):]
952
953
954
955
956
957
        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("<")
hjk's avatar
hjk committed
958
959
960
961
        if type.startswith("const "):
            type = type[6:]
        if type.startswith("volatile "):
            type = type[9:]
962
963
964
        return type

    def putSubItem(self, component, value, tryDynamic=True):
965
        if not value.IsValid():
966
            warn("INVALID SUBITEM: %s" % value.GetName())
967
            return
968
969
970
        with SubItem(self, component):
            self.putItem(value, tryDynamic)

hjk's avatar
hjk committed
971
    def putAddress(self, addr):
972
973
974
975
        #if int(addr) == 0xffffffffffffffff:
        #    raise RuntimeError("Illegal address")
        if self.currentPrintsAddress and not addr is None:
            self.put('addr="0x%x",' % int(addr))
hjk's avatar
hjk committed
976

hjk's avatar
hjk committed
977
978
979
980
981
    def isFunctionType(self, typeobj):
        if self.isGoodLldb:
            return typeobj.IsFunctionType()
        #warn("TYPE: %s" % typeobj)
        return False
hjk's avatar
hjk committed
982

983
    def putItem(self, value, tryDynamic=True):
984
        typeName = value.GetType().GetUnqualifiedType().GetName()
hjk's avatar
hjk committed
985
986
        if self.isGoodLldb:
            value.SetPreferDynamicValue(tryDynamic)
hjk's avatar
hjk committed
987
        typeClass = value.GetType().GetTypeClass()
988

hjk's avatar
hjk committed
989
        if tryDynamic:
990
            self.putAddress(value.GetLoadAddress())
hjk's avatar
hjk committed
991

992
        # Handle build-in LLDB visualizers if wanted.
993
        if False and self.useLldbDumpers and value.GetTypeSynthetic().IsValid():
994
995
996
997
            # FIXME: print "official" summary?
            summary = value.GetTypeSummary()
            if summary.IsValid():
                warn("DATA: %s" % summary.GetData())
hjk's avatar
hjk committed
998
999
            if self.isGoodLldb:
                value.SetPreferSyntheticValue(False)
1000
1001
1002
1003
1004
            provider = value.GetTypeSynthetic()
            data = provider.GetData()
            formatter = eval(data)(value, {})
            formatter.update()
            numchild = formatter.num_children()
hjk's avatar
hjk committed
1005
            self.put('iname="%s",' % self.currentIName)
hjk's avatar
hjk committed
1006
            self.putType(typeName)
hjk's avatar
hjk committed
1007
1008
            self.put('numchild="%s",' % numchild)
            self.put('addr="0x%x",' % value.GetLoadAddress())
1009
            self.putItemCount(numchild)
hjk's avatar
hjk committed
1010
1011
            if self.currentIName in self.expandedINames:
                with Children(self):
1012
1013
1014
1015
1016
1017
                    for i in xrange(numchild):
                        child = formatter.get_child_at_index(i)
                        with SubItem(self, i):
                            self.putItem(child)
            return

hjk's avatar
hjk committed
1018
1019
        # Typedefs
        if typeClass == lldb.eTypeClassTypedef:
1020
            if typeName in self.qqDumpers:
hjk's avatar
hjk committed
1021
1022
                self.putType(typeName)
                self.context = value
1023
                self.qqDumpers[typeName](self, value)
hjk's avatar
hjk committed
1024
                return
hjk's avatar
hjk committed
1025
1026
            realType = value.GetType()
            if hasattr(realType, 'GetCanonicalType'):
1027
                baseType = realType.GetCanonicalType()
1028
1029
1030
1031
1032
                if baseType != realType:
                    baseValue = value.Cast(baseType.unqualified())
                    self.putItem(baseValue)
                    self.putBetterType(realType)
                    return
hjk's avatar
hjk committed
1033

1034
        # Our turf now.
hjk's avatar
hjk committed
1035
1036
        if self.isGoodLldb:
            value.SetPreferSyntheticValue(False)
1037

1038
        # Arrays
hjk's avatar
hjk committed
1039
        if typeClass == lldb.eTypeClassArray: