lldbbridge.py 69.2 KB
Newer Older
1
2
############################################################################
#
Eike Ziller's avatar
Eike Ziller committed
3
4
# Copyright (C) 2015 The Qt Company Ltd.
# Contact: http://www.qt.io/licensing
5
6
7
8
9
10
11
#
# 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
Eike Ziller's avatar
Eike Ziller committed
12
13
# a written agreement between you and The Qt Company.  For licensing terms and
# conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
# 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
#
Eike Ziller's avatar
Eike Ziller committed
25
26
# In addition, as a special exception, The Qt Company gives you certain additional
# rights.  These rights are described in The Qt Company LGPL Exception
27
28
29
# version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
#
#############################################################################
30
31
32

import inspect
import os
33
import platform
34
import re
35
import sys
36
import threading
37
import lldb
38

hjk's avatar
hjk committed
39
sys.path.insert(1, os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))))
40
41
42

from dumper import *

43
44
45
46
47
48
#######################################################################
#
# Helpers
#
#######################################################################

49
qqWatchpointOffset = 10000
50

51
def warn(message):
52
    print('\n\nWARNING="%s",\n' % message.encode("latin1").replace('"', "'"))
53
54
55

def showException(msg, exType, exValue, exTraceback):
    warn("**** CAUGHT EXCEPTION: %s ****" % msg)
56
57
58
    import traceback
    lines = [line for line in traceback.format_exception(exType, exValue, exTraceback)]
    warn('\n'.join(lines))
59
60
61
62

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

63

hjk's avatar
hjk committed
64
65
66
67
def check(exp):
    if not exp:
        raise RuntimeError("Check failed")

68
69
Value = lldb.SBValue

70
71
def impl_SBValue__add__(self, offset):
    if self.GetType().IsPointerType():
72
        if isinstance(offset, int) or isinstance(offset, long):
73
74
75
            pass
        else:
            offset = offset.GetValueAsSigned()
76
        itemsize = self.GetType().GetPointeeType().GetByteSize()
77
        address = self.GetValueAsUnsigned() + offset * itemsize
78
        address = address & 0xFFFFFFFFFFFFFFFF  # Force unsigned
79
80
        return self.CreateValueFromAddress(None, address,
                self.GetType().GetPointeeType()).AddressOf()
hjk's avatar
hjk committed
81

82
    raise RuntimeError("SBValue.__add__ not implemented: %s" % self.GetType())
83
84
    return NotImplemented

85
def impl_SBValue__sub__(self, other):
86
    if self.GetType().IsPointerType():
87
        if isinstance(other, int) or isinstance(other, long):
88
            address = self.GetValueAsUnsigned() - other
89
90
            address = address & 0xFFFFFFFFFFFFFFFF  # Force unsigned
            return self.CreateValueFromAddress(None, address, self.GetType())
91
        if other.GetType().IsPointerType():
92
93
            itemsize = self.GetType().GetPointeeType().GetByteSize()
            return (self.GetValueAsUnsigned() - other.GetValueAsUnsigned()) / itemsize
94
    raise RuntimeError("SBValue.__sub__ not implemented: %s" % self.GetType())
95
96
    return NotImplemented

97
98
99
100
101
102
103
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):
104
    return self.GetValueAsSigned()
105
106
107
108
109
110
111
112
113
114

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
115

116
117
118
def impl_SBValue__long__(self):
    return int(self.GetValue(), 0)

119
def impl_SBValue__getitem__(value, index):
120
    if isinstance(index, int) or isinstance(index, long):
121
122
123
124
125
126
127
        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)
128
129
130
131
    item = value.GetChildMemberWithName(index)
    if item.IsValid():
        return item
    raise RuntimeError("SBValue.__getitem__: No such member '%s'" % index)
132

133
134
135
136
def impl_SBValue__deref(value):
    result = value.Dereference()
    if result.IsValid():
        return result
137
138
    exp = "*(class %s*)0x%x" % (value.GetType().GetPointeeType(), value.GetValueAsUnsigned())
    return value.CreateValueFromExpression(None, exp)
139

