Debugger: Avoid quadratic behavior for larger debug output

Task-number: QTCREATORBUG-28111
Change-Id: I0567b6af9f74c2d335d19a52765834ef7cee9449
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: David Schulz <david.schulz@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
hjk
2022-11-08 12:42:04 +01:00
parent bacb05e285
commit bb11788a0a
4 changed files with 82 additions and 93 deletions
+63 -77
View File
@@ -175,24 +175,6 @@ def importPlainDumpers(args):
registerCommand('importPlainDumpers', importPlainDumpers)
class OutputSaver():
def __init__(self, d):
self.d = d
def __enter__(self):
self.savedOutput = self.d.output
self.d.output = ''
def __exit__(self, exType, exValue, exTraceBack):
if self.d.passExceptions and exType is not None:
self.d.showException('OUTPUTSAVER', exType, exValue, exTraceBack)
self.d.output = self.savedOutput
else:
self.savedOutput += self.d.output
self.d.output = self.savedOutput
return False
#######################################################################
#
# The Dumper Class
@@ -214,7 +196,7 @@ class Dumper(DumperBase):
self.interpreterBreakpointResolvers = []
def prepare(self, args):
self.output = ''
self.output = []
self.setVariableFetchingOptions(args)
def fromFrameValue(self, nativeValue):
@@ -690,7 +672,7 @@ class Dumper(DumperBase):
safePrint(res)
return
self.output += 'data=['
self.put('data=[')
partialVar = args.get('partialvar', '')
isPartial = len(partialVar) > 0
@@ -713,27 +695,26 @@ class Dumper(DumperBase):
self.handleLocals(variables)
self.handleWatches(args)
self.output += '],typeinfo=['
self.put('],typeinfo=[')
for name in self.typesToReport.keys():
typeobj = self.typesToReport[name]
# Happens e.g. for '(anonymous namespace)::InsertDefOperation'
#if not typeobj is None:
# self.output.append('{name="%s",size="%s"}'
# % (self.hexencode(name), typeobj.sizeof))
self.output += ']'
# self.put('{name="%s",size="%s"}' % (self.hexencode(name), typeobj.sizeof))
self.put(']')
self.typesToReport = {}
if self.forceQtNamespace:
self.qtNamespaceToReport = self.qtNamespace()
if self.qtNamespaceToReport:
self.output += ',qtnamespace="%s"' % self.qtNamespaceToReport
self.put(',qtnamespace="%s"' % self.qtNamespaceToReport)
self.qtNamespaceToReport = None
self.output += ',partial="%d"' % isPartial
self.output += ',counts=%s' % self.counts
self.output += ',timings=%s' % self.timings
self.reportResult(self.output, args)
self.put(',partial="%d"' % isPartial)
self.put(',counts=%s' % self.counts)
self.put(',timings=%s' % self.timings)
self.reportResult(''.join(self.output), args)
def parseAndEvaluate(self, exp):
val = self.nativeParseAndEvaluate(exp)
@@ -1321,7 +1302,7 @@ class Dumper(DumperBase):
limit = 10000
self.prepare(args)
self.output = ''
self.output = []
i = 0
if extraQml:
@@ -1375,58 +1356,60 @@ class Dumper(DumperBase):
frame = gdb.newest_frame()
self.currentCallContext = None
self.output = []
self.put('stack={frames=[')
while i < limit and frame:
with OutputSaver(self):
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)
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)
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))
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
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))
self.put(('frame={level="%s",address="0x%x",function="%s",'
'file="%s",line="%s",module="%s",language="c"}') %
(i, pc, functionName, fileName, line, objfile))
frame = frame.older()
i += 1
self.reportResult('stack={frames=[' + self.output + ']}', args)
self.put(']}')
self.reportResult(self.takeOutput(), args)
def createResolvePendingBreakpointsHookBreakpoint(self, args):
class Resolver(gdb.Breakpoint):
@@ -1512,6 +1495,7 @@ class Dumper(DumperBase):
onHit=self.tracepointHit,
onExpression=lambda tp, expr, val: self.tracepointExpression(tp, expr, val, args))
self.reportResult("tracepoint=%s" % self.resultToMi(tp.dicts()), args)
class CliDumper(Dumper):
def __init__(self):
Dumper.__init__(self)
@@ -1559,16 +1543,18 @@ class CliDumper(Dumper):
self.expandableINames = set()
self.prepare(args)
self.output = name + ' = '
self.output = []
self.put(name + ' = ')
value = self.parseAndEvaluate(name)
with TopLevelItem(self, name):
self.putItem(value)
if not self.expandableINames:
return self.output + '\n\nNo drill down available.\n'
self.put('\n\nNo drill down available.\n')
return self.takeOutput()
pattern = ' pp ' + name + ' ' + '%s'
return (self.output
return (self.takeOutput()
+ '\n\nDrill down:\n '
+ '\n '.join(pattern % x for x in self.expandableINames)
+ '\n')