You need to sign in or sign up before continuing.
fsrefs.py 2.99 KB
Newer Older
1
#!python
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
# 
##############################################################################

"""Check FileStorage for dangling references.

usage: fsrefs.py data.fs

This script ignores versions, which might produce incorrect results
for storages that use versions.
"""

from ZODB.FileStorage import FileStorage
Jeremy Hylton's avatar
Jeremy Hylton committed
26
from ZODB.TimeStamp import TimeStamp
27 28 29 30 31
from ZODB.utils import u64
from ZODB.fsdump import get_pickle_metadata

import cPickle
import cStringIO
32
import traceback
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
import types

def get_refs(pickle):
    refs = []
    f = cStringIO.StringIO(pickle)
    u = cPickle.Unpickler(f)
    u.persistent_load = refs
    u.noload()
    u.noload()
    return refs

def report(oid, data, serial, fs, missing):
    from_mod, from_class = get_pickle_metadata(data)
    if len(missing) > 1:
        plural = "s"
    else:
        plural = ""
Jeremy Hylton's avatar
Jeremy Hylton committed
50
    ts = TimeStamp(serial)
51
    print "oid %s %s.%s" % (hex(u64(oid)), from_mod, from_class)
Jeremy Hylton's avatar
Jeremy Hylton committed
52
    print "last updated: %s, tid=%s" % (ts, hex(u64(serial)))
53 54
    print "refers to invalid object%s:" % plural
    for oid, info, reason in missing:
55 56 57 58
        if isinstance(info, types.TupleType):
            description = "%s.%s" % info
        else:
            description = str(info)
59
        print "\toid %s %s: %s" % (hex(u64(oid)), reason, description)
Jeremy Hylton's avatar
Jeremy Hylton committed
60
    print
61 62 63

def main(path):
    fs = FileStorage(path, read_only=1)
64
    noload = {}
65
    for oid in fs._index.keys():
66 67 68 69 70 71 72 73 74 75 76 77
        try:
            data, serial = fs.load(oid, "")
        except:
            print "oid %s failed to load" % hex(u64(oid))
            traceback.print_exc()
            noload[oid] = 1

            # XXX If we get here after we've already loaded objects
            # that refer to this one, we won't get error reports from
            # them.  We could fix this by making two passes over the
            # storage, but that seems like overkill.
            
78
        refs = get_refs(data)
79 80 81 82 83 84 85 86
        missing = [] # contains 3-tuples of oid, klass-metadata, reason
        for info in refs:
            try:
                ref, klass = info
            except TypeError:
                # failed to unpack
                ref = info
                klass = '<unknown>'
87
            if not fs._index.has_key(ref):
88 89 90
                missing.append((ref, klass, "missing"))
            if noload.has_key(ref):
                missing.append((ref, klass, "failed to load"))
91 92 93 94 95 96
        if missing:
            report(oid, data, serial, fs, missing)

if __name__ == "__main__":
    import sys
    main(sys.argv[1])