140
lldb.SBValue.__add__ = impl_SBValue__add__
141
lldb.SBValue.__sub__ = impl_SBValue__sub__
142
lldb.SBValue.__le__ = impl_SBValue__le__
143

144
145
lldb.SBValue.__getitem__ = impl_SBValue__getitem__
lldb.SBValue.__int__ = impl_SBValue__int__
146
lldb.SBValue.__float__ = impl_SBValue__float__
147
148
lldb.SBValue.__long__ = lambda self: long(self.GetValue(), 0)

149
lldb.SBValue.code = lambda self: self.GetTypeClass()
150
lldb.SBValue.cast = lambda self, typeObj: self.Cast(typeObj)
151
lldb.SBValue.dereference = impl_SBValue__deref
152
lldb.SBValue.address = property(lambda self: self.GetLoadAddress())
153
154

lldb.SBType.pointer = lambda self: self.GetPointerType()
hjk's avatar
hjk committed
155
lldb.SBType.target = lambda self: self.GetPointeeType()
156
lldb.SBType.code = lambda self: self.GetTypeClass()
157
lldb.SBType.sizeof = property(lambda self: self.GetByteSize())
158
159


hjk's avatar
hjk committed
160
161
lldb.SBType.unqualified = \
    lambda self: self.GetUnqualifiedType() if hasattr(self, 'GetUnqualifiedType') else self
162
163
lldb.SBType.strip_typedefs = \
    lambda self: self.GetCanonicalType() if hasattr(self, 'GetCanonicalType') else self
164

hjk's avatar
hjk committed
165
166
167
lldb.SBType.__orig__str__ = lldb.SBType.__str__
lldb.SBType.__str__ = lldb.SBType.GetName

168
class Dumper(DumperBase):
169
    def __init__(self):
170
171
        DumperBase.__init__(self)

hjk's avatar
hjk committed
172
        self.outputLock = threading.Lock()
173
        self.debugger = lldb.SBDebugger.Create()
174
        #self.debugger.SetLoggingCallback(loggingCallback)
hjk's avatar
hjk committed
175
176
177
178
        #def loggingCallback(args):
        #    s = args.strip()
        #    s = s.replace('"', "'")
        #    sys.stdout.write('log="%s"@\n' % s)
179
        #Same as: self.debugger.HandleCommand("log enable lldb dyld step")
hjk's avatar
hjk committed
180
181
        #self.debugger.EnableLog("lldb", ["dyld", "step", "process", "state",
        #    "thread", "events",
182
183
184
185
        #    "communication", "unwind", "commands"])
        #self.debugger.EnableLog("lldb", ["all"])
        self.debugger.Initialize()
        self.debugger.HandleCommand("settings set auto-confirm on")
186
187
188
189
190
191
192
193
194
195

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

196
        self.isLldb = True
hjk's avatar
hjk committed
197
        self.isGoodLldb = hasattr(lldb.SBValue, "SetPreferDynamicValue")
198
199
200
201
        self.process = None
        self.target = None
        self.eventState = lldb.eStateInvalid
        self.expandedINames = {}
202
        self.passExceptions = False
203
        self.useLldbDumpers = False
hjk's avatar
hjk committed
204
        self.autoDerefPointers = True
205
        self.useDynamicType = True
hjk's avatar
hjk committed
206
        self.useFancy = True
hjk's avatar
hjk committed
207
208
        self.formats = {}
        self.typeformats = {}
209
210

        self.currentIName = None
211
212
        self.currentValue = ReportItem()
        self.currentType = ReportItem()
213
214
215
216
        self.currentNumChild = None
        self.currentMaxNumChild = None
        self.currentPrintsAddress = None
        self.currentChildType = None
217
        self.currentChildNumChild = -1
218
        self.currentWatchers = {}
219

hjk's avatar
hjk committed
220
        self.executable_ = None
221
222
223
224
        self.startMode_ = None
        self.processArgs_ = None
        self.attachPid_ = None

225
226
        self.charType_ = None
        self.intType_ = None
227
        self.int64Type_ = None
228
229
        self.sizetType_ = None
        self.charPtrType_ = None
