forked from qt-creator/qt-creator
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:
@@ -353,9 +353,6 @@ class Dumper(DumperBase):
|
||||
self.ptrSize = lambda: size
|
||||
return size
|
||||
|
||||
def put(self, stuff):
|
||||
self.output += stuff
|
||||
|
||||
def stripQintTypedefs(self, typeName):
|
||||
if typeName.startswith('qint'):
|
||||
prefix = ''
|
||||
@@ -423,7 +420,7 @@ class Dumper(DumperBase):
|
||||
|
||||
self.setVariableFetchingOptions(args)
|
||||
|
||||
self.output = ''
|
||||
self.output = []
|
||||
|
||||
self.currentIName = 'local'
|
||||
self.put('data=[')
|
||||
@@ -445,10 +442,11 @@ class Dumper(DumperBase):
|
||||
self.qtNamespaceToReport = self.qtNamespace()
|
||||
|
||||
if self.qtNamespaceToReport:
|
||||
self.output += ',qtnamespace="%s"' % self.qtNamespaceToReport
|
||||
self.put(',qtnamespace="%s"' % self.qtNamespaceToReport)
|
||||
self.qtNamespaceToReport = None
|
||||
|
||||
self.reportResult(self.output, args)
|
||||
self.reportResult(''.join(self.output), args)
|
||||
self.output = []
|
||||
|
||||
def report(self, stuff):
|
||||
sys.stdout.write(stuff + "\n")
|
||||
|
||||
@@ -112,7 +112,7 @@ class Children():
|
||||
self.d.currentNumChild = self.savedNumChild
|
||||
self.d.currentMaxNumChild = self.savedMaxNumChild
|
||||
if self.d.isCli:
|
||||
self.d.output += '\n' + ' ' * self.d.indent
|
||||
self.d.put('\n' + ' ' * self.d.indent)
|
||||
self.d.put(self.d.childrenSuffix)
|
||||
return True
|
||||
|
||||
@@ -173,7 +173,7 @@ class DumperBase():
|
||||
self.displayStringLimit = 100
|
||||
self.useTimeStamps = False
|
||||
|
||||
self.output = ''
|
||||
self.output = []
|
||||
self.typesReported = {}
|
||||
self.typesToReport = {}
|
||||
self.qtNamespaceToReport = None
|
||||
@@ -309,9 +309,9 @@ class DumperBase():
|
||||
self.putField('name', item.name)
|
||||
else:
|
||||
self.indent += 1
|
||||
self.output += '\n' + ' ' * self.indent
|
||||
self.put('\n' + ' ' * self.indent)
|
||||
if isinstance(item.name, str):
|
||||
self.output += item.name + ' = '
|
||||
self.put(item.name + ' = ')
|
||||
item.savedIName = self.currentIName
|
||||
item.savedValue = self.currentValue
|
||||
item.savedType = self.currentType
|
||||
@@ -869,7 +869,12 @@ class DumperBase():
|
||||
self.putPlainChildren(value)
|
||||
|
||||
def put(self, stuff):
|
||||
self.output += stuff
|
||||
self.output.append(stuff)
|
||||
|
||||
def takeOutput(self):
|
||||
res = '\n'.join(self.output)
|
||||
self.output = []
|
||||
return res
|
||||
|
||||
def check(self, exp):
|
||||
if not exp:
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -1270,7 +1270,7 @@ class Dumper(DumperBase):
|
||||
self.reportResult('error="No frame"', args)
|
||||
return
|
||||
|
||||
self.output = ''
|
||||
self.output = []
|
||||
isPartial = len(self.partialVariable) > 0
|
||||
|
||||
self.currentIName = 'local'
|
||||
@@ -1323,7 +1323,7 @@ class Dumper(DumperBase):
|
||||
self.handleWatches(args)
|
||||
|
||||
self.put('],partial="%d"' % isPartial)
|
||||
self.reportResult(self.output, args)
|
||||
self.reportResult(self.takeOutput(), args)
|
||||
|
||||
|
||||
def fetchRegisters(self, args=None):
|
||||
@@ -2101,7 +2101,7 @@ class SummaryDumper(Dumper, LogMixin):
|
||||
|
||||
self.dumpermodules = ['qttypes']
|
||||
self.loadDumpers({})
|
||||
self.output = ''
|
||||
self.output = []
|
||||
|
||||
def report(self, stuff):
|
||||
return # Don't mess up lldb output
|
||||
@@ -2123,12 +2123,12 @@ class SummaryDumper(Dumper, LogMixin):
|
||||
self.expandedINames = [value.name] if expanded else []
|
||||
|
||||
savedOutput = self.output
|
||||
self.output = ''
|
||||
self.output = []
|
||||
with TopLevelItem(self, value.name):
|
||||
self.putItem(value)
|
||||
|
||||
# FIXME: Hook into putField, etc to build up object instead of parsing MI
|
||||
response = gdbmiparser.parse_response("^ok,summary=%s" % self.output)
|
||||
response = gdbmiparser.parse_response("^ok,summary=%s" % self.takeOutput()))
|
||||
|
||||
self.output = savedOutput
|
||||
self.expandedINames = oldExpanded
|
||||
|
||||
Reference in New Issue
Block a user