#!/usr/bin/python3 # -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- # # This file is part of the LibreOffice project. # # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. #
### script to help debug leaks of reference counted objects
## I. to use it, first override acquire() and release()
# void Foo::acquire() # if (this == g_pTrackedFoo) # ; // set gdb breakpoint here # Foo_Base::acquire()
# void Foo::release() # if (this == g_pTrackedFoo) # ; // set gdb breakpoint here # Foo_Base::release()
## II. run test/soffice in gdb and set breakpoints in acquire/release ## with a command to print the backtrace
# set logging enabled on # break foo.cxx:123 # break foo.cxx:234
# command 1 2 # bt # c # end # run
## III. now feed logfile gdb.txt into this script
# bin/refcount_leak.py < gdb.txt
###
from operator import itemgetter import re import sys
threshold = 2
class Trace:
clock = 0 # global counter # frames: list of stack frames, beginning with outermost def __init__(self, lines):
lines.reverse()
self.frames = lines
Trace.clock += 1
self.clock = Trace.clock
def addTrace(traces, lines): if traces isnotNoneand len(lines) > 0:
traces.append(Trace(lines))
def readGdbLog(infile):
traces_acquire = []
traces_release = []
current = None
lines = []
apattern = re.compile("^Breakpoint.*::acquire")
rpattern = re.compile("^Breakpoint.*::release") for line in infile: if apattern.match(line):
addTrace(current, lines)
lines = []
current = traces_acquire if rpattern.match(line):
addTrace(current, lines)
lines = []
current = traces_release if line.startswith("#"): # strip #123 stack frame number, and newline
lines.append(line[line.index("0x"):-1])
addTrace(current, lines)
print("# parsed traces acquire: ", len(traces_acquire))
print("# parsed traces release: ", len(traces_release)) return (traces_acquire, traces_release)
def getFunction(frame):
start = frame.index(" in ") + len(" in ") try:
end = frame.index(" at ", start) except ValueError: # argh... stack frames may be split across multiple lines if # a parameter has a fancy pretty printer return frame[start:] return frame[start:end]
def matchStack(trace_acquire, trace_release): if trace_release.clock < trace_acquire.clock: returnNone# acquire must precede release
common = 0
refpattern = re.compile(r"::Reference<.*>::Reference\(") for (frame1, frame2) in zip(trace_release.frames, trace_acquire.frames): if frame1 == frame2:
common += 1 else: if getFunction(frame1) == getFunction(frame2):
common += 1
acquireframes = len(trace_acquire.frames) # there is sometimes a dozen frames of UNO type related junk # on the stack where the acquire() happens, which breaks the # matching; try to avoid that for i in range(common, acquireframes): if refpattern.search(trace_acquire.frames[i]):
acquireframes = i+1 # cut off junk above Reference ctor break
score = max(len(trace_release.frames), acquireframes) - common # smaller score is better return (score, trace_release.clock - trace_acquire.clock)
# brute force greedy n^2 matching def matchStacks(traces_acquire, traces_release):
matches = [] for release in traces_release: for acquire in traces_acquire:
score = matchStack(acquire, release) if score isnotNone:
matches.append((score, acquire, release))
matches.sort(key=itemgetter(0)) return matches
def bestMatches(traces_acquire, traces_release, matches):
traces_aunmatched = traces_acquire
traces_runmatched = traces_release
bestmatches = [] for (score,acquire,release) in matches: ifnot(acquire in traces_aunmatched and release in traces_runmatched): continue
traces_aunmatched.remove(acquire)
traces_runmatched.remove(release)
bestmatches.append((score,acquire,release))
print("# unmatched acquire: ", len(traces_aunmatched))
print("# unmatched release: ", len(traces_runmatched)) return (bestmatches,traces_aunmatched,traces_runmatched)
def printTrace(trace): for frame in reversed(trace.frames):
print(" ", frame)
def printMatched(bestmatches): for (score,acquire,release) in reversed(bestmatches):
print("\n*** Matched trace with score: ", score)
print(" acquire: ")
printTrace(acquire)
print(" release: ")
printTrace(release)
def printUnmatched(traces, prefix): for trace in traces:
print("\n*** Unmatched trace (", prefix, "):")
printTrace(trace)
if __name__ == "__main__":
(traces_acquire, traces_release) = readGdbLog(sys.stdin)
matches = matchStacks(traces_acquire, traces_release)
(bestmatches,traces_au,traces_ru) = bestMatches(traces_acquire, traces_release, matches) # print output, sorted with the most suspicious stuff first:
printUnmatched(traces_au, "acquire")
printUnmatched(traces_ru, "release")
printMatched(bestmatches)
# vim:set shiftwidth=4 softtabstop=4 expandtab:
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.