hjk's avatar
hjk committed
230
        self.voidPtrType_ = None
231
        self.isShuttingDown_ = False
232
        self.isInterrupting_ = False
233
        self.dummyValue = None
234
235
        self.qmlBreakpointResolvers = {}
        self.qmlTriggeredBreakpoint = None
236

hjk's avatar
hjk committed
237
238
239
        self.report('lldbversion=\"%s\"' % lldb.SBDebugger.GetVersionString())
        self.reportState("enginesetupok")

240
241
242
243
    def enterSubItem(self, item):
        if isinstance(item.name, lldb.SBValue):
            # Avoid $$__synth__ suffix on Mac.
            value = item.name
hjk's avatar
hjk committed
244
245
            if self.isGoodLldb:
                value.SetPreferSyntheticValue(False)
246
247
248
249
            item.name = value.GetName()
            if item.name is None:
                self.anonNumber += 1
                item.name = "#%d" % self.anonNumber
250
251
        if not item.iname:
            item.iname = "%s.%s" % (self.currentIName, item.name)
252
253
254
255
256
257
258
259
260
261
        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
262
263
        self.currentValue = ReportItem()
        self.currentType = ReportItem()
264
265
266
267
268
269
270
271

    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
272
273
274
275
            if self.currentType.value:
                typeName = self.currentType.value
                if len(typeName) > 0 and typeName != self.currentChildType:
                    self.put('type="%s",' % typeName) # str(type.unqualified()) ?
276
            if  self.currentValue.value is None:
277
278
                self.put('value="<not accessible>",numchild="0",')
            else:
279
280
281
282
283
                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)
284
285
286
287
288
289
290
291
        except:
            pass
        self.put('},')
        self.currentIName = item.savedIName
        self.currentValue = item.savedValue
        self.currentType = item.savedType
        return True

hjk's avatar
hjk committed
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
    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

332
333
334
335
    def isSimpleType(self, typeobj):
        typeClass = typeobj.GetTypeClass()
        return typeClass == lldb.eTypeClassBuiltin

hjk's avatar
hjk committed
336
337
338
    def childWithName(self, value, name):
        child = value.GetChildMemberWithName(name)
        return child if child.IsValid() else None
339

340
341
342
    def simpleValue(self, value):
        return str(value.value)

343
344
345
346
347
348
349
350
351
    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()

352
353
354
355
356
    def enumExpression(self, enumType, enumValue):
        ns = self.qtNamespace()
        return ns + "Qt::" + enumType + "(" \
            + ns + "Qt::" + enumType + "::" + enumValue + ")"

357
    def callHelper(self, value, func, args):
358
359
        # args is a tuple.
        arg = ','.join(args)
360
        #self.warn("CALL: %s -> %s(%s)" % (value, func, arg))
361
362
        type = value.type.name
        exp = "((%s*)%s)->%s(%s)" % (type, value.address, func, arg)
363
        #self.warn("CALL: %s" % exp)
364
        result = value.CreateValueFromExpression('', exp)
365
        #self.warn("  -> %s" % result)
366
367
        return result

hjk's avatar
hjk committed
368
369
370
371
    def isBadPointer(self, value):
        target = value.dereference()
        return target.GetError().Fail()

372
373
374
375
376
    def makeValue(self, type, *args):
        thread = self.currentThread()
        frame = thread.GetFrameAtIndex(0)
        inner = ','.join(args)
        value = frame.EvaluateExpression(type + '{' + inner + '}')
377
378
379
        #self.warn("  TYPE: %s" % value.type)
        #self.warn("  ADDR: 0x%x" % value.address)
        #self.warn("  VALUE: %s" % value)
380
381
        return value

382
383
384
385
386
    def parseAndEvaluate(self, expr):
        thread = self.currentThread()
        frame = thread.GetFrameAtIndex(0)
        return frame.EvaluateExpression(expr)

387
388
389
390
391
392
393
394
    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):
395
        result = typeobj.GetDirectBaseClassAtIndex(index).GetType()
396
        return result if result.IsValid() else None
397

398
    def templateArgument(self, typeobj, index):
399
        type = typeobj.GetTemplateArgumentType(index)
