2016-01-15 14:53:55 +01:00
|
|
|
# Copyright (C) 2016 The Qt Company Ltd.
|
2023-01-04 08:52:22 +01:00
|
|
|
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2013-04-11 18:11:54 +02:00
|
|
|
|
2013-09-11 21:35:39 +02:00
|
|
|
try:
|
|
|
|
import __builtin__
|
|
|
|
except:
|
|
|
|
import builtins
|
|
|
|
|
2016-09-20 12:07:46 +02:00
|
|
|
import gdb
|
2013-04-11 18:11:54 +02:00
|
|
|
import os
|
2013-05-15 15:42:55 +02:00
|
|
|
import os.path
|
2018-01-22 12:41:49 +01:00
|
|
|
import re
|
2013-04-12 16:58:25 +02:00
|
|
|
import sys
|
2013-09-11 21:35:39 +02:00
|
|
|
import struct
|
2016-12-13 09:46:51 +01:00
|
|
|
import tempfile
|
2013-09-11 21:35:39 +02:00
|
|
|
|
2021-09-23 12:31:49 +00:00
|
|
|
from dumper import DumperBase, Children, toInteger, TopLevelItem
|
2020-02-21 10:10:00 +01:00
|
|
|
from utils import TypeCode
|
2021-01-07 13:28:05 +01:00
|
|
|
from gdbtracepoint import *
|
|
|
|
|
2013-04-10 13:55:15 +02:00
|
|
|
|
|
|
|
#######################################################################
|
|
|
|
#
|
|
|
|
# Infrastructure
|
|
|
|
#
|
|
|
|
#######################################################################
|
|
|
|
|
2020-02-24 14:37:56 +01:00
|
|
|
|
2015-06-25 09:10:25 +02:00
|
|
|
def safePrint(output):
|
2013-04-10 13:55:15 +02:00
|
|
|
try:
|
|
|
|
print(output)
|
|
|
|
except:
|
2016-11-01 09:03:45 +01:00
|
|
|
out = ''
|
2013-04-10 13:55:15 +02:00
|
|
|
for c in output:
|
|
|
|
cc = ord(c)
|
|
|
|
if cc > 127:
|
2016-11-01 09:03:45 +01:00
|
|
|
out += '\\\\%d' % cc
|
2013-04-10 13:55:15 +02:00
|
|
|
elif cc < 0:
|
2016-11-01 09:03:45 +01:00
|
|
|
out += '\\\\%d' % (cc + 256)
|
2013-04-10 13:55:15 +02:00
|
|
|
else:
|
|
|
|
out += c
|
|
|
|
print(out)
|
|
|
|
|
2020-02-24 14:37:56 +01:00
|
|
|
|
2013-04-10 13:55:15 +02:00
|
|
|
def registerCommand(name, func):
|
|
|
|
|
|
|
|
class Command(gdb.Command):
|
|
|
|
def __init__(self):
|
|
|
|
super(Command, self).__init__(name, gdb.COMMAND_OBSCURE)
|
2020-02-24 14:37:56 +01:00
|
|
|
|
2013-04-10 13:55:15 +02:00
|
|
|
def invoke(self, args, from_tty):
|
2015-06-25 09:10:25 +02:00
|
|
|
safePrint(func(args))
|
2013-04-10 13:55:15 +02:00
|
|
|
|
|
|
|
Command()
|
|
|
|
|
|
|
|
|
|
|
|
#######################################################################
|
|
|
|
#
|
|
|
|
# Convenience
|
|
|
|
#
|
|
|
|
#######################################################################
|
|
|
|
|
2019-03-19 15:09:37 +01:00
|
|
|
# For CLI dumper use, see README.txt
|
2013-04-10 13:55:15 +02:00
|
|
|
class PPCommand(gdb.Command):
|
|
|
|
def __init__(self):
|
2016-11-01 09:03:45 +01:00
|
|
|
super(PPCommand, self).__init__('pp', gdb.COMMAND_OBSCURE)
|
2020-02-24 14:37:56 +01:00
|
|
|
|
2013-04-10 13:55:15 +02:00
|
|
|
def invoke(self, args, from_tty):
|
2019-03-19 15:09:37 +01:00
|
|
|
print(theCliDumper.fetchVariable(args))
|
2013-04-10 13:55:15 +02:00
|
|
|
|
2020-02-24 14:37:56 +01:00
|
|
|
|
2013-04-10 13:55:15 +02:00
|
|
|
PPCommand()
|
|
|
|
|
2019-03-25 09:44:40 +01:00
|
|
|
# Just convenience for 'python print gdb.parse_and_eval(...)'
|
2020-02-24 14:37:56 +01:00
|
|
|
|
|
|
|
|
2013-04-10 13:55:15 +02:00
|
|
|
class PPPCommand(gdb.Command):
|
|
|
|
def __init__(self):
|
2016-11-01 09:03:45 +01:00
|
|
|
super(PPPCommand, self).__init__('ppp', gdb.COMMAND_OBSCURE)
|
2020-02-24 14:37:56 +01:00
|
|
|
|
2013-04-10 13:55:15 +02:00
|
|
|
def invoke(self, args, from_tty):
|
|
|
|
print(gdb.parse_and_eval(args))
|
|
|
|
|
2020-02-24 14:37:56 +01:00
|
|
|
|
2013-04-10 13:55:15 +02:00
|
|
|
PPPCommand()
|
|
|
|
|
|
|
|
|
|
|
|
def scanStack(p, n):
|
2013-09-11 21:35:39 +02:00
|
|
|
p = int(p)
|
2013-04-10 13:55:15 +02:00
|
|
|
r = []
|
2020-02-24 10:37:40 +01:00
|
|
|
for i in range(n):
|
2016-11-01 09:03:45 +01:00
|
|
|
f = gdb.parse_and_eval('{void*}%s' % p)
|
|
|
|
m = gdb.execute('info symbol %s' % f, to_string=True)
|
|
|
|
if not m.startswith('No symbol matches'):
|
2013-04-10 13:55:15 +02:00
|
|
|
r.append(m)
|
|
|
|
p += f.type.sizeof
|
|
|
|
return r
|
|
|
|
|
2020-02-24 14:37:56 +01:00
|
|
|
|
2013-04-10 13:55:15 +02:00
|
|
|
class ScanStackCommand(gdb.Command):
|
|
|
|
def __init__(self):
|
2016-11-01 09:03:45 +01:00
|
|
|
super(ScanStackCommand, self).__init__('scanStack', gdb.COMMAND_OBSCURE)
|
2020-02-24 14:37:56 +01:00
|
|
|
|
2013-04-10 13:55:15 +02:00
|
|
|
def invoke(self, args, from_tty):
|
|
|
|
if len(args) == 0:
|
|
|
|
args = 20
|
2016-11-01 09:03:45 +01:00
|
|
|
safePrint(scanStack(gdb.parse_and_eval('$sp'), int(args)))
|
2013-04-10 13:55:15 +02:00
|
|
|
|
2020-02-24 14:37:56 +01:00
|
|
|
|
2013-04-10 13:55:15 +02:00
|
|
|
ScanStackCommand()
|
|
|
|
|
2013-04-11 18:11:54 +02:00
|
|
|
|
2013-04-12 16:58:25 +02:00
|
|
|
#######################################################################
|
|
|
|
#
|
|
|
|
# Import plain gdb pretty printers
|
|
|
|
#
|
|
|
|
#######################################################################
|
|
|
|
|
2020-02-24 14:37:56 +01:00
|
|
|
class PlainDumper():
|
2013-04-12 16:58:25 +02:00
|
|
|
def __init__(self, printer):
|
|
|
|
self.printer = printer
|
2015-07-10 10:09:34 +02:00
|
|
|
self.typeCache = {}
|
2013-04-12 16:58:25 +02:00
|
|
|
|
|
|
|
def __call__(self, d, value):
|
2021-07-23 12:12:48 +02:00
|
|
|
if value.nativeValue is None:
|
|
|
|
# warn('PlainDumper(gdb): value.nativeValue is missing (%s)'%value)
|
|
|
|
nativeType = theDumper.lookupNativeType(value.type.name)
|
|
|
|
nativeTypePointer = nativeType.pointer()
|
|
|
|
nativePointer = gdb.Value(value.laddress)
|
|
|
|
value.nativeValue = nativePointer.cast(nativeTypePointer).dereference()
|
2016-08-22 13:45:15 +02:00
|
|
|
try:
|
2018-07-13 17:10:41 +02:00
|
|
|
printer = self.printer.gen_printer(value.nativeValue)
|
2016-08-22 13:45:15 +02:00
|
|
|
except:
|
2018-07-13 17:10:41 +02:00
|
|
|
printer = self.printer.invoke(value.nativeValue)
|
2018-08-25 00:53:15 +02:00
|
|
|
d.putType(value.nativeValue.type.name)
|
2015-01-14 15:00:09 +01:00
|
|
|
val = printer.to_string()
|
|
|
|
if isinstance(val, str):
|
2019-03-15 12:45:15 +01:00
|
|
|
# encode and avoid extra quotes ('"') at beginning and end
|
|
|
|
d.putValue(d.hexencode(val), 'utf8:1:0')
|
2016-03-23 08:53:30 +01:00
|
|
|
elif sys.version_info[0] <= 2 and isinstance(val, unicode):
|
|
|
|
d.putValue(val)
|
2020-02-24 14:37:56 +01:00
|
|
|
elif val is not None: # Assuming LazyString
|
2018-08-14 14:02:00 +02:00
|
|
|
d.putCharArrayValue(val.address, val.length,
|
|
|
|
val.type.target().sizeof)
|
2015-01-14 15:00:09 +01:00
|
|
|
|
2019-09-27 15:40:35 +02:00
|
|
|
lister = getattr(printer, 'children', None)
|
|
|
|
if lister is None:
|
|
|
|
d.putNumChild(0)
|
|
|
|
else:
|
|
|
|
if d.isExpanded():
|
|
|
|
children = lister()
|
|
|
|
with Children(d):
|
|
|
|
i = 0
|
|
|
|
for (name, child) in children:
|
|
|
|
d.putSubItem(name, d.fromNativeValue(child))
|
|
|
|
i += 1
|
|
|
|
if i > 1000:
|
|
|
|
break
|
|
|
|
d.putNumChild(1)
|
2013-04-12 16:58:25 +02:00
|
|
|
|
2020-02-24 14:37:56 +01:00
|
|
|
|
2013-04-12 16:58:25 +02:00
|
|
|
def importPlainDumpers(args):
|
2016-11-01 09:03:45 +01:00
|
|
|
if args == 'off':
|
Debugger: Import GDB pretty printers for new objfiles
Previously, if enabled in the configuration, system
GDB pretty printers were loaded only once for all
objfiles present at the point in time when the loading
happened, which meant that GDB pretty printers for
objfiles loaded later were not taken into account
and thus unavailable if they were defined in the
corresponding autoload scripts for the objfiles.
In order to make use of those as well, remember whether
loading of system GDB pretty printer is enabled, and if so,
evaluate the pretty printers set at the new objfiles
in the handler for GDB's new_objfile event.
Extract the functionality for handling one objfile's
pretty_printers to a separate function
'importPlainDumpersForObj' to avoid code duplication.
Note: For this to actually work, it is required that the
objfile passed to the registered GDB new_objfile handler
actually has the pretty printers set at this stage.
This was only recently implemented on GDB side, in
GDB commit 2c473def12b08100e6b56261f01112db7f6aeab5
("gdb: do autoload before notifying Python side in
new_objfile event", 2021-04-27, [1]).
Therefore, this currently only works with the current
development version of GDB built from its git master
branch, not with any already released GDB versions.
(When older GDB versions are used, this will just behave
as it used to, and the corresponding GDB pretty-printers
will not be used.)
[1] https://sourceware.org/git/?p=binutils-gdb.git;a=commitdiff;h=2c473def12b08100e6b56261f01112db7f6aeab5
Fixes: QTCREATORBUG-25339
Change-Id: Ibc0ab16fbb75184fa199c0709bfc73954f04c193
Reviewed-by: hjk <hjk@qt.io>
2021-05-04 15:01:15 +02:00
|
|
|
theDumper.usePlainDumpers = False
|
2016-05-23 13:49:47 +02:00
|
|
|
try:
|
2016-11-01 09:03:45 +01:00
|
|
|
gdb.execute('disable pretty-printer .* .*')
|
2016-05-23 13:49:47 +02:00
|
|
|
except:
|
|
|
|
# Might occur in non-ASCII directories
|
2020-02-21 10:10:00 +01:00
|
|
|
DumperBase.warn('COULD NOT DISABLE PRETTY PRINTERS')
|
2015-01-25 01:36:08 +01:00
|
|
|
else:
|
Debugger: Import GDB pretty printers for new objfiles
Previously, if enabled in the configuration, system
GDB pretty printers were loaded only once for all
objfiles present at the point in time when the loading
happened, which meant that GDB pretty printers for
objfiles loaded later were not taken into account
and thus unavailable if they were defined in the
corresponding autoload scripts for the objfiles.
In order to make use of those as well, remember whether
loading of system GDB pretty printer is enabled, and if so,
evaluate the pretty printers set at the new objfiles
in the handler for GDB's new_objfile event.
Extract the functionality for handling one objfile's
pretty_printers to a separate function
'importPlainDumpersForObj' to avoid code duplication.
Note: For this to actually work, it is required that the
objfile passed to the registered GDB new_objfile handler
actually has the pretty printers set at this stage.
This was only recently implemented on GDB side, in
GDB commit 2c473def12b08100e6b56261f01112db7f6aeab5
("gdb: do autoload before notifying Python side in
new_objfile event", 2021-04-27, [1]).
Therefore, this currently only works with the current
development version of GDB built from its git master
branch, not with any already released GDB versions.
(When older GDB versions are used, this will just behave
as it used to, and the corresponding GDB pretty-printers
will not be used.)
[1] https://sourceware.org/git/?p=binutils-gdb.git;a=commitdiff;h=2c473def12b08100e6b56261f01112db7f6aeab5
Fixes: QTCREATORBUG-25339
Change-Id: Ibc0ab16fbb75184fa199c0709bfc73954f04c193
Reviewed-by: hjk <hjk@qt.io>
2021-05-04 15:01:15 +02:00
|
|
|
theDumper.usePlainDumpers = True
|
2015-01-25 01:36:08 +01:00
|
|
|
theDumper.importPlainDumpers()
|
2013-04-12 16:58:25 +02:00
|
|
|
|
|
|
|
|
2020-02-24 14:37:56 +01:00
|
|
|
registerCommand('importPlainDumpers', importPlainDumpers)
|
2013-04-12 16:58:25 +02:00
|
|
|
|
2013-05-15 15:42:55 +02:00
|
|
|
|
|
|
|
#######################################################################
|
|
|
|
#
|
|
|
|
# The Dumper Class
|
|
|
|
#
|
|
|
|
#######################################################################
|
|
|
|
|
|
|
|
|
2013-09-11 21:35:39 +02:00
|
|
|
class Dumper(DumperBase):
|
|
|
|
|
2013-10-30 12:38:29 +01:00
|
|
|
def __init__(self):
|
2013-09-11 21:35:39 +02:00
|
|
|
DumperBase.__init__(self)
|
|
|
|
|
Debugger: Import GDB pretty printers for new objfiles
Previously, if enabled in the configuration, system
GDB pretty printers were loaded only once for all
objfiles present at the point in time when the loading
happened, which meant that GDB pretty printers for
objfiles loaded later were not taken into account
and thus unavailable if they were defined in the
corresponding autoload scripts for the objfiles.
In order to make use of those as well, remember whether
loading of system GDB pretty printer is enabled, and if so,
evaluate the pretty printers set at the new objfiles
in the handler for GDB's new_objfile event.
Extract the functionality for handling one objfile's
pretty_printers to a separate function
'importPlainDumpersForObj' to avoid code duplication.
Note: For this to actually work, it is required that the
objfile passed to the registered GDB new_objfile handler
actually has the pretty printers set at this stage.
This was only recently implemented on GDB side, in
GDB commit 2c473def12b08100e6b56261f01112db7f6aeab5
("gdb: do autoload before notifying Python side in
new_objfile event", 2021-04-27, [1]).
Therefore, this currently only works with the current
development version of GDB built from its git master
branch, not with any already released GDB versions.
(When older GDB versions are used, this will just behave
as it used to, and the corresponding GDB pretty-printers
will not be used.)
[1] https://sourceware.org/git/?p=binutils-gdb.git;a=commitdiff;h=2c473def12b08100e6b56261f01112db7f6aeab5
Fixes: QTCREATORBUG-25339
Change-Id: Ibc0ab16fbb75184fa199c0709bfc73954f04c193
Reviewed-by: hjk <hjk@qt.io>
2021-05-04 15:01:15 +02:00
|
|
|
# whether to load plain dumpers for objfiles
|
|
|
|
self.usePlainDumpers = False
|
|
|
|
|
2015-10-08 16:19:57 +02:00
|
|
|
# These values will be kept between calls to 'fetchVariables'.
|
2013-09-11 21:35:39 +02:00
|
|
|
self.isGdb = True
|
2015-07-10 10:09:34 +02:00
|
|
|
self.typeCache = {}
|
2015-10-09 15:00:20 +02:00
|
|
|
self.interpreterBreakpointResolvers = []
|
2013-10-30 12:38:29 +01:00
|
|
|
|
Debugger: Make dumpers somewhat work in command line GDB
With
python sys.path.insert(1, '/data/dev/creator/share/qtcreator/debugger/')
python from gdbbridge import *
in .gdbinit there's a new "GDB command", called "pp".
With code like
int main(int argc, char *argv[])
{
QString ss = "Hello";
QApplication app(argc, argv);
app.setObjectName(ss);
// break here
}
the 'pp' command can be used as follows:
(gdb) pp app
app =
[
<Myns::QGuiApplication> = {"Hello"}
staticMetaObject = <Myns::QMetaObject> = {""}
[parent] = <Myns::QObject *> = {"0x0"}
[children] = <Myns::QObjectList> = {"<3 items>"}
[properties] = "<>0 items>"
[methods] = "<6 items>"
[signals] = "<1 items>"
],<Myns::QApplication> = {"Hello"}
(gdb) pp app [properties],[children]
app =
[
<Myns::QGuiApplication> = {"Hello"}
staticMetaObject = <Myns::QMetaObject> = {""}
[parent] = <Myns::QObject *> = {"0x0"}
[children] = [
<Myns::QObject> = {""}
<Myns::QObject> = {""}
<Myns::QObject> = {"fusion"}
],<Myns::QObjectList> = {"<3 items>"}
[properties] = [
windowIcon = <Myns::QVariant (QIcon)> = {""}
cursorFlashTime = <Myns::QVariant (int)> = {"1000"}
doubleClickInterval = <Myns::QVariant (int)> = {"400"}
keyboardInputInterval = <Myns::QVariant (int)> = {"400"}
wheelScrollLines = <Myns::QVariant (int)> = {"3"}
globalStrut = <Myns::QVariant (QSize)> = {"(0, 0)"}
startDragTime = <Myns::QVariant (int)> = {"500"}
startDragDistance = <Myns::QVariant (int)> = {"10"}
styleSheet = <Myns::QVariant (QString)> = {""}
autoSipEnabled = <Myns::QVariant (bool)> = {"true"}
],"<10 items>"
[methods] = "<6 items>"
[signals] = "<1 items>"
],<Myns::QApplication> = {"Hello"}
(gdb) pp ss
ss =
<Myns::QString> = {"Hello"}
Change-Id: I6e4714a5cfe34c38917500d114ad9a70d20cff39
Reviewed-by: Christian Stenger <christian.stenger@digia.com>
Reviewed-by: hjk <hjk121@nokiamail.com>
2014-06-13 17:45:34 +02:00
|
|
|
def prepare(self, args):
|
2022-11-08 12:42:04 +01:00
|
|
|
self.output = []
|
2016-10-07 12:23:35 +02:00
|
|
|
self.setVariableFetchingOptions(args)
|
2016-07-05 16:52:32 +02:00
|
|
|
|
2016-10-25 15:32:13 +02:00
|
|
|
def fromFrameValue(self, nativeValue):
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('FROM FRAME VALUE: %s' % nativeValue.address)
|
2016-10-25 15:32:13 +02:00
|
|
|
val = nativeValue
|
2020-06-29 09:34:57 +03:00
|
|
|
if self.useDynamicType:
|
|
|
|
try:
|
|
|
|
val = nativeValue.cast(nativeValue.dynamic_type)
|
|
|
|
except:
|
|
|
|
pass
|
2016-10-25 15:32:13 +02:00
|
|
|
return self.fromNativeValue(val)
|
2016-09-06 08:54:43 +02:00
|
|
|
|
2021-01-19 16:02:48 +01:00
|
|
|
def nativeValueType(self, nativeValue):
|
|
|
|
return self.fromNativeType(nativeValue.type)
|
|
|
|
|
2016-09-06 08:54:43 +02:00
|
|
|
def fromNativeValue(self, nativeValue):
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('FROM NATIVE VALUE: %s' % nativeValue)
|
2016-10-25 15:32:13 +02:00
|
|
|
self.check(isinstance(nativeValue, gdb.Value))
|
2016-09-26 14:29:16 +02:00
|
|
|
nativeType = nativeValue.type
|
2016-10-25 15:32:13 +02:00
|
|
|
code = nativeType.code
|
|
|
|
if code == gdb.TYPE_CODE_REF:
|
2017-03-08 16:37:52 +01:00
|
|
|
targetType = self.fromNativeType(nativeType.target().unqualified())
|
2021-09-23 12:31:49 +00:00
|
|
|
val = self.createReferenceValue(toInteger(nativeValue.address), targetType)
|
2017-03-08 16:37:52 +01:00
|
|
|
val.nativeValue = nativeValue
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('CREATED REF: %s' % val)
|
2016-10-25 15:32:13 +02:00
|
|
|
return val
|
|
|
|
if code == gdb.TYPE_CODE_PTR:
|
2016-11-07 08:57:26 +01:00
|
|
|
try:
|
|
|
|
nativeTargetValue = nativeValue.dereference()
|
|
|
|
except:
|
|
|
|
nativeTargetValue = None
|
2017-03-08 16:37:52 +01:00
|
|
|
targetType = self.fromNativeType(nativeType.target().unqualified())
|
2021-09-23 12:31:49 +00:00
|
|
|
val = self.createPointerValue(toInteger(nativeValue), targetType)
|
2019-12-18 13:27:20 +01:00
|
|
|
# The nativeValue is needed in case of multiple inheritance, see
|
|
|
|
# QTCREATORBUG-17823. Using it triggers nativeValueDereferencePointer()
|
|
|
|
# later which
|
|
|
|
# is surprisingly expensive.
|
2017-03-08 16:37:52 +01:00
|
|
|
val.nativeValue = nativeValue
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('CREATED PTR 1: %s' % val)
|
2020-02-24 14:37:56 +01:00
|
|
|
if nativeValue.address is not None:
|
2021-09-23 12:31:49 +00:00
|
|
|
val.laddress = toInteger(nativeValue.address)
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('CREATED PTR 2: %s' % val)
|
2016-10-25 15:32:13 +02:00
|
|
|
return val
|
|
|
|
if code == gdb.TYPE_CODE_TYPEDEF:
|
|
|
|
targetType = nativeType.strip_typedefs().unqualified()
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('TARGET TYPE: %s' % targetType)
|
2016-10-25 15:32:13 +02:00
|
|
|
if targetType.code == gdb.TYPE_CODE_ARRAY:
|
|
|
|
val = self.Value(self)
|
|
|
|
else:
|
2018-12-17 12:27:02 +01:00
|
|
|
try:
|
|
|
|
# Cast may fail for arrays, for typedefs to __uint128_t with
|
|
|
|
# gdb.error: That operation is not available on integers
|
|
|
|
# of more than 8 bytes.
|
|
|
|
# See test for Bug5799, QTCREATORBUG-18450.
|
|
|
|
val = self.fromNativeValue(nativeValue.cast(targetType))
|
|
|
|
except:
|
|
|
|
val = self.Value(self)
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('CREATED TYPEDEF: %s' % val)
|
2018-11-30 01:25:17 +02:00
|
|
|
else:
|
|
|
|
val = self.Value(self)
|
2016-10-25 15:32:13 +02:00
|
|
|
|
2017-03-08 16:37:52 +01:00
|
|
|
val.nativeValue = nativeValue
|
2020-02-24 14:37:56 +01:00
|
|
|
if nativeValue.address is not None:
|
2021-09-23 12:31:49 +00:00
|
|
|
val.laddress = toInteger(nativeValue.address)
|
2016-09-26 14:29:16 +02:00
|
|
|
else:
|
|
|
|
size = nativeType.sizeof
|
2016-11-01 09:03:45 +01:00
|
|
|
chars = self.lookupNativeType('unsigned char')
|
2016-09-26 14:29:16 +02:00
|
|
|
y = nativeValue.cast(chars.array(0, int(nativeType.sizeof - 1)))
|
|
|
|
buf = bytearray(struct.pack('x' * size))
|
|
|
|
for i in range(size):
|
2017-04-24 18:28:54 +02:00
|
|
|
try:
|
|
|
|
buf[i] = int(y[i])
|
|
|
|
except:
|
|
|
|
pass
|
2016-09-26 14:29:16 +02:00
|
|
|
val.ldata = bytes(buf)
|
|
|
|
|
2021-01-19 16:02:48 +01:00
|
|
|
val._type = self.fromNativeType(nativeType)
|
2016-09-06 08:54:43 +02:00
|
|
|
val.lIsInScope = not nativeValue.is_optimized_out
|
2016-09-26 14:29:16 +02:00
|
|
|
code = nativeType.code
|
|
|
|
if code == gdb.TYPE_CODE_ENUM:
|
|
|
|
val.ldisplay = str(nativeValue)
|
|
|
|
intval = int(nativeValue)
|
|
|
|
if val.ldisplay != intval:
|
|
|
|
val.ldisplay += ' (%s)' % intval
|
|
|
|
elif code == gdb.TYPE_CODE_COMPLEX:
|
|
|
|
val.ldisplay = str(nativeValue)
|
Debugger: Retrieve and remember int from native GDB value
When adding expressions for bitfield members in the
debugger's expression view, their corresponding 'gdb.Value'
does not expose the fact that those are actually bitfields,
so e.g. an 'int : 3' is exposed like a "normal" 'int'.
Previously, this would result in wrong values being
retrieved in the 'DumperBase::Value::integer()' function,
when trying to read the value from the corresponding
memory address.
To avoid this, retrieve the actual int representation
for numeric values from the corresponding native 'gdb.Value',
remember them and return that one, similar to how it
is already done for known bitfield members
(s. 'Dumper::memberFromNativeFieldAndValue').
The conversion from the 'gdb.Value' does not work
for integers of a size larger than 64 bits
(like '__int128' used in the "Int128" dumper test).
Therefore, just ignore conversion failures and don't
remember any value explicitly for those cases,
so the same handling as previously used is applied.
(At a quick glance, the reason seems to be that this
is because GDB's corresponding functions use 'int64'
as a return value of the relevant functions [1] [2],
but I did not look closer into what GDB does
internally.)
Corresponding tests will be added in a separate commit.
[1] https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdbsupport/common-types.h;h=f5b2f3d249177acea77231c21c5601f959c18d2f;hb=f3034e25fa98d44b775970f40c9ec85eeae096e6#l33
[2] https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/python/py-value.c;h=6e29284aad11ff344789152a4f601b3474d86bb5;hb=f3034e25fa98d44b775970f40c9ec85eeae096e6#l1706
Fixes: QTCREATORBUG-24693
Change-Id: Idfc3390115e8796f3c778070c23424c3dbdfeddd
Reviewed-by: hjk <hjk@qt.io>
2020-09-24 12:02:06 +02:00
|
|
|
elif code in [gdb.TYPE_CODE_BOOL, gdb.TYPE_CODE_INT]:
|
|
|
|
try:
|
|
|
|
# extract int presentation from native value and remember it
|
|
|
|
val.lvalue = int(nativeValue)
|
|
|
|
except:
|
|
|
|
# GDB only support converting integers of max. 64 bits to Python int as of now
|
|
|
|
pass
|
2016-10-25 15:32:13 +02:00
|
|
|
#elif code == gdb.TYPE_CODE_ARRAY:
|
|
|
|
# val.type.ltarget = nativeValue[0].type.unqualified()
|
2016-09-06 08:54:43 +02:00
|
|
|
return val
|
|
|
|
|
2016-10-25 15:32:13 +02:00
|
|
|
def ptrSize(self):
|
|
|
|
result = gdb.lookup_type('void').pointer().sizeof
|
|
|
|
self.ptrSize = lambda: result
|
|
|
|
return result
|
|
|
|
|
2017-03-08 16:37:52 +01:00
|
|
|
def fromNativeType(self, nativeType):
|
2016-09-06 08:54:43 +02:00
|
|
|
self.check(isinstance(nativeType, gdb.Type))
|
2016-10-25 15:32:13 +02:00
|
|
|
code = nativeType.code
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('FROM NATIVE TYPE: %s' % nativeType)
|
2016-11-02 16:32:28 +01:00
|
|
|
nativeType = nativeType.unqualified()
|
2016-10-25 15:32:13 +02:00
|
|
|
|
|
|
|
if code == gdb.TYPE_CODE_PTR:
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('PTR')
|
2017-03-08 16:37:52 +01:00
|
|
|
targetType = self.fromNativeType(nativeType.target().unqualified())
|
2016-10-25 15:32:13 +02:00
|
|
|
return self.createPointerType(targetType)
|
|
|
|
|
|
|
|
if code == gdb.TYPE_CODE_REF:
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('REF')
|
2016-10-25 15:32:13 +02:00
|
|
|
targetType = self.fromNativeType(nativeType.target().unqualified())
|
|
|
|
return self.createReferenceType(targetType)
|
|
|
|
|
2019-01-21 11:57:42 +01:00
|
|
|
if hasattr(gdb, "TYPE_CODE_RVALUE_REF"):
|
|
|
|
if code == gdb.TYPE_CODE_RVALUE_REF:
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('RVALUEREF')
|
2019-01-21 11:57:42 +01:00
|
|
|
targetType = self.fromNativeType(nativeType.target())
|
|
|
|
return self.createRValueReferenceType(targetType)
|
2019-01-08 16:18:25 +01:00
|
|
|
|
2016-10-25 15:32:13 +02:00
|
|
|
if code == gdb.TYPE_CODE_ARRAY:
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('ARRAY')
|
2016-10-25 15:32:13 +02:00
|
|
|
nativeTargetType = nativeType.target().unqualified()
|
|
|
|
targetType = self.fromNativeType(nativeTargetType)
|
2020-05-07 09:56:52 +02:00
|
|
|
if nativeType.sizeof == 0:
|
|
|
|
# QTCREATORBUG-23998, note that nativeType.name == None here,
|
|
|
|
# whereas str(nativeType) returns sth like 'QObject [5]'
|
|
|
|
count = self.arrayItemCountFromTypeName(str(nativeType), 1)
|
|
|
|
else:
|
|
|
|
count = nativeType.sizeof // nativeTargetType.sizeof
|
2016-10-25 15:32:13 +02:00
|
|
|
return self.createArrayType(targetType, count)
|
|
|
|
|
|
|
|
if code == gdb.TYPE_CODE_TYPEDEF:
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('TYPEDEF')
|
2016-10-25 15:32:13 +02:00
|
|
|
nativeTargetType = nativeType.unqualified()
|
|
|
|
while nativeTargetType.code == gdb.TYPE_CODE_TYPEDEF:
|
|
|
|
nativeTargetType = nativeTargetType.strip_typedefs().unqualified()
|
|
|
|
targetType = self.fromNativeType(nativeTargetType)
|
2017-04-20 08:47:34 +02:00
|
|
|
return self.createTypedefedType(targetType, str(nativeType),
|
|
|
|
self.nativeTypeId(nativeType))
|
2016-10-25 15:32:13 +02:00
|
|
|
|
|
|
|
if code == gdb.TYPE_CODE_ERROR:
|
2020-02-21 10:10:00 +01:00
|
|
|
self.warn('Type error: %s' % nativeType)
|
2016-10-25 15:32:13 +02:00
|
|
|
return self.Type(self, '')
|
|
|
|
|
|
|
|
typeId = self.nativeTypeId(nativeType)
|
|
|
|
res = self.typeData.get(typeId, None)
|
|
|
|
if res is None:
|
2022-08-23 17:49:31 +02:00
|
|
|
tdata = self.TypeData(self, typeId)
|
2016-10-25 15:32:13 +02:00
|
|
|
tdata.name = str(nativeType)
|
|
|
|
tdata.lbitsize = nativeType.sizeof * 8
|
|
|
|
tdata.code = {
|
2020-02-24 15:10:47 +01:00
|
|
|
#gdb.TYPE_CODE_TYPEDEF : TypeCode.Typedef, # Handled above.
|
|
|
|
gdb.TYPE_CODE_METHOD: TypeCode.Function,
|
|
|
|
gdb.TYPE_CODE_VOID: TypeCode.Void,
|
|
|
|
gdb.TYPE_CODE_FUNC: TypeCode.Function,
|
|
|
|
gdb.TYPE_CODE_METHODPTR: TypeCode.Function,
|
|
|
|
gdb.TYPE_CODE_MEMBERPTR: TypeCode.Function,
|
|
|
|
#gdb.TYPE_CODE_PTR : TypeCode.Pointer, # Handled above.
|
|
|
|
#gdb.TYPE_CODE_REF : TypeCode.Reference, # Handled above.
|
|
|
|
gdb.TYPE_CODE_BOOL: TypeCode.Integral,
|
|
|
|
gdb.TYPE_CODE_CHAR: TypeCode.Integral,
|
|
|
|
gdb.TYPE_CODE_INT: TypeCode.Integral,
|
|
|
|
gdb.TYPE_CODE_FLT: TypeCode.Float,
|
|
|
|
gdb.TYPE_CODE_ENUM: TypeCode.Enum,
|
|
|
|
#gdb.TYPE_CODE_ARRAY : TypeCode.Array,
|
|
|
|
gdb.TYPE_CODE_STRUCT: TypeCode.Struct,
|
|
|
|
gdb.TYPE_CODE_UNION: TypeCode.Struct,
|
|
|
|
gdb.TYPE_CODE_COMPLEX: TypeCode.Complex,
|
|
|
|
gdb.TYPE_CODE_STRING: TypeCode.FortranString,
|
2016-10-25 15:32:13 +02:00
|
|
|
}[code]
|
2020-02-24 15:10:47 +01:00
|
|
|
if tdata.code == TypeCode.Enum:
|
2020-02-24 14:37:56 +01:00
|
|
|
tdata.enumDisplay = lambda intval, addr, form: \
|
2018-03-21 17:10:24 +01:00
|
|
|
self.nativeTypeEnumDisplay(nativeType, intval, form)
|
2020-02-24 15:10:47 +01:00
|
|
|
if tdata.code == TypeCode.Struct:
|
2020-02-24 14:37:56 +01:00
|
|
|
tdata.lalignment = lambda: \
|
2016-11-01 09:50:31 +01:00
|
|
|
self.nativeStructAlignment(nativeType)
|
2020-02-24 14:37:56 +01:00
|
|
|
tdata.lfields = lambda value: \
|
2017-03-08 16:37:52 +01:00
|
|
|
self.listMembers(value, nativeType)
|
2022-08-12 12:34:32 +02:00
|
|
|
tdata.templateArguments = lambda: \
|
|
|
|
self.listTemplateParameters(nativeType)
|
2016-10-25 15:32:13 +02:00
|
|
|
# warn('CREATE TYPE: %s' % typeId)
|
|
|
|
#else:
|
|
|
|
# warn('REUSE TYPE: %s' % typeId)
|
|
|
|
return self.Type(self, typeId)
|
|
|
|
|
|
|
|
def listTemplateParameters(self, nativeType):
|
|
|
|
targs = []
|
|
|
|
pos = 0
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
targ = nativeType.template_argument(pos)
|
|
|
|
except:
|
|
|
|
break
|
|
|
|
if isinstance(targ, gdb.Type):
|
|
|
|
targs.append(self.fromNativeType(targ.unqualified()))
|
|
|
|
elif isinstance(targ, gdb.Value):
|
|
|
|
targs.append(self.fromNativeValue(targ).value())
|
|
|
|
else:
|
2020-02-21 10:10:00 +01:00
|
|
|
raise RuntimeError('UNKNOWN TEMPLATE PARAMETER')
|
2016-10-25 15:32:13 +02:00
|
|
|
pos += 1
|
2017-04-25 18:01:26 +02:00
|
|
|
targs2 = self.listTemplateParametersManually(str(nativeType))
|
|
|
|
return targs if len(targs) >= len(targs2) else targs2
|
2016-09-06 08:54:43 +02:00
|
|
|
|
2018-03-21 17:10:24 +01:00
|
|
|
def nativeTypeEnumDisplay(self, nativeType, intval, form):
|
2016-09-15 12:31:28 +02:00
|
|
|
try:
|
2017-02-11 23:45:33 +02:00
|
|
|
enumerators = []
|
2017-02-08 16:09:39 +02:00
|
|
|
for field in nativeType.fields():
|
2017-02-11 23:45:33 +02:00
|
|
|
# If we found an exact match, return it immediately
|
2017-02-08 16:09:39 +02:00
|
|
|
if field.enumval == intval:
|
2018-03-21 17:10:24 +01:00
|
|
|
return field.name + ' (' + (form % intval) + ')'
|
2017-02-11 23:45:33 +02:00
|
|
|
enumerators.append((field.name, field.enumval))
|
|
|
|
|
|
|
|
# No match was found, try to return as flags
|
2020-02-24 14:37:56 +01:00
|
|
|
enumerators.sort(key=lambda x: x[1])
|
2017-02-11 23:45:33 +02:00
|
|
|
flags = []
|
|
|
|
v = intval
|
|
|
|
found = False
|
|
|
|
for (name, value) in enumerators:
|
|
|
|
if v & value != 0:
|
|
|
|
flags.append(name)
|
|
|
|
v = v & ~value
|
|
|
|
found = True
|
|
|
|
if not found or v != 0:
|
|
|
|
# Leftover value
|
2018-09-06 12:34:21 +02:00
|
|
|
flags.append('unknown: %d' % v)
|
2018-11-15 09:04:39 +01:00
|
|
|
return '(' + " | ".join(flags) + ') (' + (form % intval) + ')'
|
2016-09-15 12:31:28 +02:00
|
|
|
except:
|
2017-02-08 16:09:39 +02:00
|
|
|
pass
|
2018-03-21 17:10:24 +01:00
|
|
|
return form % intval
|
2016-10-25 15:32:13 +02:00
|
|
|
|
|
|
|
def nativeTypeId(self, nativeType):
|
2017-05-28 08:49:44 +03:00
|
|
|
if nativeType and (nativeType.code == gdb.TYPE_CODE_TYPEDEF):
|
2017-04-20 08:47:34 +02:00
|
|
|
return '%s{%s}' % (nativeType, nativeType.strip_typedefs())
|
2016-10-25 15:32:13 +02:00
|
|
|
name = str(nativeType)
|
|
|
|
if len(name) == 0:
|
|
|
|
c = '0'
|
|
|
|
elif name == 'union {...}':
|
|
|
|
c = 'u'
|
2016-11-01 09:50:31 +01:00
|
|
|
elif name.endswith('{...}'):
|
|
|
|
c = 's'
|
2016-10-25 15:32:13 +02:00
|
|
|
else:
|
|
|
|
return name
|
2016-11-01 09:50:31 +01:00
|
|
|
typeId = c + ''.join(['{%s:%s}' % (f.name, self.nativeTypeId(f.type))
|
|
|
|
for f in nativeType.fields()])
|
2016-10-25 15:32:13 +02:00
|
|
|
return typeId
|
2016-09-15 12:31:28 +02:00
|
|
|
|
2016-11-01 09:50:31 +01:00
|
|
|
def nativeStructAlignment(self, nativeType):
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('NATIVE ALIGN FOR %s' % nativeType.name)
|
2016-11-01 09:50:31 +01:00
|
|
|
def handleItem(nativeFieldType, align):
|
|
|
|
a = self.fromNativeType(nativeFieldType).alignment()
|
|
|
|
return a if a > align else align
|
|
|
|
align = 1
|
|
|
|
for f in nativeType.fields():
|
|
|
|
align = handleItem(f.type, align)
|
|
|
|
return align
|
|
|
|
|
2016-11-07 08:57:26 +01:00
|
|
|
#except:
|
|
|
|
# # Happens in the BoostList dumper for a 'const bool'
|
|
|
|
# # item named 'constant_time_size'. There isn't anything we can do
|
|
|
|
# # in this case.
|
2020-02-24 14:37:56 +01:00
|
|
|
# pass
|
2016-11-07 08:57:26 +01:00
|
|
|
|
|
|
|
#yield value.extractField(field)
|
|
|
|
|
2017-03-08 16:37:52 +01:00
|
|
|
def memberFromNativeFieldAndValue(self, nativeField, nativeValue, fieldName, value):
|
|
|
|
nativeMember = self.nativeMemberFromField(nativeValue, nativeField)
|
2017-04-25 13:03:51 +02:00
|
|
|
if nativeMember is None:
|
|
|
|
val = self.Value(self)
|
|
|
|
val.name = fieldName
|
2021-01-19 16:02:48 +01:00
|
|
|
val._type = self.fromNativeType(nativeField.type)
|
2017-04-25 13:03:51 +02:00
|
|
|
val.lIsInScope = False
|
|
|
|
return val
|
2017-03-08 16:37:52 +01:00
|
|
|
val = self.fromNativeValue(nativeMember)
|
|
|
|
nativeFieldType = nativeField.type.unqualified()
|
|
|
|
if nativeField.bitsize:
|
2017-05-29 00:34:17 +03:00
|
|
|
val.lvalue = int(nativeMember)
|
2017-03-08 16:37:52 +01:00
|
|
|
val.laddress = None
|
2017-06-02 15:04:40 +03:00
|
|
|
fieldType = self.fromNativeType(nativeFieldType)
|
2021-01-19 16:02:48 +01:00
|
|
|
val._type = self.createBitfieldType(fieldType, nativeField.bitsize)
|
2017-03-08 16:37:52 +01:00
|
|
|
val.isBaseClass = nativeField.is_base_class
|
|
|
|
val.name = fieldName
|
|
|
|
return val
|
2016-11-07 08:57:26 +01:00
|
|
|
|
2017-03-08 16:37:52 +01:00
|
|
|
def nativeMemberFromField(self, nativeValue, nativeField):
|
2016-11-07 08:57:26 +01:00
|
|
|
if nativeField.is_base_class:
|
|
|
|
return nativeValue.cast(nativeField.type)
|
|
|
|
try:
|
|
|
|
return nativeValue[nativeField]
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
try:
|
|
|
|
return nativeValue[nativeField.name]
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
return None
|
|
|
|
|
2017-03-08 16:37:52 +01:00
|
|
|
def listMembers(self, value, nativeType):
|
|
|
|
nativeValue = value.nativeValue
|
|
|
|
|
|
|
|
anonNumber = 0
|
|
|
|
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('LISTING FIELDS FOR %s' % nativeType)
|
2017-03-08 16:37:52 +01:00
|
|
|
for nativeField in nativeType.fields():
|
|
|
|
fieldName = nativeField.name
|
|
|
|
# Something without a name.
|
|
|
|
# Anonymous union? We need a dummy name to distinguish
|
|
|
|
# multiple anonymous unions in the struct.
|
|
|
|
# Since GDB commit b5b08fb4 anonymous structs get also reported
|
|
|
|
# with a 'None' name.
|
|
|
|
if fieldName is None or len(fieldName) == 0:
|
|
|
|
# Something without a name.
|
|
|
|
# Anonymous union? We need a dummy name to distinguish
|
|
|
|
# multiple anonymous unions in the struct.
|
|
|
|
anonNumber += 1
|
|
|
|
fieldName = '#%s' % anonNumber
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('FIELD: %s' % fieldName)
|
2017-07-04 18:47:56 +02:00
|
|
|
# hasattr(nativeField, 'bitpos') == False indicates a static field,
|
|
|
|
# but if we have access to a nativeValue .fromNativeField will
|
|
|
|
# also succeed. We essentially skip only static members from
|
|
|
|
# artificial values, like array members constructed from address.
|
|
|
|
if hasattr(nativeField, 'bitpos') or nativeValue is not None:
|
|
|
|
yield self.fromNativeField(nativeField, nativeValue, fieldName)
|
2017-03-08 16:37:52 +01:00
|
|
|
|
|
|
|
def fromNativeField(self, nativeField, nativeValue, fieldName):
|
|
|
|
nativeFieldType = nativeField.type.unqualified()
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn(' TYPE: %s' % nativeFieldType)
|
|
|
|
#DumperBase.warn(' TYPEID: %s' % self.nativeTypeId(nativeFieldType))
|
2017-03-08 16:37:52 +01:00
|
|
|
|
|
|
|
if hasattr(nativeField, 'bitpos'):
|
|
|
|
bitpos = nativeField.bitpos
|
|
|
|
else:
|
2017-06-05 11:09:15 +03:00
|
|
|
bitpos = 0
|
2016-09-06 08:54:43 +02:00
|
|
|
|
2017-03-08 16:37:52 +01:00
|
|
|
if hasattr(nativeField, 'bitsize') and nativeField.bitsize != 0:
|
|
|
|
bitsize = nativeField.bitsize
|
|
|
|
else:
|
|
|
|
bitsize = 8 * nativeFieldType.sizeof
|
2016-11-07 08:57:26 +01:00
|
|
|
|
2017-06-02 15:04:40 +03:00
|
|
|
fieldType = self.fromNativeType(nativeFieldType)
|
2017-03-08 16:37:52 +01:00
|
|
|
if bitsize != nativeFieldType.sizeof * 8:
|
2017-06-02 15:04:40 +03:00
|
|
|
fieldType = self.createBitfieldType(fieldType, bitsize)
|
2017-03-08 16:37:52 +01:00
|
|
|
else:
|
2017-06-02 15:04:40 +03:00
|
|
|
fieldType = fieldType
|
2016-11-07 08:57:26 +01:00
|
|
|
|
2017-03-08 16:37:52 +01:00
|
|
|
if nativeValue is None:
|
|
|
|
extractor = None
|
|
|
|
else:
|
|
|
|
extractor = lambda value, \
|
2020-02-24 14:37:56 +01:00
|
|
|
capturedNativeField = nativeField, \
|
|
|
|
capturedNativeValue = nativeValue, \
|
|
|
|
capturedFieldName = fieldName: \
|
2017-03-08 16:37:52 +01:00
|
|
|
self.memberFromNativeFieldAndValue(capturedNativeField,
|
|
|
|
capturedNativeValue,
|
|
|
|
capturedFieldName,
|
|
|
|
value)
|
2016-11-07 08:57:26 +01:00
|
|
|
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn("FOUND NATIVE FIELD: %s bitpos: %s" % (fieldName, bitpos))
|
2017-03-08 16:37:52 +01:00
|
|
|
return self.Field(dumper=self, name=fieldName, isBase=nativeField.is_base_class,
|
2020-02-24 14:37:56 +01:00
|
|
|
bitsize=bitsize, bitpos=bitpos, type=fieldType,
|
|
|
|
extractor=extractor)
|
2016-09-06 08:54:43 +02:00
|
|
|
|
2016-11-01 09:50:31 +01:00
|
|
|
def listLocals(self, partialVar):
|
2014-12-12 10:12:18 +01:00
|
|
|
frame = gdb.selected_frame()
|
|
|
|
|
|
|
|
try:
|
|
|
|
block = frame.block()
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('BLOCK: %s ' % block)
|
2014-12-12 10:12:18 +01:00
|
|
|
except RuntimeError as error:
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('BLOCK IN FRAME NOT ACCESSIBLE: %s' % error)
|
2014-12-12 10:12:18 +01:00
|
|
|
return []
|
|
|
|
except:
|
2020-02-21 10:10:00 +01:00
|
|
|
self.warn('BLOCK NOT ACCESSIBLE FOR UNKNOWN REASONS')
|
2014-12-12 10:12:18 +01:00
|
|
|
return []
|
|
|
|
|
|
|
|
items = []
|
|
|
|
shadowed = {}
|
|
|
|
while True:
|
|
|
|
if block is None:
|
2020-02-21 10:10:00 +01:00
|
|
|
self.warn("UNEXPECTED 'None' BLOCK")
|
2014-12-12 10:12:18 +01:00
|
|
|
break
|
|
|
|
for symbol in block:
|
2015-10-08 16:05:29 +02:00
|
|
|
|
2020-02-24 14:37:56 +01:00
|
|
|
# Filter out labels etc.
|
|
|
|
if symbol.is_variable or symbol.is_argument:
|
|
|
|
name = symbol.print_name
|
2014-12-12 10:12:18 +01:00
|
|
|
|
2020-02-24 14:37:56 +01:00
|
|
|
if name in ('__in_chrg', '__PRETTY_FUNCTION__'):
|
|
|
|
continue
|
2016-10-25 15:32:13 +02:00
|
|
|
|
2020-02-24 14:37:56 +01:00
|
|
|
if partialVar is not None and partialVar != name:
|
|
|
|
continue
|
2014-12-12 10:12:18 +01:00
|
|
|
|
2020-02-24 14:37:56 +01:00
|
|
|
# 'NotImplementedError: Symbol type not yet supported in
|
|
|
|
# Python scripts.'
|
|
|
|
#DumperBase.warn('SYMBOL %s (%s, %s)): ' % (symbol, name, symbol.name))
|
|
|
|
if self.passExceptions and not self.isTesting:
|
|
|
|
nativeValue = frame.read_var(name, block)
|
|
|
|
value = self.fromFrameValue(nativeValue)
|
|
|
|
value.name = name
|
|
|
|
#DumperBase.warn('READ 0: %s' % value.stringify())
|
|
|
|
items.append(value)
|
|
|
|
continue
|
2014-12-12 10:12:18 +01:00
|
|
|
|
2020-02-24 14:37:56 +01:00
|
|
|
try:
|
|
|
|
# Same as above, but for production.
|
|
|
|
nativeValue = frame.read_var(name, block)
|
|
|
|
value = self.fromFrameValue(nativeValue)
|
|
|
|
value.name = name
|
|
|
|
#DumperBase.warn('READ 1: %s' % value.stringify())
|
|
|
|
items.append(value)
|
|
|
|
continue
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
|
|
|
try:
|
|
|
|
#DumperBase.warn('READ 2: %s' % item.value)
|
|
|
|
value = self.fromFrameValue(frame.read_var(name))
|
|
|
|
value.name = name
|
|
|
|
items.append(value)
|
|
|
|
continue
|
|
|
|
except:
|
|
|
|
# RuntimeError: happens for
|
|
|
|
# void foo() { std::string s; std::wstring w; }
|
|
|
|
# ValueError: happens for (as of 2010/11/4)
|
|
|
|
# a local struct as found e.g. in
|
|
|
|
# gcc sources in gcc.c, int execute()
|
|
|
|
pass
|
|
|
|
|
|
|
|
try:
|
|
|
|
#DumperBase.warn('READ 3: %s %s' % (name, item.value))
|
|
|
|
#DumperBase.warn('ITEM 3: %s' % item.value)
|
|
|
|
value = self.fromFrameValue(gdb.parse_and_eval(name))
|
|
|
|
value.name = name
|
|
|
|
items.append(value)
|
|
|
|
except:
|
|
|
|
# Can happen in inlined code (see last line of
|
|
|
|
# RowPainter::paintChars(): 'RuntimeError:
|
|
|
|
# No symbol '__val' in current context.\n'
|
|
|
|
pass
|
2014-12-12 10:12:18 +01:00
|
|
|
|
|
|
|
# The outermost block in a function has the function member
|
|
|
|
# FIXME: check whether this is guaranteed.
|
2020-02-24 14:37:56 +01:00
|
|
|
if block.function is not None:
|
2014-12-12 10:12:18 +01:00
|
|
|
break
|
|
|
|
|
|
|
|
block = block.superblock
|
|
|
|
|
|
|
|
return items
|
|
|
|
|
2016-12-15 18:28:58 +01:00
|
|
|
def reportToken(self, args):
|
|
|
|
pass
|
|
|
|
|
2015-04-15 12:38:11 +02:00
|
|
|
# Hack to avoid QDate* dumper timeouts with GDB 7.4 on 32 bit
|
|
|
|
# due to misaligned %ebx in SSE calls (qstring.cpp:findChar)
|
|
|
|
# This seems to be fixed in 7.9 (or earlier)
|
|
|
|
def canCallLocale(self):
|
2016-09-14 14:44:58 +02:00
|
|
|
return self.ptrSize() == 8
|
2014-12-12 10:12:18 +01:00
|
|
|
|
2015-10-08 16:19:57 +02:00
|
|
|
def fetchVariables(self, args):
|
2016-07-22 10:20:01 +02:00
|
|
|
self.resetStats()
|
Debugger: Make dumpers somewhat work in command line GDB
With
python sys.path.insert(1, '/data/dev/creator/share/qtcreator/debugger/')
python from gdbbridge import *
in .gdbinit there's a new "GDB command", called "pp".
With code like
int main(int argc, char *argv[])
{
QString ss = "Hello";
QApplication app(argc, argv);
app.setObjectName(ss);
// break here
}
the 'pp' command can be used as follows:
(gdb) pp app
app =
[
<Myns::QGuiApplication> = {"Hello"}
staticMetaObject = <Myns::QMetaObject> = {""}
[parent] = <Myns::QObject *> = {"0x0"}
[children] = <Myns::QObjectList> = {"<3 items>"}
[properties] = "<>0 items>"
[methods] = "<6 items>"
[signals] = "<1 items>"
],<Myns::QApplication> = {"Hello"}
(gdb) pp app [properties],[children]
app =
[
<Myns::QGuiApplication> = {"Hello"}
staticMetaObject = <Myns::QMetaObject> = {""}
[parent] = <Myns::QObject *> = {"0x0"}
[children] = [
<Myns::QObject> = {""}
<Myns::QObject> = {""}
<Myns::QObject> = {"fusion"}
],<Myns::QObjectList> = {"<3 items>"}
[properties] = [
windowIcon = <Myns::QVariant (QIcon)> = {""}
cursorFlashTime = <Myns::QVariant (int)> = {"1000"}
doubleClickInterval = <Myns::QVariant (int)> = {"400"}
keyboardInputInterval = <Myns::QVariant (int)> = {"400"}
wheelScrollLines = <Myns::QVariant (int)> = {"3"}
globalStrut = <Myns::QVariant (QSize)> = {"(0, 0)"}
startDragTime = <Myns::QVariant (int)> = {"500"}
startDragDistance = <Myns::QVariant (int)> = {"10"}
styleSheet = <Myns::QVariant (QString)> = {""}
autoSipEnabled = <Myns::QVariant (bool)> = {"true"}
],"<10 items>"
[methods] = "<6 items>"
[signals] = "<1 items>"
],<Myns::QApplication> = {"Hello"}
(gdb) pp ss
ss =
<Myns::QString> = {"Hello"}
Change-Id: I6e4714a5cfe34c38917500d114ad9a70d20cff39
Reviewed-by: Christian Stenger <christian.stenger@digia.com>
Reviewed-by: hjk <hjk121@nokiamail.com>
2014-06-13 17:45:34 +02:00
|
|
|
self.prepare(args)
|
2015-03-26 13:03:38 +01:00
|
|
|
|
2020-02-24 14:37:56 +01:00
|
|
|
self.isBigEndian = gdb.execute('show endian', to_string=True).find('big endian') > 0
|
2016-11-07 08:57:26 +01:00
|
|
|
self.packCode = '>' if self.isBigEndian else '<'
|
|
|
|
|
2015-10-09 15:00:20 +02:00
|
|
|
(ok, res) = self.tryFetchInterpreterVariables(args)
|
|
|
|
if ok:
|
|
|
|
safePrint(res)
|
|
|
|
return
|
2015-10-08 16:19:57 +02:00
|
|
|
|
2022-11-08 12:42:04 +01:00
|
|
|
self.put('data=[')
|
2015-02-05 12:51:25 +01:00
|
|
|
|
2016-11-01 09:03:45 +01:00
|
|
|
partialVar = args.get('partialvar', '')
|
2016-09-20 11:52:06 +02:00
|
|
|
isPartial = len(partialVar) > 0
|
|
|
|
partialName = partialVar.split('.')[1].split('@')[0] if isPartial else None
|
2013-05-15 15:42:55 +02:00
|
|
|
|
2016-11-01 09:50:31 +01:00
|
|
|
variables = self.listLocals(partialName)
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('VARIABLES: %s' % variables)
|
2015-12-16 17:17:38 +01:00
|
|
|
|
2013-05-15 15:42:55 +02:00
|
|
|
# Take care of the return value of the last function call.
|
Debugger: Make dumpers somewhat work in command line GDB
With
python sys.path.insert(1, '/data/dev/creator/share/qtcreator/debugger/')
python from gdbbridge import *
in .gdbinit there's a new "GDB command", called "pp".
With code like
int main(int argc, char *argv[])
{
QString ss = "Hello";
QApplication app(argc, argv);
app.setObjectName(ss);
// break here
}
the 'pp' command can be used as follows:
(gdb) pp app
app =
[
<Myns::QGuiApplication> = {"Hello"}
staticMetaObject = <Myns::QMetaObject> = {""}
[parent] = <Myns::QObject *> = {"0x0"}
[children] = <Myns::QObjectList> = {"<3 items>"}
[properties] = "<>0 items>"
[methods] = "<6 items>"
[signals] = "<1 items>"
],<Myns::QApplication> = {"Hello"}
(gdb) pp app [properties],[children]
app =
[
<Myns::QGuiApplication> = {"Hello"}
staticMetaObject = <Myns::QMetaObject> = {""}
[parent] = <Myns::QObject *> = {"0x0"}
[children] = [
<Myns::QObject> = {""}
<Myns::QObject> = {""}
<Myns::QObject> = {"fusion"}
],<Myns::QObjectList> = {"<3 items>"}
[properties] = [
windowIcon = <Myns::QVariant (QIcon)> = {""}
cursorFlashTime = <Myns::QVariant (int)> = {"1000"}
doubleClickInterval = <Myns::QVariant (int)> = {"400"}
keyboardInputInterval = <Myns::QVariant (int)> = {"400"}
wheelScrollLines = <Myns::QVariant (int)> = {"3"}
globalStrut = <Myns::QVariant (QSize)> = {"(0, 0)"}
startDragTime = <Myns::QVariant (int)> = {"500"}
startDragDistance = <Myns::QVariant (int)> = {"10"}
styleSheet = <Myns::QVariant (QString)> = {""}
autoSipEnabled = <Myns::QVariant (bool)> = {"true"}
],"<10 items>"
[methods] = "<6 items>"
[signals] = "<1 items>"
],<Myns::QApplication> = {"Hello"}
(gdb) pp ss
ss =
<Myns::QString> = {"Hello"}
Change-Id: I6e4714a5cfe34c38917500d114ad9a70d20cff39
Reviewed-by: Christian Stenger <christian.stenger@digia.com>
Reviewed-by: hjk <hjk121@nokiamail.com>
2014-06-13 17:45:34 +02:00
|
|
|
if len(self.resultVarName) > 0:
|
2013-05-15 15:42:55 +02:00
|
|
|
try:
|
2016-09-06 08:54:43 +02:00
|
|
|
value = self.parseAndEvaluate(self.resultVarName)
|
|
|
|
value.name = self.resultVarName
|
2016-11-01 09:03:45 +01:00
|
|
|
value.iname = 'return.' + self.resultVarName
|
2016-09-06 08:54:43 +02:00
|
|
|
variables.append(value)
|
2013-05-15 15:42:55 +02:00
|
|
|
except:
|
|
|
|
# Don't bother. It's only supplementary information anyway.
|
|
|
|
pass
|
|
|
|
|
2016-09-20 11:52:06 +02:00
|
|
|
self.handleLocals(variables)
|
|
|
|
self.handleWatches(args)
|
2013-05-15 15:42:55 +02:00
|
|
|
|
2022-11-08 12:42:04 +01:00
|
|
|
self.put('],typeinfo=[')
|
2013-10-31 10:28:11 +01:00
|
|
|
for name in self.typesToReport.keys():
|
2014-01-24 15:13:20 +01:00
|
|
|
typeobj = self.typesToReport[name]
|
2013-10-31 10:28:11 +01:00
|
|
|
# Happens e.g. for '(anonymous namespace)::InsertDefOperation'
|
2016-09-06 08:54:43 +02:00
|
|
|
#if not typeobj is None:
|
2022-11-08 12:42:04 +01:00
|
|
|
# self.put('{name="%s",size="%s"}' % (self.hexencode(name), typeobj.sizeof))
|
|
|
|
self.put(']')
|
2013-10-31 10:28:11 +01:00
|
|
|
self.typesToReport = {}
|
2014-01-24 15:13:20 +01:00
|
|
|
|
2015-02-11 17:51:15 +01:00
|
|
|
if self.forceQtNamespace:
|
2017-10-23 09:13:07 +02:00
|
|
|
self.qtNamespaceToReport = self.qtNamespace()
|
2014-03-07 15:48:13 +01:00
|
|
|
|
2014-02-27 12:54:20 +01:00
|
|
|
if self.qtNamespaceToReport:
|
2022-11-08 12:42:04 +01:00
|
|
|
self.put(',qtnamespace="%s"' % self.qtNamespaceToReport)
|
2014-02-27 12:54:20 +01:00
|
|
|
self.qtNamespaceToReport = None
|
2015-03-26 13:03:38 +01:00
|
|
|
|
2022-11-08 12:42:04 +01:00
|
|
|
self.put(',partial="%d"' % isPartial)
|
|
|
|
self.put(',counts=%s' % self.counts)
|
|
|
|
self.put(',timings=%s' % self.timings)
|
|
|
|
self.reportResult(''.join(self.output), args)
|
2013-05-23 15:47:28 +02:00
|
|
|
|
2016-09-06 08:54:43 +02:00
|
|
|
def parseAndEvaluate(self, exp):
|
2018-10-17 16:05:14 +02:00
|
|
|
val = self.nativeParseAndEvaluate(exp)
|
|
|
|
return None if val is None else self.fromNativeValue(val)
|
|
|
|
|
|
|
|
def nativeParseAndEvaluate(self, exp):
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('EVALUATE "%s"' % exp)
|
2013-09-11 21:35:39 +02:00
|
|
|
try:
|
2016-09-06 08:54:43 +02:00
|
|
|
val = gdb.parse_and_eval(exp)
|
2018-10-17 16:05:14 +02:00
|
|
|
return val
|
2016-09-06 08:54:43 +02:00
|
|
|
except RuntimeError as error:
|
|
|
|
if self.passExceptions:
|
2020-02-21 10:10:00 +01:00
|
|
|
self.warn("Cannot evaluate '%s': %s" % (exp, error))
|
2016-11-01 17:22:56 +01:00
|
|
|
return None
|
2013-09-11 21:35:39 +02:00
|
|
|
|
2016-09-06 08:54:43 +02:00
|
|
|
def callHelper(self, rettype, value, function, args):
|
2020-03-03 11:11:50 +01:00
|
|
|
if self.isWindowsTarget():
|
|
|
|
raise Exception("gdb crashes when calling functions on Windows")
|
2013-09-11 21:35:39 +02:00
|
|
|
# args is a tuple.
|
2016-11-01 09:03:45 +01:00
|
|
|
arg = ''
|
2013-09-11 21:35:39 +02:00
|
|
|
for i in range(len(args)):
|
|
|
|
if i:
|
|
|
|
arg += ','
|
|
|
|
a = args[i]
|
|
|
|
if (':' in a) and not ("'" in a):
|
|
|
|
arg = "'%s'" % a
|
|
|
|
else:
|
|
|
|
arg += a
|
|
|
|
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('CALL: %s -> %s(%s)' % (value, function, arg))
|
2016-09-30 13:56:46 +02:00
|
|
|
typeName = value.type.name
|
2016-11-01 09:03:45 +01:00
|
|
|
if typeName.find(':') >= 0:
|
2014-01-29 00:41:48 +01:00
|
|
|
typeName = "'" + typeName + "'"
|
2013-09-11 21:35:39 +02:00
|
|
|
# 'class' is needed, see http://sourceware.org/bugzilla/show_bug.cgi?id=11912
|
2016-11-01 09:03:45 +01:00
|
|
|
#exp = '((class %s*)%s)->%s(%s)' % (typeName, value.laddress, function, arg)
|
2017-08-08 13:49:59 +02:00
|
|
|
addr = value.address()
|
2016-11-01 09:50:31 +01:00
|
|
|
if addr is None:
|
2020-02-24 14:37:56 +01:00
|
|
|
addr = self.pokeValue(value)
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('PTR: %s -> %s(%s)' % (value, function, addr))
|
2016-11-01 09:03:45 +01:00
|
|
|
exp = '((%s*)0x%x)->%s(%s)' % (typeName, addr, function, arg)
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('CALL: %s' % exp)
|
2014-01-29 00:41:48 +01:00
|
|
|
result = gdb.parse_and_eval(exp)
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn(' -> %s' % result)
|
2016-10-25 15:32:13 +02:00
|
|
|
res = self.fromNativeValue(result)
|
2017-08-08 13:49:59 +02:00
|
|
|
if value.address() is None:
|
2016-11-01 09:50:31 +01:00
|
|
|
self.releaseValue(addr)
|
2016-10-25 15:32:13 +02:00
|
|
|
return res
|
2013-10-16 17:04:34 +02:00
|
|
|
|
|
|
|
def makeExpression(self, value):
|
2016-11-01 09:03:45 +01:00
|
|
|
typename = '::' + value.type.name
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn(' TYPE: %s' % typename)
|
2016-11-01 09:03:45 +01:00
|
|
|
exp = '(*(%s*)(0x%x))' % (typename, value.address())
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn(' EXP: %s' % exp)
|
2013-10-16 17:04:34 +02:00
|
|
|
return exp
|
|
|
|
|
|
|
|
def makeStdString(init):
|
|
|
|
# Works only for small allocators, but they are usually empty.
|
2020-02-24 14:37:56 +01:00
|
|
|
gdb.execute('set $d=(std::string*)calloc(sizeof(std::string), 2)')
|
2016-11-01 09:03:45 +01:00
|
|
|
gdb.execute('call($d->basic_string("' + init +
|
2020-02-24 14:37:56 +01:00
|
|
|
'",*(std::allocator<char>*)(1+$d)))')
|
2016-11-01 09:03:45 +01:00
|
|
|
value = gdb.parse_and_eval('$d').dereference()
|
2013-10-16 17:04:34 +02:00
|
|
|
return value
|
|
|
|
|
2014-01-29 00:41:48 +01:00
|
|
|
def pokeValue(self, value):
|
2016-09-06 08:54:43 +02:00
|
|
|
# Allocates inferior memory and copies the contents of value.
|
|
|
|
# Returns a pointer to the copy.
|
2014-01-29 00:41:48 +01:00
|
|
|
# Avoid malloc symbol clash with QVector
|
2016-09-06 08:54:43 +02:00
|
|
|
size = value.type.size()
|
|
|
|
data = value.data()
|
|
|
|
h = self.hexencode(data)
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('DATA: %s' % h)
|
2020-02-24 14:37:56 +01:00
|
|
|
string = ''.join('\\x' + h[2 * i:2 * i + 2] for i in range(size))
|
2016-11-01 09:50:31 +01:00
|
|
|
exp = '(%s*)memcpy(calloc(%d, 1), "%s", %d)' \
|
|
|
|
% (value.type.name, size, string, size)
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('EXP: %s' % exp)
|
2016-09-06 08:54:43 +02:00
|
|
|
res = gdb.parse_and_eval(exp)
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('RES: %s' % res)
|
2021-09-23 12:31:49 +00:00
|
|
|
return toInteger(res)
|
2013-06-28 14:04:05 +02:00
|
|
|
|
2016-11-01 09:50:31 +01:00
|
|
|
def releaseValue(self, address):
|
|
|
|
gdb.parse_and_eval('free(0x%x)' % address)
|
|
|
|
|
2014-12-12 09:00:30 +01:00
|
|
|
def setValue(self, address, typename, value):
|
2016-11-01 09:03:45 +01:00
|
|
|
cmd = 'set {%s}%s=%s' % (typename, address, value)
|
2013-11-20 18:55:09 +01:00
|
|
|
gdb.execute(cmd)
|
|
|
|
|
2014-12-12 09:00:30 +01:00
|
|
|
def setValues(self, address, typename, values):
|
2016-11-01 09:03:45 +01:00
|
|
|
cmd = 'set {%s[%s]}%s={%s}' \
|
2014-12-12 09:00:30 +01:00
|
|
|
% (typename, len(values), address, ','.join(map(str, values)))
|
2013-11-20 18:55:09 +01:00
|
|
|
gdb.execute(cmd)
|
|
|
|
|
2013-10-30 11:40:53 +01:00
|
|
|
def selectedInferior(self):
|
|
|
|
try:
|
2013-10-30 12:38:29 +01:00
|
|
|
# gdb.Inferior is new in gdb 7.2
|
2013-10-30 15:07:54 +01:00
|
|
|
self.cachedInferior = gdb.selected_inferior()
|
2013-10-30 11:40:53 +01:00
|
|
|
except:
|
|
|
|
# Pre gdb 7.4. Right now we don't have more than one inferior anyway.
|
2013-10-30 15:07:54 +01:00
|
|
|
self.cachedInferior = gdb.inferiors()[0]
|
|
|
|
|
|
|
|
# Memoize result.
|
|
|
|
self.selectedInferior = lambda: self.cachedInferior
|
|
|
|
return self.cachedInferior
|
2013-10-30 11:40:53 +01:00
|
|
|
|
2015-10-08 16:19:57 +02:00
|
|
|
def readRawMemory(self, address, size):
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('READ: %s FROM 0x%x' % (size, address))
|
2016-11-07 08:57:26 +01:00
|
|
|
if address == 0 or size == 0:
|
|
|
|
return bytes()
|
|
|
|
res = self.selectedInferior().read_memory(address, size)
|
|
|
|
return res
|
2013-07-09 14:19:45 -07:00
|
|
|
|
2017-03-07 08:40:43 +01:00
|
|
|
def findStaticMetaObject(self, type):
|
|
|
|
symbolName = type.name + '::staticMetaObject'
|
2016-09-06 08:54:43 +02:00
|
|
|
symbol = gdb.lookup_global_symbol(symbolName, gdb.SYMBOL_VAR_DOMAIN)
|
|
|
|
if not symbol:
|
|
|
|
return 0
|
2014-02-28 12:05:48 +01:00
|
|
|
try:
|
2016-09-06 08:54:43 +02:00
|
|
|
# Older GDB ~7.4 don't have gdb.Symbol.value()
|
2021-09-23 12:31:49 +00:00
|
|
|
return toInteger(symbol.value().address)
|
2014-02-28 12:05:48 +01:00
|
|
|
except:
|
|
|
|
pass
|
2016-09-06 08:54:43 +02:00
|
|
|
|
|
|
|
address = gdb.parse_and_eval("&'%s'" % symbolName)
|
2021-09-23 12:31:49 +00:00
|
|
|
return toInteger(address)
|
2014-02-25 15:52:22 +01:00
|
|
|
|
2013-11-11 09:54:54 +01:00
|
|
|
def isArmArchitecture(self):
|
|
|
|
return 'arm' in gdb.TARGET_CONFIG.lower()
|
|
|
|
|
|
|
|
def isQnxTarget(self):
|
|
|
|
return 'qnx' in gdb.TARGET_CONFIG.lower()
|
|
|
|
|
2014-04-01 18:11:57 +02:00
|
|
|
def isWindowsTarget(self):
|
|
|
|
# We get i686-w64-mingw32
|
|
|
|
return 'mingw' in gdb.TARGET_CONFIG.lower()
|
|
|
|
|
2016-10-06 13:36:02 +02:00
|
|
|
def isMsvcTarget(self):
|
|
|
|
return False
|
|
|
|
|
2016-10-31 17:59:49 +01:00
|
|
|
def prettySymbolByAddress(self, address):
|
|
|
|
try:
|
|
|
|
return str(gdb.parse_and_eval('(void(*))0x%x' % address))
|
|
|
|
except:
|
|
|
|
return '0x%x' % address
|
|
|
|
|
2014-03-07 18:46:44 +01:00
|
|
|
def qtVersionString(self):
|
|
|
|
try:
|
2016-11-01 09:03:45 +01:00
|
|
|
return str(gdb.lookup_symbol('qVersion')[0].value()())
|
2014-03-07 18:46:44 +01:00
|
|
|
except:
|
|
|
|
pass
|
2013-10-30 15:07:54 +01:00
|
|
|
try:
|
2014-01-30 12:40:24 +01:00
|
|
|
ns = self.qtNamespace()
|
2014-03-07 18:46:44 +01:00
|
|
|
return str(gdb.parse_and_eval("((const char*(*)())'%sqVersion')()" % ns))
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
return None
|
|
|
|
|
|
|
|
def qtVersion(self):
|
2015-08-13 12:12:54 +02:00
|
|
|
try:
|
|
|
|
# Only available with Qt 5.3+
|
2016-11-01 09:03:45 +01:00
|
|
|
qtversion = int(str(gdb.parse_and_eval('((void**)&qtHookData)[2]')), 16)
|
2015-08-13 12:12:54 +02:00
|
|
|
self.qtVersion = lambda: qtversion
|
|
|
|
return qtversion
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
2014-03-07 18:46:44 +01:00
|
|
|
try:
|
|
|
|
version = self.qtVersionString()
|
2020-02-24 14:37:56 +01:00
|
|
|
(major, minor, patch) = version[version.find('"') + 1:version.rfind('"')].split('.')
|
2014-03-07 18:46:44 +01:00
|
|
|
qtversion = 0x10000 * int(major) + 0x100 * int(minor) + int(patch)
|
|
|
|
self.qtVersion = lambda: qtversion
|
|
|
|
return qtversion
|
2013-10-30 15:07:54 +01:00
|
|
|
except:
|
2014-03-07 15:48:13 +01:00
|
|
|
# Use fallback until we have a better answer.
|
|
|
|
return self.fallbackQtVersion
|
2013-10-30 15:07:54 +01:00
|
|
|
|
2015-11-02 16:01:43 +01:00
|
|
|
def createSpecialBreakpoints(self, args):
|
|
|
|
self.specialBreakpoints = []
|
2020-02-24 14:37:56 +01:00
|
|
|
|
2015-11-02 16:01:43 +01:00
|
|
|
def newSpecial(spec):
|
2019-03-25 17:12:01 +01:00
|
|
|
# GDB < 8.1 does not have the 'qualified' parameter here,
|
|
|
|
# GDB >= 8.1 applies some generous pattern matching, hitting
|
|
|
|
# e.g. also Foo::abort() when asking for '::abort'
|
|
|
|
class Pre81SpecialBreakpoint(gdb.Breakpoint):
|
|
|
|
def __init__(self, spec):
|
|
|
|
super(Pre81SpecialBreakpoint, self).__init__(spec,
|
2020-02-24 14:37:56 +01:00
|
|
|
gdb.BP_BREAKPOINT, internal=True)
|
2019-03-25 17:12:01 +01:00
|
|
|
self.spec = spec
|
|
|
|
|
|
|
|
def stop(self):
|
|
|
|
print("Breakpoint on '%s' hit." % self.spec)
|
|
|
|
return True
|
|
|
|
|
2015-11-02 16:01:43 +01:00
|
|
|
class SpecialBreakpoint(gdb.Breakpoint):
|
|
|
|
def __init__(self, spec):
|
2019-03-25 17:12:01 +01:00
|
|
|
super(SpecialBreakpoint, self).__init__(spec,
|
2020-02-24 14:37:56 +01:00
|
|
|
gdb.BP_BREAKPOINT,
|
|
|
|
internal=True,
|
|
|
|
qualified=True)
|
2015-11-02 16:01:43 +01:00
|
|
|
self.spec = spec
|
|
|
|
|
|
|
|
def stop(self):
|
|
|
|
print("Breakpoint on '%s' hit." % self.spec)
|
|
|
|
return True
|
2019-03-25 17:12:01 +01:00
|
|
|
|
|
|
|
try:
|
|
|
|
return SpecialBreakpoint(spec)
|
|
|
|
except:
|
|
|
|
return Pre81SpecialBreakpoint(spec)
|
2015-11-02 16:01:43 +01:00
|
|
|
|
|
|
|
# FIXME: ns is accessed too early. gdb.Breakpoint() has no
|
|
|
|
# 'rbreak' replacement, and breakpoints created with
|
2016-11-01 09:03:45 +01:00
|
|
|
# 'gdb.execute('rbreak...') cannot be made invisible.
|
2015-11-02 16:01:43 +01:00
|
|
|
# So let's ignore the existing of namespaced builds for this
|
|
|
|
# fringe feature here for now.
|
|
|
|
ns = self.qtNamespace()
|
|
|
|
if args.get('breakonabort', 0):
|
2016-11-01 09:03:45 +01:00
|
|
|
self.specialBreakpoints.append(newSpecial('abort'))
|
2015-11-02 16:01:43 +01:00
|
|
|
|
|
|
|
if args.get('breakonwarning', 0):
|
2016-11-01 09:03:45 +01:00
|
|
|
self.specialBreakpoints.append(newSpecial(ns + 'qWarning'))
|
|
|
|
self.specialBreakpoints.append(newSpecial(ns + 'QMessageLogger::warning'))
|
2015-11-02 16:01:43 +01:00
|
|
|
|
|
|
|
if args.get('breakonfatal', 0):
|
2016-11-01 09:03:45 +01:00
|
|
|
self.specialBreakpoints.append(newSpecial(ns + 'qFatal'))
|
|
|
|
self.specialBreakpoints.append(newSpecial(ns + 'QMessageLogger::fatal'))
|
2015-11-02 16:01:43 +01:00
|
|
|
|
2014-03-11 13:24:19 +01:00
|
|
|
#def threadname(self, maximalStackDepth, objectPrivateType):
|
|
|
|
# e = gdb.selected_frame()
|
2016-11-01 09:03:45 +01:00
|
|
|
# out = ''
|
2014-03-11 13:24:19 +01:00
|
|
|
# ns = self.qtNamespace()
|
|
|
|
# while True:
|
|
|
|
# maximalStackDepth -= 1
|
|
|
|
# if maximalStackDepth < 0:
|
|
|
|
# break
|
|
|
|
# e = e.older()
|
|
|
|
# if e == None or e.name() == None:
|
|
|
|
# break
|
2016-11-01 09:03:45 +01:00
|
|
|
# if e.name() in (ns + 'QThreadPrivate::start', '_ZN14QThreadPrivate5startEPv@4'):
|
2014-03-11 13:24:19 +01:00
|
|
|
# try:
|
2016-11-01 09:03:45 +01:00
|
|
|
# thrptr = e.read_var('thr').dereference()
|
|
|
|
# d_ptr = thrptr['d_ptr']['d'].cast(objectPrivateType).dereference()
|
2014-03-11 13:24:19 +01:00
|
|
|
# try:
|
2016-11-01 09:03:45 +01:00
|
|
|
# objectName = d_ptr['objectName']
|
2014-03-11 13:24:19 +01:00
|
|
|
# except: # Qt 5
|
2016-11-01 09:03:45 +01:00
|
|
|
# p = d_ptr['extraData']
|
2014-03-11 13:24:19 +01:00
|
|
|
# if not self.isNull(p):
|
2016-11-01 09:03:45 +01:00
|
|
|
# objectName = p.dereference()['objectName']
|
2014-03-11 13:24:19 +01:00
|
|
|
# if not objectName is None:
|
2016-09-06 08:54:43 +02:00
|
|
|
# (data, size, alloc) = self.stringData(objectName)
|
2014-03-11 13:24:19 +01:00
|
|
|
# if size > 0:
|
|
|
|
# s = self.readMemory(data, 2 * size)
|
|
|
|
#
|
|
|
|
# thread = gdb.selected_thread()
|
2015-12-11 13:28:21 +01:00
|
|
|
# inner = '{valueencoded="uf16:2:0",id="'
|
2014-03-11 13:24:19 +01:00
|
|
|
# inner += str(thread.num) + '",value="'
|
|
|
|
# inner += s
|
|
|
|
# #inner += self.encodeString(objectName)
|
|
|
|
# inner += '"},'
|
|
|
|
#
|
|
|
|
# out += inner
|
|
|
|
# except:
|
|
|
|
# pass
|
|
|
|
# return out
|
2013-10-30 12:38:29 +01:00
|
|
|
|
|
|
|
def threadnames(self, maximalStackDepth):
|
2013-11-20 00:34:58 +01:00
|
|
|
# FIXME: This needs a proper implementation for MinGW, and only there.
|
2014-03-11 13:24:19 +01:00
|
|
|
# Linux, Mac and QNX mirror the objectName() to the underlying threads,
|
2013-11-20 00:34:58 +01:00
|
|
|
# so we get the names already as part of the -thread-info output.
|
|
|
|
return '[]'
|
2014-03-11 13:24:19 +01:00
|
|
|
#out = '['
|
|
|
|
#oldthread = gdb.selected_thread()
|
|
|
|
#if oldthread:
|
|
|
|
# try:
|
2016-11-11 12:04:27 +01:00
|
|
|
# objectPrivateType = gdb.lookup_type(ns + 'QObjectPrivate').pointer()
|
2014-03-11 13:24:19 +01:00
|
|
|
# inferior = self.selectedInferior()
|
|
|
|
# for thread in inferior.threads():
|
|
|
|
# thread.switch()
|
|
|
|
# out += self.threadname(maximalStackDepth, objectPrivateType)
|
|
|
|
# except:
|
|
|
|
# pass
|
|
|
|
# oldthread.switch()
|
|
|
|
#return out + ']'
|
2013-10-30 12:38:29 +01:00
|
|
|
|
2013-10-30 15:07:54 +01:00
|
|
|
def importPlainDumper(self, printer):
|
2016-11-01 09:03:45 +01:00
|
|
|
name = printer.name.replace('::', '__')
|
2013-10-30 15:07:54 +01:00
|
|
|
self.qqDumpers[name] = PlainDumper(printer)
|
2016-11-01 09:03:45 +01:00
|
|
|
self.qqFormats[name] = ''
|
2013-10-30 15:07:54 +01:00
|
|
|
|
Debugger: Import GDB pretty printers for new objfiles
Previously, if enabled in the configuration, system
GDB pretty printers were loaded only once for all
objfiles present at the point in time when the loading
happened, which meant that GDB pretty printers for
objfiles loaded later were not taken into account
and thus unavailable if they were defined in the
corresponding autoload scripts for the objfiles.
In order to make use of those as well, remember whether
loading of system GDB pretty printer is enabled, and if so,
evaluate the pretty printers set at the new objfiles
in the handler for GDB's new_objfile event.
Extract the functionality for handling one objfile's
pretty_printers to a separate function
'importPlainDumpersForObj' to avoid code duplication.
Note: For this to actually work, it is required that the
objfile passed to the registered GDB new_objfile handler
actually has the pretty printers set at this stage.
This was only recently implemented on GDB side, in
GDB commit 2c473def12b08100e6b56261f01112db7f6aeab5
("gdb: do autoload before notifying Python side in
new_objfile event", 2021-04-27, [1]).
Therefore, this currently only works with the current
development version of GDB built from its git master
branch, not with any already released GDB versions.
(When older GDB versions are used, this will just behave
as it used to, and the corresponding GDB pretty-printers
will not be used.)
[1] https://sourceware.org/git/?p=binutils-gdb.git;a=commitdiff;h=2c473def12b08100e6b56261f01112db7f6aeab5
Fixes: QTCREATORBUG-25339
Change-Id: Ibc0ab16fbb75184fa199c0709bfc73954f04c193
Reviewed-by: hjk <hjk@qt.io>
2021-05-04 15:01:15 +02:00
|
|
|
def importPlainDumpersForObj(self, obj):
|
|
|
|
for printers in obj.pretty_printers + gdb.pretty_printers:
|
2023-01-08 16:57:53 +01:00
|
|
|
if hasattr(printers, "subprinters"):
|
|
|
|
for printer in printers.subprinters:
|
|
|
|
self.importPlainDumper(printer)
|
|
|
|
else:
|
|
|
|
self.warn('Loading a printer without the subprinters attribute not supported.')
|
Debugger: Import GDB pretty printers for new objfiles
Previously, if enabled in the configuration, system
GDB pretty printers were loaded only once for all
objfiles present at the point in time when the loading
happened, which meant that GDB pretty printers for
objfiles loaded later were not taken into account
and thus unavailable if they were defined in the
corresponding autoload scripts for the objfiles.
In order to make use of those as well, remember whether
loading of system GDB pretty printer is enabled, and if so,
evaluate the pretty printers set at the new objfiles
in the handler for GDB's new_objfile event.
Extract the functionality for handling one objfile's
pretty_printers to a separate function
'importPlainDumpersForObj' to avoid code duplication.
Note: For this to actually work, it is required that the
objfile passed to the registered GDB new_objfile handler
actually has the pretty printers set at this stage.
This was only recently implemented on GDB side, in
GDB commit 2c473def12b08100e6b56261f01112db7f6aeab5
("gdb: do autoload before notifying Python side in
new_objfile event", 2021-04-27, [1]).
Therefore, this currently only works with the current
development version of GDB built from its git master
branch, not with any already released GDB versions.
(When older GDB versions are used, this will just behave
as it used to, and the corresponding GDB pretty-printers
will not be used.)
[1] https://sourceware.org/git/?p=binutils-gdb.git;a=commitdiff;h=2c473def12b08100e6b56261f01112db7f6aeab5
Fixes: QTCREATORBUG-25339
Change-Id: Ibc0ab16fbb75184fa199c0709bfc73954f04c193
Reviewed-by: hjk <hjk@qt.io>
2021-05-04 15:01:15 +02:00
|
|
|
|
2013-10-30 15:07:54 +01:00
|
|
|
def importPlainDumpers(self):
|
|
|
|
for obj in gdb.objfiles():
|
Debugger: Import GDB pretty printers for new objfiles
Previously, if enabled in the configuration, system
GDB pretty printers were loaded only once for all
objfiles present at the point in time when the loading
happened, which meant that GDB pretty printers for
objfiles loaded later were not taken into account
and thus unavailable if they were defined in the
corresponding autoload scripts for the objfiles.
In order to make use of those as well, remember whether
loading of system GDB pretty printer is enabled, and if so,
evaluate the pretty printers set at the new objfiles
in the handler for GDB's new_objfile event.
Extract the functionality for handling one objfile's
pretty_printers to a separate function
'importPlainDumpersForObj' to avoid code duplication.
Note: For this to actually work, it is required that the
objfile passed to the registered GDB new_objfile handler
actually has the pretty printers set at this stage.
This was only recently implemented on GDB side, in
GDB commit 2c473def12b08100e6b56261f01112db7f6aeab5
("gdb: do autoload before notifying Python side in
new_objfile event", 2021-04-27, [1]).
Therefore, this currently only works with the current
development version of GDB built from its git master
branch, not with any already released GDB versions.
(When older GDB versions are used, this will just behave
as it used to, and the corresponding GDB pretty-printers
will not be used.)
[1] https://sourceware.org/git/?p=binutils-gdb.git;a=commitdiff;h=2c473def12b08100e6b56261f01112db7f6aeab5
Fixes: QTCREATORBUG-25339
Change-Id: Ibc0ab16fbb75184fa199c0709bfc73954f04c193
Reviewed-by: hjk <hjk@qt.io>
2021-05-04 15:01:15 +02:00
|
|
|
self.importPlainDumpersForObj(obj)
|
2013-10-30 15:07:54 +01:00
|
|
|
|
|
|
|
def qtNamespace(self):
|
2017-03-15 10:05:05 +01:00
|
|
|
# This function is replaced by handleQtCoreLoaded()
|
|
|
|
return ''
|
2016-11-07 08:57:26 +01:00
|
|
|
|
2016-12-02 09:06:05 +01:00
|
|
|
def findSymbol(self, symbolName):
|
|
|
|
try:
|
2021-09-23 12:31:49 +00:00
|
|
|
return toInteger(gdb.parse_and_eval("(size_t)&'%s'" % symbolName))
|
2016-12-02 09:06:05 +01:00
|
|
|
except:
|
|
|
|
return 0
|
|
|
|
|
2017-03-15 09:19:12 +01:00
|
|
|
def handleNewObjectFile(self, objfile):
|
|
|
|
name = objfile.filename
|
|
|
|
if self.isWindowsTarget():
|
2020-11-25 17:18:24 +01:00
|
|
|
qtCoreMatch = re.match(r'.*Qt[56]?Core[^/.]*d?\.dll', name)
|
2017-03-15 09:19:12 +01:00
|
|
|
else:
|
2020-11-25 17:18:24 +01:00
|
|
|
qtCoreMatch = re.match(r'.*/libQt[56]?Core[^/.]*\.so', name)
|
2017-10-23 09:52:52 +02:00
|
|
|
|
2018-01-22 12:41:49 +01:00
|
|
|
if qtCoreMatch is not None:
|
2018-09-24 13:57:16 +02:00
|
|
|
self.addDebugLibs(objfile)
|
2017-03-15 09:19:12 +01:00
|
|
|
self.handleQtCoreLoaded(objfile)
|
|
|
|
|
Debugger: Import GDB pretty printers for new objfiles
Previously, if enabled in the configuration, system
GDB pretty printers were loaded only once for all
objfiles present at the point in time when the loading
happened, which meant that GDB pretty printers for
objfiles loaded later were not taken into account
and thus unavailable if they were defined in the
corresponding autoload scripts for the objfiles.
In order to make use of those as well, remember whether
loading of system GDB pretty printer is enabled, and if so,
evaluate the pretty printers set at the new objfiles
in the handler for GDB's new_objfile event.
Extract the functionality for handling one objfile's
pretty_printers to a separate function
'importPlainDumpersForObj' to avoid code duplication.
Note: For this to actually work, it is required that the
objfile passed to the registered GDB new_objfile handler
actually has the pretty printers set at this stage.
This was only recently implemented on GDB side, in
GDB commit 2c473def12b08100e6b56261f01112db7f6aeab5
("gdb: do autoload before notifying Python side in
new_objfile event", 2021-04-27, [1]).
Therefore, this currently only works with the current
development version of GDB built from its git master
branch, not with any already released GDB versions.
(When older GDB versions are used, this will just behave
as it used to, and the corresponding GDB pretty-printers
will not be used.)
[1] https://sourceware.org/git/?p=binutils-gdb.git;a=commitdiff;h=2c473def12b08100e6b56261f01112db7f6aeab5
Fixes: QTCREATORBUG-25339
Change-Id: Ibc0ab16fbb75184fa199c0709bfc73954f04c193
Reviewed-by: hjk <hjk@qt.io>
2021-05-04 15:01:15 +02:00
|
|
|
if self.usePlainDumpers:
|
|
|
|
self.importPlainDumpersForObj(objfile)
|
|
|
|
|
2018-09-24 13:57:16 +02:00
|
|
|
def addDebugLibs(self, objfile):
|
|
|
|
# The directory where separate debug symbols are searched for
|
|
|
|
# is "/usr/lib/debug".
|
|
|
|
try:
|
|
|
|
cooked = gdb.execute('show debug-file-directory', to_string=True)
|
|
|
|
clean = cooked.split('"')[1]
|
|
|
|
newdir = '/'.join(objfile.filename.split('/')[:-1])
|
|
|
|
gdb.execute('set debug-file-directory %s:%s' % (clean, newdir))
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
2017-03-15 09:19:12 +01:00
|
|
|
def handleQtCoreLoaded(self, objfile):
|
|
|
|
fd, tmppath = tempfile.mkstemp()
|
|
|
|
os.close(fd)
|
|
|
|
try:
|
Debugger: Avoid exception with GDB 10 to fix running dumper tests
Since GDB commit 1ba1ac88011703abcd0271e4f5d00927dc69a09a [1]
("gdb: Enable stdin on exception in execute_gdb_command",
committed 2020-01-24), stdin for GDB is re-enabled whenever
an exception is caught during 'gdb.execute()'.
When running the dumper tests for GDB, such an exception
was thrown when loading the qtcore library, resulting
in the GDB commands after the 'run' command (s.
'tst_Dumpers::dumper') to be be handled by GDB prematurely
when loading the library instead of when reaching the
end of the test program.
This resulted in the dumper tests failing with output like
29: (gdb)
29: &"up 0\n"
29: &"No stack.\n"
29: ^error,msg="No stack."
29: (gdb)
29: &"python theDumper.fetchVariables({'token':2,'fancy':1,'forcens':1,'autoderef':1,'dyntype':1,'passexceptions':1,'testing':1,'qobjectnames':1,'expanded':['local']})\n"
29: &"Traceback (most recent call last):\n"
29: &" File \"<string>\", line 1, in <module>\n"
29: &" File \".../qt-creator/share/qtcreator/debugger/gdbbridge.py\", line 711, in fetchVariables\n"
29: &" variables = self.listLocals(partialName)\n"
29: &" File \".../qt-creator/share/qtcreator/debugger/gdbbridge.py\", line 597, in listLocals\n"
29: &" frame = gdb.selected_frame()\n"
29: &"gdb.error: No frame is currently selected.\n"
29: &"Error while executing Python code.\n"
29: ^error,msg="Error while executing Python code."
Avoid the exception by first trying to 'gdb.execute' the
command that works for GDB 8+ and fall back to the command
for earlier GDB versions, i.e. reverse the order. That
should be fine for all GDB versions, given the commit
mentioned above is only contained from GDB 10 on.
While at it, move the assignment to 'cmd' into the
corresponding 'try' blocks to make the context a bit
clearer.
[1] https://sourceware.org/git/?p=binutils-gdb.git;a=commitdiff;h=1ba1ac88011703abcd0271e4f5d00927dc69a09a
Change-Id: I2d1bb52a777801e6bd77a5eda581a2fd5dd3a18f
Reviewed-by: hjk <hjk@qt.io>
2021-02-18 17:17:58 +01:00
|
|
|
cmd = 'maint print msymbols -objfile "%s" -- %s' % (objfile.filename, tmppath)
|
2020-02-24 14:37:56 +01:00
|
|
|
symbols = gdb.execute(cmd, to_string=True)
|
2017-03-15 09:19:12 +01:00
|
|
|
except:
|
2018-07-05 15:58:35 +02:00
|
|
|
try:
|
Debugger: Avoid exception with GDB 10 to fix running dumper tests
Since GDB commit 1ba1ac88011703abcd0271e4f5d00927dc69a09a [1]
("gdb: Enable stdin on exception in execute_gdb_command",
committed 2020-01-24), stdin for GDB is re-enabled whenever
an exception is caught during 'gdb.execute()'.
When running the dumper tests for GDB, such an exception
was thrown when loading the qtcore library, resulting
in the GDB commands after the 'run' command (s.
'tst_Dumpers::dumper') to be be handled by GDB prematurely
when loading the library instead of when reaching the
end of the test program.
This resulted in the dumper tests failing with output like
29: (gdb)
29: &"up 0\n"
29: &"No stack.\n"
29: ^error,msg="No stack."
29: (gdb)
29: &"python theDumper.fetchVariables({'token':2,'fancy':1,'forcens':1,'autoderef':1,'dyntype':1,'passexceptions':1,'testing':1,'qobjectnames':1,'expanded':['local']})\n"
29: &"Traceback (most recent call last):\n"
29: &" File \"<string>\", line 1, in <module>\n"
29: &" File \".../qt-creator/share/qtcreator/debugger/gdbbridge.py\", line 711, in fetchVariables\n"
29: &" variables = self.listLocals(partialName)\n"
29: &" File \".../qt-creator/share/qtcreator/debugger/gdbbridge.py\", line 597, in listLocals\n"
29: &" frame = gdb.selected_frame()\n"
29: &"gdb.error: No frame is currently selected.\n"
29: &"Error while executing Python code.\n"
29: ^error,msg="Error while executing Python code."
Avoid the exception by first trying to 'gdb.execute' the
command that works for GDB 8+ and fall back to the command
for earlier GDB versions, i.e. reverse the order. That
should be fine for all GDB versions, given the commit
mentioned above is only contained from GDB 10 on.
While at it, move the assignment to 'cmd' into the
corresponding 'try' blocks to make the context a bit
clearer.
[1] https://sourceware.org/git/?p=binutils-gdb.git;a=commitdiff;h=1ba1ac88011703abcd0271e4f5d00927dc69a09a
Change-Id: I2d1bb52a777801e6bd77a5eda581a2fd5dd3a18f
Reviewed-by: hjk <hjk@qt.io>
2021-02-18 17:17:58 +01:00
|
|
|
# command syntax depends on gdb version - below is gdb < 8
|
|
|
|
cmd = 'maint print msymbols %s "%s"' % (tmppath, objfile.filename)
|
2020-02-24 14:37:56 +01:00
|
|
|
symbols = gdb.execute(cmd, to_string=True)
|
2018-07-05 15:58:35 +02:00
|
|
|
except:
|
|
|
|
pass
|
2017-03-15 09:19:12 +01:00
|
|
|
ns = ''
|
|
|
|
with open(tmppath) as f:
|
|
|
|
for line in f:
|
|
|
|
if line.find('msgHandlerGrabbed ') >= 0:
|
|
|
|
# [11] b 0x7ffff683c000 _ZN4MynsL17msgHandlerGrabbedE
|
|
|
|
# section .tbss Myns::msgHandlerGrabbed qlogging.cpp
|
2020-02-24 14:37:56 +01:00
|
|
|
ns = re.split(r'_ZN?(\d*)(\w*)L17msgHandlerGrabbedE? ', line)[2]
|
2017-03-15 09:19:12 +01:00
|
|
|
if len(ns):
|
|
|
|
ns += '::'
|
|
|
|
break
|
2017-10-23 09:52:52 +02:00
|
|
|
if line.find('currentThreadData ') >= 0:
|
|
|
|
# [ 0] b 0x7ffff67d3000 _ZN2UUL17currentThreadDataE
|
|
|
|
# section .tbss UU::currentThreadData qthread_unix.cpp\\n
|
2020-02-24 14:37:56 +01:00
|
|
|
ns = re.split(r'_ZN?(\d*)(\w*)L17currentThreadDataE? ', line)[2]
|
2017-10-23 09:52:52 +02:00
|
|
|
if len(ns):
|
|
|
|
ns += '::'
|
|
|
|
break
|
2017-03-15 09:19:12 +01:00
|
|
|
os.remove(tmppath)
|
|
|
|
|
|
|
|
lenns = len(ns)
|
|
|
|
strns = ('%d%s' % (lenns - 2, ns[:lenns - 2])) if lenns else ''
|
|
|
|
|
|
|
|
if lenns:
|
2017-03-15 10:05:05 +01:00
|
|
|
# This might be wrong, but we can't do better: We found
|
|
|
|
# a libQt5Core and could not extract a namespace.
|
|
|
|
# The best guess is that there isn't any.
|
|
|
|
self.qtNamespaceToReport = ns
|
|
|
|
self.qtNamespace = lambda: ns
|
|
|
|
|
2017-03-15 09:19:12 +01:00
|
|
|
sym = '_ZN%s7QObject11customEventEPNS_6QEventE' % strns
|
|
|
|
else:
|
|
|
|
sym = '_ZN7QObject11customEventEP6QEvent'
|
|
|
|
self.qtCustomEventFunc = self.findSymbol(sym)
|
|
|
|
|
|
|
|
sym += '@plt'
|
|
|
|
self.qtCustomEventPltFunc = self.findSymbol(sym)
|
|
|
|
|
|
|
|
sym = '_ZNK%s7QObject8propertyEPKc' % strns
|
2020-10-15 14:47:34 +02:00
|
|
|
if not self.isWindowsTarget(): # prevent calling the property function on windows
|
|
|
|
self.qtPropertyFunc = self.findSymbol(sym)
|
2017-03-15 09:19:12 +01:00
|
|
|
|
2015-02-10 13:40:26 +01:00
|
|
|
def assignValue(self, args):
|
|
|
|
typeName = self.hexdecode(args['type'])
|
|
|
|
expr = self.hexdecode(args['expr'])
|
|
|
|
value = self.hexdecode(args['value'])
|
|
|
|
simpleType = int(args['simpleType'])
|
2013-10-30 15:07:54 +01:00
|
|
|
ns = self.qtNamespace()
|
2013-11-20 18:55:09 +01:00
|
|
|
if typeName.startswith(ns):
|
|
|
|
typeName = typeName[len(ns):]
|
2016-11-01 09:03:45 +01:00
|
|
|
typeName = typeName.replace('::', '__')
|
2013-11-20 18:55:09 +01:00
|
|
|
pos = typeName.find('<')
|
2013-10-30 15:07:54 +01:00
|
|
|
if pos != -1:
|
2013-11-20 18:55:09 +01:00
|
|
|
typeName = typeName[0:pos]
|
2015-02-10 13:40:26 +01:00
|
|
|
if typeName in self.qqEditable and not simpleType:
|
|
|
|
#self.qqEditable[typeName](self, expr, value)
|
2017-08-08 13:49:59 +02:00
|
|
|
expr = self.parseAndEvaluate(expr)
|
2015-02-10 13:40:26 +01:00
|
|
|
self.qqEditable[typeName](self, expr, value)
|
2013-10-30 15:07:54 +01:00
|
|
|
else:
|
2016-11-01 09:03:45 +01:00
|
|
|
cmd = 'set variable (%s)=%s' % (expr, value)
|
2013-11-20 18:55:09 +01:00
|
|
|
gdb.execute(cmd)
|
2013-10-30 15:07:54 +01:00
|
|
|
|
2017-09-07 17:47:22 +02:00
|
|
|
def appendSolibSearchPath(self, args):
|
|
|
|
new = list(map(self.hexdecode, args['path']))
|
|
|
|
old = [gdb.parameter('solib-search-path')]
|
2021-08-03 08:40:56 +02:00
|
|
|
joined = os.pathsep.join([item for item in old + new if item != ''])
|
|
|
|
gdb.execute('set solib-search-path %s' % joined)
|
2017-09-07 17:47:22 +02:00
|
|
|
|
2016-12-13 09:46:51 +01:00
|
|
|
def watchPoint(self, args):
|
2016-12-15 18:28:58 +01:00
|
|
|
self.reportToken(args)
|
2016-12-13 09:46:51 +01:00
|
|
|
ns = self.qtNamespace()
|
|
|
|
lenns = len(ns)
|
|
|
|
strns = ('%d%s' % (lenns - 2, ns[:lenns - 2])) if lenns else ''
|
|
|
|
sym = '_ZN%s12QApplication8widgetAtEii' % strns
|
|
|
|
expr = '%s(%s,%s)' % (sym, args['x'], args['y'])
|
|
|
|
res = self.parseAndEvaluate(expr)
|
|
|
|
p = 0 if res is None else res.pointer()
|
|
|
|
n = ("'%sQWidget'" % ns) if lenns else 'QWidget'
|
2016-12-15 18:28:58 +01:00
|
|
|
self.reportResult('selected="0x%x",expr="(%s*)0x%x"' % (p, n, p), args)
|
2016-12-13 09:46:51 +01:00
|
|
|
|
2017-03-22 14:19:50 +01:00
|
|
|
def nativeValueDereferencePointer(self, value):
|
2019-12-18 13:27:20 +01:00
|
|
|
# This is actually pretty expensive, up to 100ms.
|
2017-03-22 14:19:50 +01:00
|
|
|
deref = value.nativeValue.dereference()
|
2020-06-29 09:34:57 +03:00
|
|
|
if self.useDynamicType:
|
|
|
|
deref = deref.cast(deref.dynamic_type)
|
|
|
|
return self.fromNativeValue(deref)
|
2017-03-08 16:37:52 +01:00
|
|
|
|
2017-03-22 14:19:50 +01:00
|
|
|
def nativeValueDereferenceReference(self, value):
|
|
|
|
nativeValue = value.nativeValue
|
2017-03-08 16:37:52 +01:00
|
|
|
return self.fromNativeValue(nativeValue.cast(nativeValue.type.target()))
|
|
|
|
|
2016-09-26 14:29:16 +02:00
|
|
|
def nativeDynamicTypeName(self, address, baseType):
|
2016-11-07 08:57:26 +01:00
|
|
|
# Needed for Gdb13393 test.
|
|
|
|
nativeType = self.lookupNativeType(baseType.name)
|
2016-11-10 21:28:23 +01:00
|
|
|
if nativeType is None:
|
|
|
|
return None
|
2016-11-07 08:57:26 +01:00
|
|
|
nativeTypePointer = nativeType.pointer()
|
|
|
|
nativeValue = gdb.Value(address).cast(nativeTypePointer).dereference()
|
|
|
|
val = nativeValue.cast(nativeValue.dynamic_type)
|
|
|
|
return str(val.type)
|
|
|
|
#try:
|
2016-11-01 09:03:45 +01:00
|
|
|
# vtbl = gdb.execute('info symbol {%s*}0x%x' % (baseType.name, address), to_string = True)
|
2016-11-07 08:57:26 +01:00
|
|
|
#except:
|
|
|
|
# return None
|
2016-11-01 09:03:45 +01:00
|
|
|
#pos1 = vtbl.find('vtable ')
|
2016-11-07 08:57:26 +01:00
|
|
|
#if pos1 == -1:
|
|
|
|
# return None
|
|
|
|
#pos1 += 11
|
2016-11-01 09:03:45 +01:00
|
|
|
#pos2 = vtbl.find(' +', pos1)
|
2016-11-07 08:57:26 +01:00
|
|
|
#if pos2 == -1:
|
|
|
|
# return None
|
|
|
|
#return vtbl[pos1 : pos2]
|
|
|
|
|
|
|
|
def nativeDynamicType(self, address, baseType):
|
|
|
|
# Needed for Gdb13393 test.
|
|
|
|
nativeType = self.lookupNativeType(baseType.name)
|
2016-11-10 21:28:23 +01:00
|
|
|
if nativeType is None:
|
|
|
|
return baseType
|
2016-11-07 08:57:26 +01:00
|
|
|
nativeTypePointer = nativeType.pointer()
|
|
|
|
nativeValue = gdb.Value(address).cast(nativeTypePointer).dereference()
|
|
|
|
return self.fromNativeType(nativeValue.dynamic_type)
|
2013-10-31 10:28:11 +01:00
|
|
|
|
2014-01-10 20:01:35 +01:00
|
|
|
def enumExpression(self, enumType, enumValue):
|
2016-11-01 09:03:45 +01:00
|
|
|
return self.qtNamespace() + 'Qt::' + enumValue
|
2014-01-10 20:01:35 +01:00
|
|
|
|
2016-09-06 08:54:43 +02:00
|
|
|
def lookupNativeType(self, typeName):
|
|
|
|
nativeType = self.lookupNativeTypeHelper(typeName)
|
2020-02-24 14:37:56 +01:00
|
|
|
if nativeType is not None:
|
2016-09-06 08:54:43 +02:00
|
|
|
self.check(isinstance(nativeType, gdb.Type))
|
|
|
|
return nativeType
|
|
|
|
|
|
|
|
def lookupNativeTypeHelper(self, typeName):
|
|
|
|
typeobj = self.typeCache.get(typeName)
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('LOOKUP 1: %s -> %s' % (typeName, typeobj))
|
2020-02-24 14:37:56 +01:00
|
|
|
if typeobj is not None:
|
2014-12-12 09:00:30 +01:00
|
|
|
return typeobj
|
2013-10-31 10:28:11 +01:00
|
|
|
|
2016-11-01 09:03:45 +01:00
|
|
|
if typeName == 'void':
|
2016-09-06 08:54:43 +02:00
|
|
|
typeobj = gdb.lookup_type(typeName)
|
|
|
|
self.typeCache[typeName] = typeobj
|
|
|
|
self.typesToReport[typeName] = typeobj
|
2014-12-12 09:00:30 +01:00
|
|
|
return typeobj
|
2013-10-31 10:28:11 +01:00
|
|
|
|
|
|
|
#try:
|
2016-11-01 09:03:45 +01:00
|
|
|
# typeobj = gdb.parse_and_eval('{%s}&main' % typeName).typeobj
|
2014-12-12 09:00:30 +01:00
|
|
|
# if not typeobj is None:
|
2016-09-06 08:54:43 +02:00
|
|
|
# self.typeCache[typeName] = typeobj
|
|
|
|
# self.typesToReport[typeName] = typeobj
|
2014-12-12 09:00:30 +01:00
|
|
|
# return typeobj
|
2013-10-31 10:28:11 +01:00
|
|
|
#except:
|
|
|
|
# pass
|
|
|
|
|
|
|
|
# See http://sourceware.org/bugzilla/show_bug.cgi?id=13269
|
2016-11-01 09:03:45 +01:00
|
|
|
# gcc produces '{anonymous}', gdb '(anonymous namespace)'
|
|
|
|
# '<unnamed>' has been seen too. The only thing gdb
|
|
|
|
# understands when reading things back is '(anonymous namespace)'
|
|
|
|
if typeName.find('{anonymous}') != -1:
|
2016-09-06 08:54:43 +02:00
|
|
|
ts = typeName
|
2016-11-01 09:03:45 +01:00
|
|
|
ts = ts.replace('{anonymous}', '(anonymous namespace)')
|
2016-09-06 08:54:43 +02:00
|
|
|
typeobj = self.lookupNativeType(ts)
|
2020-02-24 14:37:56 +01:00
|
|
|
if typeobj is not None:
|
2016-09-06 08:54:43 +02:00
|
|
|
self.typeCache[typeName] = typeobj
|
|
|
|
self.typesToReport[typeName] = typeobj
|
2014-12-12 09:00:30 +01:00
|
|
|
return typeobj
|
2013-10-31 10:28:11 +01:00
|
|
|
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn(" RESULT FOR 7.2: '%s': %s" % (typeName, typeobj))
|
2013-10-31 10:28:11 +01:00
|
|
|
|
|
|
|
# This part should only trigger for
|
|
|
|
# gdb 7.1 for types with namespace separators.
|
|
|
|
# And anonymous namespaces.
|
|
|
|
|
2016-09-06 08:54:43 +02:00
|
|
|
ts = typeName
|
2013-10-31 10:28:11 +01:00
|
|
|
while True:
|
2016-11-01 09:03:45 +01:00
|
|
|
if ts.startswith('class '):
|
2013-10-31 10:28:11 +01:00
|
|
|
ts = ts[6:]
|
2016-11-01 09:03:45 +01:00
|
|
|
elif ts.startswith('struct '):
|
2013-10-31 10:28:11 +01:00
|
|
|
ts = ts[7:]
|
2016-11-01 09:03:45 +01:00
|
|
|
elif ts.startswith('const '):
|
2013-10-31 10:28:11 +01:00
|
|
|
ts = ts[6:]
|
2016-11-01 09:03:45 +01:00
|
|
|
elif ts.startswith('volatile '):
|
2013-10-31 10:28:11 +01:00
|
|
|
ts = ts[9:]
|
2016-11-01 09:03:45 +01:00
|
|
|
elif ts.startswith('enum '):
|
2013-10-31 10:28:11 +01:00
|
|
|
ts = ts[5:]
|
2016-11-01 09:03:45 +01:00
|
|
|
elif ts.endswith(' const'):
|
2013-10-31 10:28:11 +01:00
|
|
|
ts = ts[:-6]
|
2016-11-01 09:03:45 +01:00
|
|
|
elif ts.endswith(' volatile'):
|
2013-10-31 10:28:11 +01:00
|
|
|
ts = ts[:-9]
|
2016-11-01 09:03:45 +01:00
|
|
|
elif ts.endswith('*const'):
|
2013-10-31 10:28:11 +01:00
|
|
|
ts = ts[:-5]
|
2016-11-01 09:03:45 +01:00
|
|
|
elif ts.endswith('*volatile'):
|
2013-10-31 10:28:11 +01:00
|
|
|
ts = ts[:-8]
|
|
|
|
else:
|
|
|
|
break
|
|
|
|
|
|
|
|
if ts.endswith('*'):
|
2016-09-06 08:54:43 +02:00
|
|
|
typeobj = self.lookupNativeType(ts[0:-1])
|
2020-02-24 14:37:56 +01:00
|
|
|
if typeobj is not None:
|
2014-12-12 09:00:30 +01:00
|
|
|
typeobj = typeobj.pointer()
|
2016-09-06 08:54:43 +02:00
|
|
|
self.typeCache[typeName] = typeobj
|
|
|
|
self.typesToReport[typeName] = typeobj
|
2014-12-12 09:00:30 +01:00
|
|
|
return typeobj
|
2013-10-31 10:28:11 +01:00
|
|
|
|
|
|
|
try:
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn("LOOKING UP 1 '%s'" % ts)
|
2014-12-12 09:00:30 +01:00
|
|
|
typeobj = gdb.lookup_type(ts)
|
2013-10-31 10:28:11 +01:00
|
|
|
except RuntimeError as error:
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn("LOOKING UP 2 '%s' ERROR %s" % (ts, error))
|
2013-10-31 10:28:11 +01:00
|
|
|
# See http://sourceware.org/bugzilla/show_bug.cgi?id=11912
|
|
|
|
exp = "(class '%s'*)0" % ts
|
|
|
|
try:
|
2016-09-06 08:54:43 +02:00
|
|
|
typeobj = self.parse_and_eval(exp).type.target()
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn("LOOKING UP 3 '%s'" % typeobj)
|
2013-10-31 10:28:11 +01:00
|
|
|
except:
|
2016-11-01 09:03:45 +01:00
|
|
|
# Can throw 'RuntimeError: No type named class Foo.'
|
2013-10-31 10:28:11 +01:00
|
|
|
pass
|
|
|
|
except:
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn("LOOKING UP '%s' FAILED" % ts)
|
2013-10-31 10:28:11 +01:00
|
|
|
pass
|
|
|
|
|
2020-02-24 14:37:56 +01:00
|
|
|
if typeobj is not None:
|
2020-02-21 10:10:00 +01:00
|
|
|
#DumperBase.warn('CACHING: %s' % typeobj)
|
2016-09-06 08:54:43 +02:00
|
|
|
self.typeCache[typeName] = typeobj
|
|
|
|
self.typesToReport[typeName] = typeobj
|
2013-10-31 10:28:11 +01:00
|
|
|
|
2016-11-01 09:03:45 +01:00
|
|
|
# This could still be None as gdb.lookup_type('char[3]') generates
|
|
|
|
# 'RuntimeError: No type named char[3]'
|
2016-09-06 08:54:43 +02:00
|
|
|
#self.typeCache[typeName] = typeobj
|
|
|
|
#self.typesToReport[typeName] = typeobj
|
2014-12-12 09:00:30 +01:00
|
|
|
return typeobj
|
2013-10-31 10:28:11 +01:00
|
|
|
|
2015-10-08 16:19:57 +02:00
|
|
|
def doContinue(self):
|
|
|
|
gdb.execute('continue')
|
|
|
|
|
2015-10-27 16:13:04 +01:00
|
|
|
def fetchStack(self, args):
|
2020-08-27 13:10:38 +02:00
|
|
|
|
2016-10-11 14:58:55 +02:00
|
|
|
def fromNativePath(string):
|
|
|
|
return string.replace('\\', '/')
|
2015-03-01 11:49:19 +02:00
|
|
|
|
2016-10-11 14:58:55 +02:00
|
|
|
extraQml = int(args.get('extraqml', '0'))
|
2015-02-11 16:05:55 +01:00
|
|
|
limit = int(args['limit'])
|
|
|
|
if limit <= 0:
|
2020-02-24 14:37:56 +01:00
|
|
|
limit = 10000
|
2015-02-11 16:05:55 +01:00
|
|
|
|
2015-10-08 16:19:57 +02:00
|
|
|
self.prepare(args)
|
2022-11-08 12:42:04 +01:00
|
|
|
self.output = []
|
2014-12-05 18:45:54 +01:00
|
|
|
|
|
|
|
i = 0
|
2016-10-11 14:58:55 +02:00
|
|
|
if extraQml:
|
|
|
|
frame = gdb.newest_frame()
|
|
|
|
ns = self.qtNamespace()
|
|
|
|
needle = self.qtNamespace() + 'QV4::ExecutionEngine'
|
2020-08-27 13:10:38 +02:00
|
|
|
pats = [
|
|
|
|
'{0}qt_v4StackTraceForEngine((void*)0x{1:x})',
|
|
|
|
'{0}qt_v4StackTrace((({0}QV4::ExecutionEngine *)0x{1:x})->currentContext())',
|
|
|
|
'{0}qt_v4StackTrace((({0}QV4::ExecutionEngine *)0x{1:x})->currentContext)',
|
|
|
|
]
|
2016-10-11 14:58:55 +02:00
|
|
|
done = False
|
|
|
|
while i < limit and frame and not done:
|
|
|
|
block = None
|
|
|
|
try:
|
|
|
|
block = frame.block()
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
if block is not None:
|
|
|
|
for symbol in block:
|
|
|
|
if symbol.is_variable or symbol.is_argument:
|
|
|
|
value = symbol.value(frame)
|
|
|
|
typeobj = value.type
|
|
|
|
if typeobj.code == gdb.TYPE_CODE_PTR:
|
2020-02-24 14:37:56 +01:00
|
|
|
dereftype = typeobj.target().unqualified()
|
|
|
|
if dereftype.name == needle:
|
2021-09-23 12:31:49 +00:00
|
|
|
addr = toInteger(value)
|
2020-08-27 13:10:38 +02:00
|
|
|
res = None
|
|
|
|
for pat in pats:
|
|
|
|
try:
|
|
|
|
expr = pat.format(ns, addr)
|
|
|
|
res = str(gdb.parse_and_eval(expr))
|
|
|
|
break
|
|
|
|
except:
|
|
|
|
continue
|
|
|
|
|
|
|
|
if res is None:
|
|
|
|
done = True
|
|
|
|
break
|
|
|
|
|
2016-10-11 14:58:55 +02:00
|
|
|
pos = res.find('"stack=[')
|
|
|
|
if pos != -1:
|
|
|
|
res = res[pos + 8:-2]
|
|
|
|
res = res.replace('\\\"', '\"')
|
|
|
|
res = res.replace('func=', 'function=')
|
|
|
|
self.put(res)
|
|
|
|
done = True
|
|
|
|
break
|
|
|
|
frame = frame.older()
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
frame = gdb.newest_frame()
|
2015-01-22 12:05:00 +01:00
|
|
|
self.currentCallContext = None
|
2022-11-08 12:42:04 +01:00
|
|
|
self.output = []
|
|
|
|
self.put('stack={frames=[')
|
2015-02-11 16:05:55 +01:00
|
|
|
while i < limit and frame:
|
2022-11-08 12:42:04 +01:00
|
|
|
name = frame.name()
|
|
|
|
functionName = '??' if name is None else name
|
|
|
|
fileName = ''
|
|
|
|
objfile = ''
|
|
|
|
symtab = ''
|
|
|
|
pc = frame.pc()
|
|
|
|
sal = frame.find_sal()
|
|
|
|
line = -1
|
|
|
|
if sal:
|
|
|
|
line = sal.line
|
|
|
|
symtab = sal.symtab
|
|
|
|
if symtab is not None:
|
|
|
|
objfile = fromNativePath(symtab.objfile.filename)
|
|
|
|
fullname = symtab.fullname()
|
|
|
|
if fullname is None:
|
|
|
|
fileName = ''
|
|
|
|
else:
|
|
|
|
fileName = fromNativePath(fullname)
|
|
|
|
|
|
|
|
if self.nativeMixed and functionName == 'qt_qmlDebugMessageAvailable':
|
|
|
|
interpreterStack = self.extractInterpreterStack()
|
|
|
|
#print('EXTRACTED INTEPRETER STACK: %s' % interpreterStack)
|
|
|
|
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)
|
|
|
|
|
|
|
|
self.put(('frame={function="%s",file="%s",'
|
|
|
|
'line="%s",language="%s",context="%s"}')
|
|
|
|
% (function, self.hexencode(fileName), lineNumber, language, context))
|
|
|
|
|
|
|
|
if False and self.isInternalInterpreterFrame(functionName):
|
|
|
|
frame = frame.older()
|
|
|
|
self.put(('frame={address="0x%x",function="%s",'
|
|
|
|
'file="%s",line="%s",'
|
|
|
|
'module="%s",language="c",usable="0"}') %
|
|
|
|
(pc, functionName, fileName, line, objfile))
|
|
|
|
i += 1
|
|
|
|
frame = frame.older()
|
|
|
|
continue
|
|
|
|
|
|
|
|
self.put(('frame={level="%s",address="0x%x",function="%s",'
|
|
|
|
'file="%s",line="%s",module="%s",language="c"}') %
|
|
|
|
(i, pc, functionName, fileName, line, objfile))
|
2014-12-05 18:45:54 +01:00
|
|
|
|
2023-10-17 13:45:40 +02:00
|
|
|
try:
|
|
|
|
# This may fail with something like
|
|
|
|
# gdb.error: DW_FORM_addr_index used without .debug_addr section
|
|
|
|
#[in module /data/dev/qt-6/qtbase/lib/libQt6Widgets.so.6]
|
|
|
|
frame = frame.older()
|
|
|
|
except:
|
|
|
|
break
|
2014-12-05 18:45:54 +01:00
|
|
|
i += 1
|
2022-11-08 12:42:04 +01:00
|
|
|
self.put(']}')
|
|
|
|
self.reportResult(self.takeOutput(), args)
|
2014-12-05 18:45:54 +01:00
|
|
|
|
2015-02-04 16:27:46 +01:00
|
|
|
def createResolvePendingBreakpointsHookBreakpoint(self, args):
|
2015-02-04 10:48:33 +01:00
|
|
|
class Resolver(gdb.Breakpoint):
|
2015-02-04 16:27:46 +01:00
|
|
|
def __init__(self, dumper, args):
|
2015-02-04 10:48:33 +01:00
|
|
|
self.dumper = dumper
|
2015-02-04 16:27:46 +01:00
|
|
|
self.args = args
|
2016-11-01 09:03:45 +01:00
|
|
|
spec = 'qt_qmlDebugConnectorOpen'
|
2015-02-04 10:48:33 +01:00
|
|
|
super(Resolver, self).\
|
|
|
|
__init__(spec, gdb.BP_BREAKPOINT, internal=True, temporary=False)
|
|
|
|
|
|
|
|
def stop(self):
|
2015-10-14 13:26:22 +02:00
|
|
|
self.dumper.resolvePendingInterpreterBreakpoint(args)
|
2015-02-04 10:48:33 +01:00
|
|
|
self.enabled = False
|
|
|
|
return False
|
|
|
|
|
2015-10-09 15:00:20 +02:00
|
|
|
self.interpreterBreakpointResolvers.append(Resolver(self, args))
|
2015-02-04 10:48:33 +01:00
|
|
|
|
2015-02-11 12:20:21 +01:00
|
|
|
def exitGdb(self, _):
|
2016-11-01 09:03:45 +01:00
|
|
|
gdb.execute('quit')
|
2015-02-04 10:48:33 +01:00
|
|
|
|
2021-09-05 09:51:08 +03:00
|
|
|
def reportResult(self, result, args):
|
2016-12-15 18:28:58 +01:00
|
|
|
print('result={token="%s",%s}' % (args.get("token", 0), result))
|
2015-03-18 16:48:57 +01:00
|
|
|
|
2015-02-11 17:51:15 +01:00
|
|
|
def profile1(self, args):
|
2016-11-01 09:03:45 +01:00
|
|
|
'''Internal profiling'''
|
2015-02-11 17:51:15 +01:00
|
|
|
import cProfile
|
2016-11-01 09:03:45 +01:00
|
|
|
tempDir = tempfile.gettempdir() + '/bbprof'
|
2015-10-08 16:19:57 +02:00
|
|
|
cProfile.run('theDumper.fetchVariables(%s)' % args, tempDir)
|
2015-02-11 17:51:15 +01:00
|
|
|
import pstats
|
|
|
|
pstats.Stats(tempDir).sort_stats('time').print_stats()
|
|
|
|
|
|
|
|
def profile2(self, args):
|
|
|
|
import timeit
|
2015-10-08 16:19:57 +02:00
|
|
|
print(timeit.repeat('theDumper.fetchVariables(%s)' % args,
|
2020-02-24 14:37:56 +01:00
|
|
|
'from __main__ import theDumper', number=10))
|
2015-02-11 17:51:15 +01:00
|
|
|
|
2021-01-07 13:28:05 +01:00
|
|
|
def tracepointModified(self, tp):
|
|
|
|
self.tpExpressions = {}
|
|
|
|
self.tpExpressionWarnings = []
|
|
|
|
s = self.resultToMi(tp.dicts())
|
|
|
|
def handler():
|
|
|
|
print("tracepointmodified=%s" % s)
|
|
|
|
gdb.post_event(handler)
|
|
|
|
|
|
|
|
def tracepointHit(self, tp, result):
|
|
|
|
expressions = '{' + ','.join(["%s=%s" % (k,v) for k,v in self.tpExpressions.items()]) + '}'
|
|
|
|
warnings = []
|
|
|
|
if 'warning' in result.keys():
|
|
|
|
warnings.append(result.pop('warning'))
|
|
|
|
warnings += self.tpExpressionWarnings
|
|
|
|
r = self.resultToMi(result)
|
|
|
|
w = self.resultToMi(warnings)
|
|
|
|
def handler():
|
|
|
|
print("tracepointhit={result=%s,expressions=%s,warnings=%s}" % (r, expressions, w))
|
|
|
|
gdb.post_event(handler)
|
|
|
|
|
|
|
|
def tracepointExpression(self, tp, expression, value, args):
|
|
|
|
key = "x" + str(len(self.tpExpressions))
|
|
|
|
if (isinstance(value, gdb.Value)):
|
|
|
|
try:
|
|
|
|
val = self.fromNativeValue(value)
|
|
|
|
self.prepare(args)
|
|
|
|
with TopLevelItem(self, expression):
|
|
|
|
self.putItem(val)
|
|
|
|
self.tpExpressions[key] = self.output
|
|
|
|
except Exception as e:
|
|
|
|
self.tpExpressions[key] = '"<N/A>"'
|
|
|
|
self.tpExpressionWarnings.append(str(e))
|
|
|
|
elif (isinstance(value, Exception)):
|
|
|
|
self.tpExpressions[key] = '"<N/A>"'
|
|
|
|
self.tpExpressionWarnings.append(str(value))
|
|
|
|
else:
|
|
|
|
self.tpExpressions[key] = '"<N/A>"'
|
|
|
|
self.tpExpressionWarnings.append('Unknown expression value type')
|
|
|
|
return key
|
|
|
|
|
|
|
|
def createTracepoint(self, args):
|
|
|
|
"""
|
|
|
|
Creates a tracepoint
|
|
|
|
"""
|
|
|
|
tp = GDBTracepoint.create(args,
|
|
|
|
onModified=self.tracepointModified,
|
|
|
|
onHit=self.tracepointHit,
|
|
|
|
onExpression=lambda tp, expr, val: self.tracepointExpression(tp, expr, val, args))
|
|
|
|
self.reportResult("tracepoint=%s" % self.resultToMi(tp.dicts()), args)
|
2022-11-08 12:42:04 +01:00
|
|
|
|
Debugger: Make dumpers somewhat work in command line GDB
With
python sys.path.insert(1, '/data/dev/creator/share/qtcreator/debugger/')
python from gdbbridge import *
in .gdbinit there's a new "GDB command", called "pp".
With code like
int main(int argc, char *argv[])
{
QString ss = "Hello";
QApplication app(argc, argv);
app.setObjectName(ss);
// break here
}
the 'pp' command can be used as follows:
(gdb) pp app
app =
[
<Myns::QGuiApplication> = {"Hello"}
staticMetaObject = <Myns::QMetaObject> = {""}
[parent] = <Myns::QObject *> = {"0x0"}
[children] = <Myns::QObjectList> = {"<3 items>"}
[properties] = "<>0 items>"
[methods] = "<6 items>"
[signals] = "<1 items>"
],<Myns::QApplication> = {"Hello"}
(gdb) pp app [properties],[children]
app =
[
<Myns::QGuiApplication> = {"Hello"}
staticMetaObject = <Myns::QMetaObject> = {""}
[parent] = <Myns::QObject *> = {"0x0"}
[children] = [
<Myns::QObject> = {""}
<Myns::QObject> = {""}
<Myns::QObject> = {"fusion"}
],<Myns::QObjectList> = {"<3 items>"}
[properties] = [
windowIcon = <Myns::QVariant (QIcon)> = {""}
cursorFlashTime = <Myns::QVariant (int)> = {"1000"}
doubleClickInterval = <Myns::QVariant (int)> = {"400"}
keyboardInputInterval = <Myns::QVariant (int)> = {"400"}
wheelScrollLines = <Myns::QVariant (int)> = {"3"}
globalStrut = <Myns::QVariant (QSize)> = {"(0, 0)"}
startDragTime = <Myns::QVariant (int)> = {"500"}
startDragDistance = <Myns::QVariant (int)> = {"10"}
styleSheet = <Myns::QVariant (QString)> = {""}
autoSipEnabled = <Myns::QVariant (bool)> = {"true"}
],"<10 items>"
[methods] = "<6 items>"
[signals] = "<1 items>"
],<Myns::QApplication> = {"Hello"}
(gdb) pp ss
ss =
<Myns::QString> = {"Hello"}
Change-Id: I6e4714a5cfe34c38917500d114ad9a70d20cff39
Reviewed-by: Christian Stenger <christian.stenger@digia.com>
Reviewed-by: hjk <hjk121@nokiamail.com>
2014-06-13 17:45:34 +02:00
|
|
|
class CliDumper(Dumper):
|
|
|
|
def __init__(self):
|
|
|
|
Dumper.__init__(self)
|
|
|
|
self.childrenPrefix = '['
|
|
|
|
self.chidrenSuffix = '] '
|
|
|
|
self.indent = 0
|
|
|
|
self.isCli = True
|
2019-03-19 15:09:37 +01:00
|
|
|
self.setupDumpers({})
|
Debugger: Make dumpers somewhat work in command line GDB
With
python sys.path.insert(1, '/data/dev/creator/share/qtcreator/debugger/')
python from gdbbridge import *
in .gdbinit there's a new "GDB command", called "pp".
With code like
int main(int argc, char *argv[])
{
QString ss = "Hello";
QApplication app(argc, argv);
app.setObjectName(ss);
// break here
}
the 'pp' command can be used as follows:
(gdb) pp app
app =
[
<Myns::QGuiApplication> = {"Hello"}
staticMetaObject = <Myns::QMetaObject> = {""}
[parent] = <Myns::QObject *> = {"0x0"}
[children] = <Myns::QObjectList> = {"<3 items>"}
[properties] = "<>0 items>"
[methods] = "<6 items>"
[signals] = "<1 items>"
],<Myns::QApplication> = {"Hello"}
(gdb) pp app [properties],[children]
app =
[
<Myns::QGuiApplication> = {"Hello"}
staticMetaObject = <Myns::QMetaObject> = {""}
[parent] = <Myns::QObject *> = {"0x0"}
[children] = [
<Myns::QObject> = {""}
<Myns::QObject> = {""}
<Myns::QObject> = {"fusion"}
],<Myns::QObjectList> = {"<3 items>"}
[properties] = [
windowIcon = <Myns::QVariant (QIcon)> = {""}
cursorFlashTime = <Myns::QVariant (int)> = {"1000"}
doubleClickInterval = <Myns::QVariant (int)> = {"400"}
keyboardInputInterval = <Myns::QVariant (int)> = {"400"}
wheelScrollLines = <Myns::QVariant (int)> = {"3"}
globalStrut = <Myns::QVariant (QSize)> = {"(0, 0)"}
startDragTime = <Myns::QVariant (int)> = {"500"}
startDragDistance = <Myns::QVariant (int)> = {"10"}
styleSheet = <Myns::QVariant (QString)> = {""}
autoSipEnabled = <Myns::QVariant (bool)> = {"true"}
],"<10 items>"
[methods] = "<6 items>"
[signals] = "<1 items>"
],<Myns::QApplication> = {"Hello"}
(gdb) pp ss
ss =
<Myns::QString> = {"Hello"}
Change-Id: I6e4714a5cfe34c38917500d114ad9a70d20cff39
Reviewed-by: Christian Stenger <christian.stenger@digia.com>
Reviewed-by: hjk <hjk121@nokiamail.com>
2014-06-13 17:45:34 +02:00
|
|
|
|
|
|
|
def put(self, line):
|
2023-06-21 13:53:04 +02:00
|
|
|
if self.output:
|
|
|
|
if self.output[-1].endswith('\n'):
|
|
|
|
self.output[-1] = self.output[-1][0:-1]
|
|
|
|
self.output.append(line)
|
Debugger: Make dumpers somewhat work in command line GDB
With
python sys.path.insert(1, '/data/dev/creator/share/qtcreator/debugger/')
python from gdbbridge import *
in .gdbinit there's a new "GDB command", called "pp".
With code like
int main(int argc, char *argv[])
{
QString ss = "Hello";
QApplication app(argc, argv);
app.setObjectName(ss);
// break here
}
the 'pp' command can be used as follows:
(gdb) pp app
app =
[
<Myns::QGuiApplication> = {"Hello"}
staticMetaObject = <Myns::QMetaObject> = {""}
[parent] = <Myns::QObject *> = {"0x0"}
[children] = <Myns::QObjectList> = {"<3 items>"}
[properties] = "<>0 items>"
[methods] = "<6 items>"
[signals] = "<1 items>"
],<Myns::QApplication> = {"Hello"}
(gdb) pp app [properties],[children]
app =
[
<Myns::QGuiApplication> = {"Hello"}
staticMetaObject = <Myns::QMetaObject> = {""}
[parent] = <Myns::QObject *> = {"0x0"}
[children] = [
<Myns::QObject> = {""}
<Myns::QObject> = {""}
<Myns::QObject> = {"fusion"}
],<Myns::QObjectList> = {"<3 items>"}
[properties] = [
windowIcon = <Myns::QVariant (QIcon)> = {""}
cursorFlashTime = <Myns::QVariant (int)> = {"1000"}
doubleClickInterval = <Myns::QVariant (int)> = {"400"}
keyboardInputInterval = <Myns::QVariant (int)> = {"400"}
wheelScrollLines = <Myns::QVariant (int)> = {"3"}
globalStrut = <Myns::QVariant (QSize)> = {"(0, 0)"}
startDragTime = <Myns::QVariant (int)> = {"500"}
startDragDistance = <Myns::QVariant (int)> = {"10"}
styleSheet = <Myns::QVariant (QString)> = {""}
autoSipEnabled = <Myns::QVariant (bool)> = {"true"}
],"<10 items>"
[methods] = "<6 items>"
[signals] = "<1 items>"
],<Myns::QApplication> = {"Hello"}
(gdb) pp ss
ss =
<Myns::QString> = {"Hello"}
Change-Id: I6e4714a5cfe34c38917500d114ad9a70d20cff39
Reviewed-by: Christian Stenger <christian.stenger@digia.com>
Reviewed-by: hjk <hjk121@nokiamail.com>
2014-06-13 17:45:34 +02:00
|
|
|
|
|
|
|
def putNumChild(self, numchild):
|
|
|
|
pass
|
|
|
|
|
2016-10-25 15:32:13 +02:00
|
|
|
def putOriginalAddress(self, address):
|
Debugger: Make dumpers somewhat work in command line GDB
With
python sys.path.insert(1, '/data/dev/creator/share/qtcreator/debugger/')
python from gdbbridge import *
in .gdbinit there's a new "GDB command", called "pp".
With code like
int main(int argc, char *argv[])
{
QString ss = "Hello";
QApplication app(argc, argv);
app.setObjectName(ss);
// break here
}
the 'pp' command can be used as follows:
(gdb) pp app
app =
[
<Myns::QGuiApplication> = {"Hello"}
staticMetaObject = <Myns::QMetaObject> = {""}
[parent] = <Myns::QObject *> = {"0x0"}
[children] = <Myns::QObjectList> = {"<3 items>"}
[properties] = "<>0 items>"
[methods] = "<6 items>"
[signals] = "<1 items>"
],<Myns::QApplication> = {"Hello"}
(gdb) pp app [properties],[children]
app =
[
<Myns::QGuiApplication> = {"Hello"}
staticMetaObject = <Myns::QMetaObject> = {""}
[parent] = <Myns::QObject *> = {"0x0"}
[children] = [
<Myns::QObject> = {""}
<Myns::QObject> = {""}
<Myns::QObject> = {"fusion"}
],<Myns::QObjectList> = {"<3 items>"}
[properties] = [
windowIcon = <Myns::QVariant (QIcon)> = {""}
cursorFlashTime = <Myns::QVariant (int)> = {"1000"}
doubleClickInterval = <Myns::QVariant (int)> = {"400"}
keyboardInputInterval = <Myns::QVariant (int)> = {"400"}
wheelScrollLines = <Myns::QVariant (int)> = {"3"}
globalStrut = <Myns::QVariant (QSize)> = {"(0, 0)"}
startDragTime = <Myns::QVariant (int)> = {"500"}
startDragDistance = <Myns::QVariant (int)> = {"10"}
styleSheet = <Myns::QVariant (QString)> = {""}
autoSipEnabled = <Myns::QVariant (bool)> = {"true"}
],"<10 items>"
[methods] = "<6 items>"
[signals] = "<1 items>"
],<Myns::QApplication> = {"Hello"}
(gdb) pp ss
ss =
<Myns::QString> = {"Hello"}
Change-Id: I6e4714a5cfe34c38917500d114ad9a70d20cff39
Reviewed-by: Christian Stenger <christian.stenger@digia.com>
Reviewed-by: hjk <hjk121@nokiamail.com>
2014-06-13 17:45:34 +02:00
|
|
|
pass
|
|
|
|
|
2020-05-29 13:08:07 +02:00
|
|
|
def fetchVariable(self, line):
|
|
|
|
# HACK: Currently, the response to the QtCore loading is completely
|
|
|
|
# eaten by theDumper, so copy the results here. Better would be
|
|
|
|
# some shared component.
|
|
|
|
self.qtCustomEventFunc = theDumper.qtCustomEventFunc
|
|
|
|
self.qtCustomEventPltFunc = theDumper.qtCustomEventPltFunc
|
|
|
|
self.qtPropertyFunc = theDumper.qtPropertyFunc
|
|
|
|
|
|
|
|
names = line.split(' ')
|
|
|
|
name = names[0]
|
|
|
|
|
|
|
|
toExpand = set()
|
|
|
|
for n in names:
|
|
|
|
while n:
|
|
|
|
toExpand.add(n)
|
2023-01-08 16:12:15 +01:00
|
|
|
try:
|
|
|
|
n = n[0:n.rindex('.')]
|
|
|
|
except ValueError:
|
|
|
|
break
|
2020-05-29 13:08:07 +02:00
|
|
|
|
2019-03-19 15:09:37 +01:00
|
|
|
args = {}
|
2015-04-15 12:38:11 +02:00
|
|
|
args['fancy'] = 1
|
2020-06-02 14:16:36 +02:00
|
|
|
args['passexceptions'] = 1
|
2015-04-15 12:38:11 +02:00
|
|
|
args['autoderef'] = 1
|
2015-12-16 14:13:44 +01:00
|
|
|
args['qobjectnames'] = 1
|
2019-03-19 15:09:37 +01:00
|
|
|
args['varlist'] = name
|
2020-05-29 13:08:07 +02:00
|
|
|
args['expanded'] = toExpand
|
|
|
|
self.expandableINames = set()
|
2015-04-15 12:38:11 +02:00
|
|
|
self.prepare(args)
|
2020-05-29 13:08:07 +02:00
|
|
|
|
2022-11-08 12:42:04 +01:00
|
|
|
self.output = []
|
|
|
|
self.put(name + ' = ')
|
2019-03-19 15:09:37 +01:00
|
|
|
value = self.parseAndEvaluate(name)
|
Debugger: Make dumpers somewhat work in command line GDB
With
python sys.path.insert(1, '/data/dev/creator/share/qtcreator/debugger/')
python from gdbbridge import *
in .gdbinit there's a new "GDB command", called "pp".
With code like
int main(int argc, char *argv[])
{
QString ss = "Hello";
QApplication app(argc, argv);
app.setObjectName(ss);
// break here
}
the 'pp' command can be used as follows:
(gdb) pp app
app =
[
<Myns::QGuiApplication> = {"Hello"}
staticMetaObject = <Myns::QMetaObject> = {""}
[parent] = <Myns::QObject *> = {"0x0"}
[children] = <Myns::QObjectList> = {"<3 items>"}
[properties] = "<>0 items>"
[methods] = "<6 items>"
[signals] = "<1 items>"
],<Myns::QApplication> = {"Hello"}
(gdb) pp app [properties],[children]
app =
[
<Myns::QGuiApplication> = {"Hello"}
staticMetaObject = <Myns::QMetaObject> = {""}
[parent] = <Myns::QObject *> = {"0x0"}
[children] = [
<Myns::QObject> = {""}
<Myns::QObject> = {""}
<Myns::QObject> = {"fusion"}
],<Myns::QObjectList> = {"<3 items>"}
[properties] = [
windowIcon = <Myns::QVariant (QIcon)> = {""}
cursorFlashTime = <Myns::QVariant (int)> = {"1000"}
doubleClickInterval = <Myns::QVariant (int)> = {"400"}
keyboardInputInterval = <Myns::QVariant (int)> = {"400"}
wheelScrollLines = <Myns::QVariant (int)> = {"3"}
globalStrut = <Myns::QVariant (QSize)> = {"(0, 0)"}
startDragTime = <Myns::QVariant (int)> = {"500"}
startDragDistance = <Myns::QVariant (int)> = {"10"}
styleSheet = <Myns::QVariant (QString)> = {""}
autoSipEnabled = <Myns::QVariant (bool)> = {"true"}
],"<10 items>"
[methods] = "<6 items>"
[signals] = "<1 items>"
],<Myns::QApplication> = {"Hello"}
(gdb) pp ss
ss =
<Myns::QString> = {"Hello"}
Change-Id: I6e4714a5cfe34c38917500d114ad9a70d20cff39
Reviewed-by: Christian Stenger <christian.stenger@digia.com>
Reviewed-by: hjk <hjk121@nokiamail.com>
2014-06-13 17:45:34 +02:00
|
|
|
with TopLevelItem(self, name):
|
|
|
|
self.putItem(value)
|
2020-05-29 13:08:07 +02:00
|
|
|
|
|
|
|
if not self.expandableINames:
|
2022-11-08 12:42:04 +01:00
|
|
|
self.put('\n\nNo drill down available.\n')
|
|
|
|
return self.takeOutput()
|
2020-05-29 13:08:07 +02:00
|
|
|
|
|
|
|
pattern = ' pp ' + name + ' ' + '%s'
|
2022-11-08 12:42:04 +01:00
|
|
|
return (self.takeOutput()
|
2020-05-29 13:08:07 +02:00
|
|
|
+ '\n\nDrill down:\n '
|
|
|
|
+ '\n '.join(pattern % x for x in self.expandableINames)
|
|
|
|
+ '\n')
|
Debugger: Make dumpers somewhat work in command line GDB
With
python sys.path.insert(1, '/data/dev/creator/share/qtcreator/debugger/')
python from gdbbridge import *
in .gdbinit there's a new "GDB command", called "pp".
With code like
int main(int argc, char *argv[])
{
QString ss = "Hello";
QApplication app(argc, argv);
app.setObjectName(ss);
// break here
}
the 'pp' command can be used as follows:
(gdb) pp app
app =
[
<Myns::QGuiApplication> = {"Hello"}
staticMetaObject = <Myns::QMetaObject> = {""}
[parent] = <Myns::QObject *> = {"0x0"}
[children] = <Myns::QObjectList> = {"<3 items>"}
[properties] = "<>0 items>"
[methods] = "<6 items>"
[signals] = "<1 items>"
],<Myns::QApplication> = {"Hello"}
(gdb) pp app [properties],[children]
app =
[
<Myns::QGuiApplication> = {"Hello"}
staticMetaObject = <Myns::QMetaObject> = {""}
[parent] = <Myns::QObject *> = {"0x0"}
[children] = [
<Myns::QObject> = {""}
<Myns::QObject> = {""}
<Myns::QObject> = {"fusion"}
],<Myns::QObjectList> = {"<3 items>"}
[properties] = [
windowIcon = <Myns::QVariant (QIcon)> = {""}
cursorFlashTime = <Myns::QVariant (int)> = {"1000"}
doubleClickInterval = <Myns::QVariant (int)> = {"400"}
keyboardInputInterval = <Myns::QVariant (int)> = {"400"}
wheelScrollLines = <Myns::QVariant (int)> = {"3"}
globalStrut = <Myns::QVariant (QSize)> = {"(0, 0)"}
startDragTime = <Myns::QVariant (int)> = {"500"}
startDragDistance = <Myns::QVariant (int)> = {"10"}
styleSheet = <Myns::QVariant (QString)> = {""}
autoSipEnabled = <Myns::QVariant (bool)> = {"true"}
],"<10 items>"
[methods] = "<6 items>"
[signals] = "<1 items>"
],<Myns::QApplication> = {"Hello"}
(gdb) pp ss
ss =
<Myns::QString> = {"Hello"}
Change-Id: I6e4714a5cfe34c38917500d114ad9a70d20cff39
Reviewed-by: Christian Stenger <christian.stenger@digia.com>
Reviewed-by: hjk <hjk121@nokiamail.com>
2014-06-13 17:45:34 +02:00
|
|
|
|
2019-03-19 15:09:37 +01:00
|
|
|
|
|
|
|
# Global instances.
|
2015-09-25 08:45:20 +02:00
|
|
|
theDumper = Dumper()
|
2019-03-19 15:09:37 +01:00
|
|
|
theCliDumper = CliDumper()
|
|
|
|
|
2013-10-30 12:38:29 +01:00
|
|
|
|
2015-02-11 17:51:15 +01:00
|
|
|
######################################################################
|
2013-10-30 12:38:29 +01:00
|
|
|
#
|
|
|
|
# ThreadNames Command
|
|
|
|
#
|
|
|
|
#######################################################################
|
2013-06-06 18:28:53 +02:00
|
|
|
|
|
|
|
def threadnames(arg):
|
2013-10-30 12:38:29 +01:00
|
|
|
return theDumper.threadnames(int(arg))
|
2013-05-15 15:42:55 +02:00
|
|
|
|
2020-02-24 14:37:56 +01:00
|
|
|
|
2016-11-01 09:03:45 +01:00
|
|
|
registerCommand('threadnames', threadnames)
|
2013-05-15 15:42:55 +02:00
|
|
|
|
2015-01-22 12:05:00 +01:00
|
|
|
#######################################################################
|
|
|
|
#
|
|
|
|
# Native Mixed
|
|
|
|
#
|
|
|
|
#######################################################################
|
|
|
|
|
2020-02-24 14:37:56 +01:00
|
|
|
|
2015-10-14 13:26:22 +02:00
|
|
|
class InterpreterMessageBreakpoint(gdb.Breakpoint):
|
2015-10-08 16:19:57 +02:00
|
|
|
def __init__(self):
|
2016-11-01 09:03:45 +01:00
|
|
|
spec = 'qt_qmlDebugMessageAvailable'
|
2015-10-14 13:26:22 +02:00
|
|
|
super(InterpreterMessageBreakpoint, self).\
|
2015-10-08 16:19:57 +02:00
|
|
|
__init__(spec, gdb.BP_BREAKPOINT, internal=True)
|
|
|
|
|
|
|
|
def stop(self):
|
2016-11-01 09:03:45 +01:00
|
|
|
print('Interpreter event received.')
|
2015-10-14 13:26:22 +02:00
|
|
|
return theDumper.handleInterpreterMessage()
|
2015-10-08 16:19:57 +02:00
|
|
|
|
2017-03-15 09:19:12 +01:00
|
|
|
|
|
|
|
#######################################################################
|
|
|
|
#
|
|
|
|
# Shared objects
|
|
|
|
#
|
|
|
|
#######################################################################
|
|
|
|
|
|
|
|
def new_objfile_handler(event):
|
|
|
|
return theDumper.handleNewObjectFile(event.new_objfile)
|
|
|
|
|
2020-02-24 14:37:56 +01:00
|
|
|
|
2017-03-15 09:19:12 +01:00
|
|
|
gdb.events.new_objfile.connect(new_objfile_handler)
|
|
|
|
|
|
|
|
|
2016-10-25 15:32:13 +02:00
|
|
|
#InterpreterMessageBreakpoint()
|