hjk's avatar
hjk committed
400
        if type.IsValid():
401
402
403
404
405
            return type
        inner = self.extractTemplateArgument(typeobj.GetName(), index)
        return self.lookupType(inner)

    def numericTemplateArgument(self, typeobj, index):
406
        # There seems no API to extract the numeric value.
407
        inner = self.extractTemplateArgument(typeobj.GetName(), index)
408
409
410
411
412
413
414
415
        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
416

hjk's avatar
hjk committed
417
418
419
420
421
422
    def isReferenceType(self, typeobj):
        return typeobj.IsReferenceType()

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

423
424
425
426
427
428
429
430
431
    def isWindowsTarget(self):
        return False

    def isQnxTarget(self):
        return False

    def isArmArchitecture(self):
        return False

432
    def qtVersionAndNamespace(self):
433
434
        for func in self.target.FindFunctions('qVersion'):
            name = func.GetSymbol().GetName()
435
436
            if name.endswith('()'):
                name = name[:-2]
437
438
439
            if name.count(':') > 2:
                continue

440
441
442
            qtNamespace = name[:name.find('qVersion')]
            self.qtNamespace = lambda: qtNamespace

443
444
445
446
447
448
449
450
451
452
            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)
453

454
455
456
457
            if not res.IsValid() or not res.GetType().IsPointerType():
                continue

            version = str(res)
458
459
460
            if version.count('.') != 2:
                continue

461
462
            version.replace("'", '"') # Both seem possible
            version = version[version.find('"')+1:version.rfind('"')]
463
464
465
466
467
468
469
470

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

            return (qtNamespace, qtVersion)

        return ('', 0x50200)
471
472

    def qtNamespace(self):
473
        return self.qtVersionAndNamespace()[0]
474
475

    def qtVersion(self):
476
        self.qtVersionAndNamespace()
477
        return self.qtVersionAndNamespace()[1]
478

hjk's avatar
hjk committed
479
480
481
    def intSize(self):
        return 4

482
483
484
    def ptrSize(self):
        return self.target.GetAddressByteSize()

485
    def intType(self):
486
        return self.target.GetBasicType(lldb.eBasicTypeInt)
487

488
    def int64Type(self):
489
        return self.target.GetBasicType(lldb.eBasicTypeLongLong)
490

491
    def charType(self):
492
        return self.target.GetBasicType(lldb.eBasicTypeChar)
493
494

    def charPtrType(self):
495
        return self.target.GetBasicType(lldb.eBasicTypeChar).GetPointerType()
496
497

    def voidPtrType(self):
498
        return self.target.GetBasicType(lldb.eBasicVoid).GetPointerType()
499
500
501
502
503
504

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

505
506
507
    def addressOf(self, value):
        return int(value.GetLoadAddress())

508
    def extractUInt(self, address):
509
        error = lldb.SBError()
510
        return int(self.process.ReadUnsignedFromMemory(address, 4, error))
hjk's avatar
hjk committed
511

512
513
514
515
516
517
518
    def extractInt(self, address):
        i = self.extractUInt(address)
        if i >= 0x80000000:
            i -= 0x100000000
        return i

    def extractUInt64(self, address):
519
        error = lldb.SBError()
520
        return int(self.process.ReadUnsignedFromMemory(address, 8, error))
521

522
523
524
525
526
527
    def extractInt64(self, address):
        i = self.extractUInt64(address)
        if i >= 0x8000000000000000:
            i -= 0x10000000000000000
        return i

528
    def extractByte(self, address):
529
        error = lldb.SBError()
530
        return int(self.process.ReadUnsignedFromMemory(address, 1, error) & 0xFF)
531

532
533
534
535
536
537
    def handleCommand(self, command):
        result = lldb.SBCommandReturnObject()
        self.debugger.GetCommandInterpreter().HandleCommand(command, result)
        success = result.Succeeded()
        if success:
            self.report('output="%s"' % result.GetOutput())
538
        else:
539
540
541
542
543
544
            self.report('error="%s"' % result.GetError())
        self.reportData()

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

545
    def isMovableType(self, type):
546
        if type.GetTypeClass() in (lldb.eTypeClassBuiltin, lldb.eTypeClassPointer):
547
            return True
548
        return self.isKnownMovableType(self.stripNamespaceFromType(type.GetName()))
549

550
551
552
553
554
555
556
    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
557
558
559
    def putSimpleValue(self, value, encoding = None, priority = 0):
        self.putValue(value.GetValue(), encoding, priority)

560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
    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
588

589
590
    def createPointerValue(self, address, pointeeType):
        addr = int(address) & 0xFFFFFFFFFFFFFFFF
591
592
593
594
595
596
        sbaddr = lldb.SBAddress(addr, self.target)
        # Any type.
        # FIXME: This can be replaced with self.target.CreateValueFromExpression
        # as soon as we drop support for lldb builds not having that (~Xcode 6.1)
        dummy = self.target.CreateValueFromAddress('@', sbaddr, self.target.FindFirstType('char'))
        return dummy.CreateValueFromExpression('', '(%s*)%s' % (pointeeType, addr))
597
598
599

    def createValue(self, address, referencedType):
        addr = int(address) & 0xFFFFFFFFFFFFFFFF
600
        sbaddr = lldb.SBAddress(addr, self.target)
601
        return self.target.CreateValueFromAddress('@', sbaddr, referencedType)
602

603
604
605
606
607
    def childRange(self):
        if self.currentMaxNumChild is None:
            return xrange(0, self.currentNumChild)
        return xrange(min(self.currentMaxNumChild, self.currentNumChild))

608
609
610
    def canonicalTypeName(self, name):
        return re.sub('\\bconst\\b', '', name).replace(' ', '')

611
    def lookupType(self, name):
612
        #self.warn("LOOKUP TYPE NAME: %s" % name)
613
614
615
        typeobj = self.target.FindFirstType(name)
        if typeobj.IsValid():
            return typeobj
hjk's avatar
hjk committed
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
        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)
646
        return None
647

648
    def setupInferior(self, args):
649
        self.reportToken(args)
650
        error = lldb.SBError()
651
652

        self.executable_ = args['executable']
653
        self.startMode_ = args.get('startMode', 1)
654
        self.breakOnMain_ = args.get('breakOnMain', 0)
hjk's avatar
hjk committed
655
        self.useTerminal_ = args.get('useTerminal', 0)
656
657
        self.processArgs_ = args.get('processArgs', [])
        self.processArgs_ = map(lambda x: self.hexdecode(x), self.processArgs_)
658
        self.attachPid_ = args.get('attachPid', 0)
Fawzi Mohamed's avatar
Fawzi Mohamed committed
659
        self.sysRoot_ = args.get('sysRoot', '')
Fawzi Mohamed's avatar
Fawzi Mohamed committed
660
        self.remoteChannel_ = args.get('remoteChannel', '')
661
        self.platform_ = args.get('platform', '')
662

hjk's avatar
hjk committed
663
        self.ignoreStops = 0
664
665
666
667
668
669
670
671
672
        self.silentStops = 0
        if platform.system() == "Linux":
            if self.startMode_ == AttachCore:
                pass
            else:
                if self.useTerminal_:
                    self.ignoreStops = 2
                else:
                    self.silentStops = 1
hjk's avatar
hjk committed
673

674
675
676
677
        else:
            if self.useTerminal_:
                self.ignoreStops = 1

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

684

685
686
687
688
        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)
689

690
        if self.target.IsValid():
691
692
            for bp in args['bkpts']:
                self.insertBreakpoint(bp)
693

694
695
        state = "inferiorsetupok" if self.target.IsValid() else "inferiorsetupfailed"
        self.report('state="%s",msg="%s",exe="%s"' % (state, error, self.executable_))
696

697
698
    def runEngine(self, args):
        self.prepare(args)
hjk's avatar
hjk committed
699
700
701
        s = threading.Thread(target=self.loop, args=[])
        s.start()

702
    def prepare(self, args):
703
        self.reportToken(args)
704
        error = lldb.SBError()
hjk's avatar
hjk committed
705
706
        listener = self.debugger.GetListener()

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

hjk's avatar
hjk committed
750
    def loop(self):
hjk's avatar
hjk committed
751
        event = lldb.SBEvent()
hjk's avatar
hjk committed
752
        listener = self.debugger.GetListener()
hjk's avatar
hjk committed
753
754
755
756
757
        while True:
            if listener.WaitForEvent(10000000, event):
                self.handleEvent(event)
            else:
                warn('TIMEOUT')
758

759
    def describeError(self, error):
760
761
762
763
764
        desc = lldb.SBStream()
        error.GetDescription(desc)
        result = 'error={type="%s"' % error.GetType()
        result += ',code="%s"' % error.GetError()
        result += ',desc="%s"}' % desc.GetData()
765
766
767
768
        return result

    def reportError(self, error):
        self.report(self.describeError(error))
hjk's avatar
hjk committed
769
770
        if error.GetType():
            self.reportStatus(error.GetCString())
771
772

    def currentThread(self):
773
        return None if self.process is None else self.process.GetSelectedThread()
774
775

    def currentFrame(self):
776
777
        thread = self.currentThread()
        return None if thread is None else thread.GetSelectedFrame()
778

779
    def reportLocation(self, frame):
780
781
782
783
784
        if int(frame.pc) != 0xffffffffffffffff:
            file = fileName(frame.line_entry.file)
            line = frame.line_entry.line
            self.report('location={file="%s",line="%s",addr="%s"}'
                % (file, line, frame.pc))
785

786
787
788
    def firstStoppedThread(self):
        for i in xrange(0, self.process.GetNumThreads()):
            thread = self.process.GetThreadAtIndex(i)
789
790
791
792
793
794
            reason = thread.GetStopReason()
            if (reason == lldb.eStopReasonBreakpoint or
                    reason == lldb.eStopReasonException or
                    reason == lldb.eStopReasonPlanComplete or
                    reason == lldb.eStopReasonSignal or
                    reason == lldb.eStopReasonWatchpoint):
795
796
797
                return thread
        return None

798
799
    def reportThreads(self, args):
        self.reportToken(args)
800
        result = 'threads={threads=['
801
        for i in xrange(0, self.process.GetNumThreads()):
802
            thread = self.process.GetThreadAtIndex(i)
hjk's avatar
hjk committed
803
804
805
806
807
808
809
            if thread.is_stopped:
                state = "stopped"
            elif thread.is_suspended:
                state = "suspended"
            else:
                state = "unknown"
            reason = thread.GetStopReason()
810
811
            result += '{id="%d"' % thread.GetThreadID()
            result += ',index="%s"' % i
812
            result += ',details="%s"' % thread.GetQueueName()
hjk's avatar
hjk committed
813
814
            result += ',stop-reason="%s"' % self.stopReason(thread.GetStopReason())
            result += ',state="%s"' % state
815
            result += ',name="%s"' % thread.GetName()
816
817
818
819
820
            result += ',frame={'
            frame = thread.GetFrameAtIndex(0)
            result += 'pc="0x%x"' % frame.pc
            result += ',addr="0x%x"' % frame.pc
            result += ',fp="0x%x"' % frame.fp
821
            result += ',func="%s"' % frame.GetFunctionName()
822
823
824
825
826
            result += ',line="%s"' % frame.line_entry.line
            result += ',fullname="%s"' % fileName(frame.line_entry.file)
            result += ',file="%s"' % fileName(frame.line_entry.file)
            result += '}},'

827
        result += ']},'
828
829
        self.report(result)

830
831
832
833
    def reportCurrentThread(self, args):
        self.reportToken(args)
        self.report('current-thread={id="%s"}' % self.currentThread().id)

834
    def firstUsableFrame(self, thread):
hjk's avatar
hjk committed
835
        for i in xrange(10):
hjk's avatar
hjk committed
836
837
838
839
840
841
842
            frame = thread.GetFrameAtIndex(i)
            lineEntry = frame.GetLineEntry()
            line = lineEntry.GetLine()
            if line != 0:
                return i
        return None

843
    def reportStack(self, args):
844
        self.reportToken(args)
845
        if not self.process:
846
            self.report('msg="No process"')
847
848
849
850
851
            return
        thread = self.currentThread()
        if not thread:
            self.report('msg="No thread"')
            return
852

853
854
        self.reportLocation(thread.GetFrameAtIndex(0)) # FIXME

855
        isNativeMixed = int(args.get('nativeMixed', 0))
856

857
858
        limit = args.get('stacklimit', -1)
        (n, isLimited) = (limit, True) if limit > 0 else (thread.GetNumFrames(), False)
859
        self.currentCallContext = None
860
        result = 'stack={current-thread="%s"' % thread.GetThreadID()
861
862
863
        result += ',frames=['
        for i in xrange(n):
            frame = thread.GetFrameAtIndex(i)
864
865
866
            if not frame.IsValid():
                isLimited = False
                break
867

868
            lineEntry = frame.GetLineEntry()
869
870
871
872
873
874
875
876
877
878
            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

879
            if isNativeMixed:
880
                if self.isReportableQmlFrame(functionName):
881
882
883
884
885
886
887
888
889
                    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'
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907

                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
908
909
910
911
        result += ']'
        result += ',hasmore="%d"' % isLimited
        result += ',limit="%d"' % limit
        result += '}'
912
        self.report(result)
913
914
        self.reportContinuation(args)

915
916
917
918
919
920
    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"])

921
    def reportContinuation(self, args):
922
        if not self.isShuttingDown_ and "continuation" in args:
923
            self.report('continuation=\"%s\"' % args["continuation"])
924

hjk's avatar
hjk committed
925
    def extractBlob(self, base, size):
hjk's avatar
hjk committed
926
        if size == 0:
hjk's avatar
hjk committed
927
            return Blob("")
928
929
        base = int(base) & 0xFFFFFFFFFFFFFFFF
        size = int(size) & 0xFFFFFFFF
hjk's avatar
hjk committed
930
        error = lldb.SBError()
hjk's avatar
hjk committed
931
        return Blob(self.process.ReadMemory(base, size, error))
hjk's avatar
hjk committed
932

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

943
944
945
946
947
948
949
    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)

950
951
    def findSymbol(self, symbolName):
        return self.target.FindFirstGlobalVariable(symbolName)
hjk's avatar
hjk committed
952

953
    def stripNamespaceFromType(self, typeName):
954
        #type = self.stripClassTag(typeName)
955
        type = typeName
956
957
958
        ns = self.qtNamespace()
        if len(ns) > 0 and type.startswith(ns):
            type = type[len(ns):]
959
960
961
962
963
964
        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
965
966
967
968
        if type.startswith("const "):
            type = type[6:]
        if type.startswith("volatile "):
            type = type[9:]
969
970
971
        return type

    def putSubItem(self, component, value, tryDynamic=True):
972
        if not value.IsValid():
973
            self.warn("INVALID SUBITEM: %s" % value.GetName())
974
            return
975
976
977
        with SubItem(self, component):
            self.putItem(value, tryDynamic)

hjk's avatar
hjk committed
978
    def putAddress(self, addr):
979
980
981
982
        #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
983

hjk's avatar
hjk committed
984
985
986
987
988
    def isFunctionType(self, typeobj):
        if self.isGoodLldb:
            return typeobj.IsFunctionType()
        #warn("TYPE: %s" % typeobj)
        return False
hjk's avatar
hjk committed
989

990
    def putItem(self, value, tryDynamic=True):
991
        typeName = value.GetType().GetUnqualifiedType().GetName()
hjk's avatar
hjk committed
992
993
        if self.isGoodLldb:
            value.SetPreferDynamicValue(tryDynamic)
hjk's avatar
hjk committed
994
        typeClass = value.GetType().GetTypeClass()
995

hjk's avatar
hjk committed
996
        if tryDynamic:
997
            self.putAddress(value.GetLoadAddress())
hjk's avatar
hjk committed
998

999
        # Handle build-in LLDB visualizers if wanted.
1000
        if False and self.useLldbDumpers and value.GetTypeSynthetic().IsValid():
1001
1002
1003
1004
            # FIXME: print "official" summary?
            summary