public inbox for cluster-cvs@sourceware.org
help / color / mirror / Atom feed
* master - build: create contrib/ top level section
@ 2008-08-20 19:26 Andrew Price
0 siblings, 0 replies; only message in thread
From: Andrew Price @ 2008-08-20 19:26 UTC (permalink / raw)
To: cluster-cvs-relay
Gitweb: http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=139fb344064623f0edd93ffbf1897915cbb13fcb
Commit: 139fb344064623f0edd93ffbf1897915cbb13fcb
Parent: d5d8e39eef7802a7d314cafe112deee0133a8227
Author: Fabio M. Di Nitto <fdinitto@redhat.com>
AuthorDate: Wed Aug 20 10:04:30 2008 +0200
Committer: Fabio M. Di Nitto <fdinitto@redhat.com>
CommitterDate: Wed Aug 20 12:28:31 2008 +0200
build: create contrib/ top level section
now that the project is more open to the community, create
a top level contrib section where to add community contribution.
change build system to cope with it and make it disabled by default.
add option --enable_contrib to top level configure
move askant into contrib/
Signed-off-by: Fabio M. Di Nitto <fdinitto@redhat.com>
---
Makefile | 10 +-
askant/INSTALL | 42 ---
askant/PLUGINAPI | 65 -----
askant/README | 74 -----
askant/askant/about.py | 5 -
askant/askant/askant.py | 24 --
askant/askant/blktrace.py | 93 -------
askant/askant/commands.py | 333 -----------------------
askant/askant/sysfs.py | 86 ------
askant/fsplugins/gfs2/gfs2.c | 405 ----------------------------
askant/fsplugins/gfs2/gfs2.h | 3 -
askant/fsplugins/gfs2/gfs2module.c | 104 -------
askant/scripts/askant | 6 -
askant/setup.py | 18 --
configure | 7 +
contrib/askant/INSTALL | 42 +++
contrib/askant/PLUGINAPI | 65 +++++
contrib/askant/README | 74 +++++
contrib/askant/askant/about.py | 5 +
contrib/askant/askant/askant.py | 24 ++
contrib/askant/askant/blktrace.py | 93 +++++++
contrib/askant/askant/commands.py | 333 +++++++++++++++++++++++
contrib/askant/askant/sysfs.py | 86 ++++++
contrib/askant/fsplugins/gfs2/gfs2.c | 405 ++++++++++++++++++++++++++++
contrib/askant/fsplugins/gfs2/gfs2.h | 3 +
contrib/askant/fsplugins/gfs2/gfs2module.c | 104 +++++++
contrib/askant/scripts/askant | 6 +
contrib/askant/setup.py | 18 ++
make/defines.mk.input | 1 +
29 files changed, 1274 insertions(+), 1260 deletions(-)
diff --git a/Makefile b/Makefile
index 30e3838..de16e70 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,8 @@ include make/defines.mk
REALSUBDIRS = gnbd-kernel/src gfs-kernel/src/gfs \
cman/lib config cman dlm fence/libfenced group \
- fence gfs gfs2 gnbd rgmanager bindings doc
+ fence gfs gfs2 gnbd rgmanager bindings doc \
+ contrib
SUBDIRS = $(filter-out \
$(if ${without_gnbd-kernel/src},gnbd-kernel/src) \
@@ -43,6 +44,7 @@ gfs2: group
gnbd: cman
rgmanager: cman dlm
bindings: cman
+contrib: gfs2
oldconfig:
@if [ -f $(OBJDIR)/.configure.sh ]; then \
@@ -58,7 +60,11 @@ uninstall:
set -e && for i in ${SUBDIRS}; do ${MAKE} -C $$i $@; done
clean:
- set -e && for i in ${REALSUBDIRS}; do legacy_code=1 ${MAKE} -C $$i $@; done
+ set -e && for i in ${REALSUBDIRS}; do \
+ contrib_code=1 \
+ legacy_code=1 \
+ ${MAKE} -C $$i $@;\
+ done
distclean: clean
rm -f make/defines.mk
diff --git a/askant/INSTALL b/askant/INSTALL
deleted file mode 100644
index ce26b82..0000000
--- a/askant/INSTALL
+++ /dev/null
@@ -1,42 +0,0 @@
-
- How To Install Askant
- ---------------------
-
-o Build Dependencies
- - Python
- - libgfs2 (part of the Red Hat cluster suite as a static lib)
-
-o Dependencies
- - Python
- - blktrace and blkparse with appropriate kernel config options enabled and
- debugfs mounted
-
-o Intro
- Askant uses Python's distutils for its standard installation procedure so the
- setup.py script provides installation routines. Note that it doesn't provide
- uninstallation routines so the best way to install it is to obtain a package
- for your particular distribution and install that.
-
-o Installation
- To install askant to the default location on your system, run as root:
-
- ./setup.py install
-
- If you wish to install it to a different root directory (e.g. /var/mychroot)
- or prefix directory (e.g. /usr/local instead of /usr) see:
-
- ./setup.py install --help
-
- To find out everything else setup.py can do:
-
- ./setup.py --help-commands
-
-o Use Without Installing
- To use askant without installing it (e.g. if you want to test patches
- quickly) it is necessary to carry out the build stage to compile the required
- plugins. Do:
-
- ./setup.py build
- cd build/lib*
- python askant/askant.py
-
diff --git a/askant/PLUGINAPI b/askant/PLUGINAPI
deleted file mode 100644
index 4c2eceb..0000000
--- a/askant/PLUGINAPI
+++ /dev/null
@@ -1,65 +0,0 @@
-
- Writing File System Plugins for Askant
- --------------------------------------
-
-0. Contents
-
- 1. Intro
- 2. API
- 3. Reference
-
-
-1. Intro
-
-In order to gather file system data with which to enhance the data provided by
-blktrace, there must be specific code written for each file system type we wish
-to support. This is where file system plugins come along. The plugins must
-basically traverse an on-disk file system structure on a block device and report
-back their findings through a callback handed to them by askant. They must also
-expose a function which stops their traversal loop in case askant wishes to stop
-their execution prematurely.
-
-
-2. API
-
-A file system plugin must be written as a Python module. This means that C, C++
-and Python are suitable languages to write them in. They must expose a number of
-Python functions to askant:
-
-parsefs(dev)
-
- This function is called when askant wants the plugin to parse the file
- system on the device specified by the device, dev.
-
-get_block_size()
-
- This function is called to ascertain the file system block size which is
- required in order to map the disk sector numbers provided by blktrace to
- file system block numbers. It takes no arguments and should return a Python
- integer value.
-
-set_report_hook(func)
-
- This function is called to pass in a report function which the plugin should
- call to report the details of a file system block. func is a function which
- accepts four arguments in this order:
-
- Block number: a Python long integer
- Block type: a Python string
- Block number of parent: a Python long integer
- Block file name: a Python string (should be "" for non-inode blocks)
-
-handle_sigint()
-
- This function should make the plugin stop its parsing loop. This allows it
- to be interrupted if askant catches a signal, for example. It takes no
- arguments and its return value is not used.
-
-
-3. Reference
-
-o See fsplugins/gfs2/gfs2module.c for an example of writing a Python fs plugin
- module in C.
-
-o http://www.python.org/doc/ext/intro.html shows how to extend Python with C or
- C++.
diff --git a/askant/README b/askant/README
deleted file mode 100644
index 74e3219..0000000
--- a/askant/README
+++ /dev/null
@@ -1,74 +0,0 @@
-
- Askant - A Block I/O Analysis Tool
- ----------------------------------
-
-0. Contents
-
- 1. Intro
- 2. Commands and their options
- 3. Examples
-
-
-1. Intro
-
-Askant allows you to gather file system block I/O data for performance analysis.
-It uses blktrace to trace block operations and adds file system specific data by
-parsing fs block information straight from the storage device. To do this it
-invokes functionality from file system parser plugins which traverse the on-disk
-structures and report details of the blocks they encounter through callbacks.
-
-Usage: askant <command> [options...]
-
-o See 'askant --commands' for a full list of commands.
-
-
-2. Commands and their options
-
- dumpfs
-
- Dumps the block data provided by an fs plugin from the given file system.
-
- -d The block device which contains the file system under scrutiny
- -t The type of the file system, i.e. the name of the fs plugin to load
- -o Output file (optional). Stdout is used if '-' or omitted.
- (Note that the output file should be on a different device to the one
- specified with -d.)
-
- unlinks
-
- Does a dumpfs and runs blktrace until interrupted. It then blkparses the
- blktrace data and adds the block data. This is suitable for tracing I/O
- operations during an unlink test run.
-
- -d The block device which contains the file system under scrutiny
- -t The type of the file system, i.e. the name of the fs plugin to load
- -g The path to the debugfs mount point, defaults to /sys/kernel/debug
- -D The directory in which to store the block dumps and blktrace files.
-
- creates
-
- Runs blktrace until interrupted. It then does a dumpfs and blkparses the
- blktrace data and adds the block data. This is suitable for tracing I/O
- operations during a file creation test run.
-
- -d The block device which contains the file system under scrutiny
- -t The type of the file system, i.e. the name of the fs plugin to load
- -g The path to the debugfs mount point, defaults to /sys/kernel/debug
- -D The directory in which to store the block dumps and blktrace files.
-
-3. Examples
-
-o Parse the file system and dump information about each block to stdout e.g.:
-
- # askant dumpfs -t gfs2 -d /dev/sda3
-
-o Trace 'unlinks' by dumping block information before the blktrace and matching
- block data to provide a more detailed blkparse output e.g.:
-
- # askant unlinks -t gfs2 -d /dev/sda3 -D /tmp/dir -g /mnt/debugfs
-
-o Trace 'creates' by dumping block information after the blktrace and matching
- block data to provide a more detailed blkparse output e.g.:
-
- # askant creates -t gfs2 -d /dev/sda3 -D /tmp/dir -g /mnt/debugfs
-
diff --git a/askant/askant/__init__.py b/askant/askant/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/askant/askant/about.py b/askant/askant/about.py
deleted file mode 100644
index 3d40a74..0000000
--- a/askant/askant/about.py
+++ /dev/null
@@ -1,5 +0,0 @@
-"""
-Version information for askant.
-"""
-
-version = "0.0.1"
diff --git a/askant/askant/askant.py b/askant/askant/askant.py
deleted file mode 100644
index a4376b6..0000000
--- a/askant/askant/askant.py
+++ /dev/null
@@ -1,24 +0,0 @@
-"""
-The main entry point for askant.
-"""
-
-import sys
-import commands
-
-def main():
-
- """Run askant"""
-
- cmds = commands.Commands()
- cmds.register_command(commands.DumpFSCommand())
- cmds.register_command(commands.UnlinksCommand())
- cmds.register_command(commands.CreatesCommand())
-
- try:
- cmds.process_argv(sys.argv)
- except commands.NoFSPluginException, p:
- print >>sys.stderr, "Plugin not found: %s" %p
-
-
-if __name__ == '__main__':
- main()
diff --git a/askant/askant/blktrace.py b/askant/askant/blktrace.py
deleted file mode 100644
index fdf0219..0000000
--- a/askant/askant/blktrace.py
+++ /dev/null
@@ -1,93 +0,0 @@
-"""
-Wrapper classes for running blktrace and blkparse and using their output
-"""
-
-import os
-import sys
-import time
-import signal
-from subprocess import Popen
-from subprocess import PIPE
-from sysfs import Sysfs
-
-class BlktraceException(Exception):
-
- def __init__(self, val, msg):
- Exception.__init__(self)
- self.val = val
- self.msg = msg
-
- def __str__(self):
- return self.msg
-
-class Blktrace:
-
- def __init__(self, dev, debugfs='/sys/kernel/debug'):
- self.dev = dev
- self.debugfs = debugfs
- self.sysfs = Sysfs(dev)
- self.btpid = -1
-
- def handle_sigint(self, sig, frame):
- if self.btpid >= 0:
- os.kill(self.btpid, signal.SIGTERM)
-
- def trace(self, tracefile):
-
- if not self.dev:
- raise Exception("No device specified.")
-
- btargs = ['blktrace',
- '-d', self.dev,
- '-r', self.debugfs,
- '-o', tracefile]
-
- blktrace = Popen(btargs, bufsize=1, stdout=PIPE,
- stderr=open('/dev/null','w'))
- self.btpid = blktrace.pid
- btres = None
- while btres is None:
- time.sleep(1)
- btres = blktrace.poll()
-
- self.btpid = -1
- if btres:
- raise BlktraceException(btres,
- 'blktrace exited with code ' + str(btres))
-
-
-
- def parse(self, tracefile, getblk):
-
- if not self.dev:
- raise Exception("No device specified.")
-
- offset = self.sysfs.get_partition_start_sector()
-
- bpargs = ['blkparse', '-i', tracefile]
- blkparse = Popen(bpargs, bufsize=1, stdout=PIPE)
-
- bpres = None
- while bpres is None:
- output = blkparse.stdout.readline().strip()
- if output:
- chunks = output.split()
- try:
- # chunks[7] is the sector number
- blk = list(getblk(int(chunks[7])))
- print "%s %s %s" %\
- (' '.join(blk[0:3]),\
- output.strip(),
- blk[3].strip())
- except KeyError:
- pass
- except ValueError:
- pass
-
- bpres = blkparse.poll()
-
- if bpres:
- raise BlktraceException(bpres,
- 'blkparse exited with code ' + str(bpres))
-
-
diff --git a/askant/askant/commands.py b/askant/askant/commands.py
deleted file mode 100644
index ad4639f..0000000
--- a/askant/askant/commands.py
+++ /dev/null
@@ -1,333 +0,0 @@
-"""Command line parsing and processing for askant"""
-
-import fs
-import os
-import sys
-import time
-import about
-import signal
-import tempfile
-from sysfs import Sysfs, SysfsException
-from blktrace import Blktrace, BlktraceException
-from optparse import OptionParser, make_option
-
-class NoFSPluginException(Exception):
-
- """A generic Exception class for when file system plugins are missing"""
-
- pass # Nothing special about this exception
-
-class Commands:
-
- """Provides a convenient interface to the askant commands"""
-
- def __init__(self):
-
- self.commands = {}
- self.parser = OptionParser(usage="%prog COMMAND [options]",
- version=about.version)
-
-
- def register_command(self, command):
- self.commands[str(command)] = command
-
- def __lookup_command(self, command):
- try:
- return self.commands[command]
- except KeyError, k:
- return DefaultCommand()
-
- def __parse_command(self, command, argv):
- self.parser.prog = "%s %s" %\
- (self.parser.get_prog_name(), str(command))
- self.parser.set_usage(command.get_usage())
- optlst = command.get_options()
- for o in optlst:
- self.parser.add_option(o)
- self.parser.set_description(command.get_help())
-
- if str(command) != "":
- argv = argv[1:]
-
- return self.parser.parse_args(argv)
-
- def process_argv(self, argv):
- try:
- command = self.__lookup_command(argv[1])
- except IndexError, i:
- # This exits
- self.parser.error("No command specified. Try --help")
-
- options, args = self.__parse_command(command, argv[1:])
-
- command.do_command(options, args, self)
-
-class Command:
- def __init__(self):
- self.options = []
-
-class DefaultCommand(Command):
- def __str__(self):
- return ""
-
- def get_options(self):
- self.options.append(make_option("-c","--commands",
- action="store_true",
- help="Lists available commands"))
- return self.options
-
- def get_usage(self):
- return "%prog COMMAND [options] [args]"
-
- def get_help(self):
- return "Askant is a tool for tracing I/O activity in Linux "\
- "and adding extra file system context by "\
- "parsing file system data directly."
-
- def do_command(self, options, args, base):
- if options.commands:
- base.parser.print_usage()
- print "Available commands:"
- keys = base.commands.keys()
- keys.sort()
- for k in keys:
- print " %10s %s" %\
- (k, base.commands[k].get_help())
- print ""
- print "For command-specific help, use "\
- "%sCOMMAND --help" % base.parser.get_prog_name()
- else:
- base.parser.error("Command not found. Try --commands")
-
-
-class FSCommandTemplate(Command):
- def __init__(self):
- self.fsmod = None
- self.block_table = {}
- self.block_func = self.__report_hook
- self.sector_size = None
- self.offset = None
- self.options = [
- make_option("-d","--device", metavar="DEVICE",
- help="The device to analyse."),
- ]
-
- def load_fs_plugin(self, fsname):
- try:
- self.fsmod = __import__("fs." + fsname,
- globals(), locals(), ["fs"])
- except ImportError, i:
- raise NoFSPluginException(fsname)
-
- def parse_fs(self, dev, hook=None):
- if hook is None:
- hook = self.block_func
-
- sfs = Sysfs(dev)
- offset = sfs.get_partition_start_sector()
- sector_size = sfs.get_dev_sector_size()
-
- self.fsmod.set_report_hook(hook)
-
- try:
- sfs = Sysfs(dev)
- self.offset = sfs.get_partition_start_sector()
- self.sector_size = sfs.get_dev_sector_size()
- signal.signal(signal.SIGINT, self.fsmod.handle_sigint)
- self.blk_size = self.fsmod.get_block_size(dev)
- self.fsmod.parsefs(dev)
- signal.signal(signal.SIGINT, signal.SIG_DFL)
- except IOError, i:
- print >>sys.stderr, str(i)
- sys.exit(1)
-
- def set_outfile(self, outfile):
- self.outfile = outfile
-
- def trace(self, options, dump_before):
-
- sfs = Sysfs(options.device)
- self.offset = sfs.get_partition_start_sector()
- self.sector_size = sfs.get_dev_sector_size()
-
- tmstamp = time.strftime('%Y%m%d%H%M%S')
- self.block_func = self.__report_hook
- try:
- self.outfile = open(os.path.join(options.outdir, "blocks-%s" %
- tmstamp), 'w')
- except IOError, e:
- print >>sys.stderr, str(e)
- sys.exit(1)
-
- if dump_before:
- print >>sys.stderr, "Gathering block data..."
- self.parse_fs(options.device)
- self.outfile.close()
-
- if options.debugfs:
- bt = Blktrace(options.device, options.debugfs)
- else:
- bt = Blktrace(options.device)
-
- os.chdir(options.outdir) # Blktrace doesn't use absolute paths
- print >>sys.stderr, "Tracing. Hit Ctrl-C to end..."
- try:
- signal.signal(signal.SIGINT, bt.handle_sigint)
- bt.trace(tmstamp)
- signal.signal(signal.SIGINT, signal.SIG_DFL)
- except BlktraceException, b:
- signal.signal(signal.SIGINT, signal.SIG_DFL)
- print >>sys.stderr, str(b)
- sys.exit(1)
-
- if not dump_before:
- print >>sys.stderr, "Gathering block data..."
- self.parse_fs(options.device)
- self.outfile.close()
-
- print >>sys.stderr, "Matching blocks..."
- blockdb = open(self.outfile.name, 'r')
- blocks = {}
- for l in blockdb.readlines():
- s = l.split('\t')
- blocks[int(s[0])] = tuple(s[1:])
-
- bt.parse(os.path.join(options.outdir, tmstamp), blocks.__getitem__)
- blockdb.close()
-
- def __report_hook(self, blk, type, parent, fn):
- if not fn:
- fn = ""
- try:
- self.outfile.write("%d\t%d\t%s\t%d\t\"%s\"\n" % (
- ((blk * self.blk_size)/self.sector_size) + self.offset,
- blk, type, parent, fn))
- except Exception, e:
- print str(e)
-
- def __dict_hook(self, blk, type, parent, fn):
- self.block_table[
- ((blk * self.blk_size)/self.sector_size)+self.offset] =\
- (blk, type, parent, fn)
-
-
-class DumpFSCommand(FSCommandTemplate):
- def __str__(self):
- return "dumpfs"
-
- def get_options(self):
- self.options.append(make_option("-t","--type", metavar="FSTYPE",
- help="The type of file system on the device."))
- self.options.append(make_option("-o","--output", metavar="FILE",
- default="-",
- help="File to write output to or '-' for stdout "
- "(default)"))
- return self.options
-
- def get_usage(self):
- return "%prog [options]"
-
- def get_help(self):
- return "Dump fs block information in TSV format."
-
- def do_command(self, options, args, base):
-
- if not options.device:
- base.parser.error("No device specified. Use -d.")
-
- if not options.type:
- base.parser.error("No file system type specified. "
- "Use -t.")
- try:
- if options.output != "-":
- self.set_outfile(open(options.output, 'w'))
- else:
- self.set_outfile(sys.stdout)
- except IOError, e:
- print >>sys.stderr,\
- "Unable to open file for writing: %s" %\
- options.output
- return
- except KeyError:
- pass
-
- self.load_fs_plugin(options.type)
- try:
- self.parse_fs(options.device)
- except SysfsException, s:
- base.parser.error(s)
-
-class UnlinksCommand(FSCommandTemplate):
- def __str__(self):
- return "unlinks"
-
- def get_options(self):
- self.options.append(make_option("-t","--type", metavar="FSTYPE",
- help="The type of file system on the device."))
- self.options.append(make_option("-g","--debugfs", metavar="PATH",
- default="/sys/kernel/debug",
- help="The path to the debugfs mountpoint. Default "
- "/sys/kernel/debug"))
- self.options.append(make_option("-D","--outdir", metavar="DIR",
- help="Directory to write output to. Required."))
- return self.options
-
- def get_usage(self):
- return "%prog [options]"
-
- def get_help(self):
- return "Trace block I/O activity during unlink tests."
-
- def do_command(self, options, args, base):
- if not options.device:
- base.parser.error("No device specified. Use -d.")
-
- self.device = options.device
-
- if not options.type:
- base.parser.error("No file system type specified. "
- "Use -t.")
- if not options.outdir:
- base.parser.error("No output directory specified. "
- "Use -D.")
-
- self.load_fs_plugin(options.type)
- self.trace(options, dump_before=True)
-
-class CreatesCommand(FSCommandTemplate):
- def __str__(self):
- return "creates"
-
- def get_options(self):
- self.options.append(make_option("-t","--type", metavar="FSTYPE",
- help="The type of file system on the device."))
- self.options.append(make_option("-g","--debugfs", metavar="PATH",
- default="/sys/kernel/debug",
- help="The path to the debugfs mountpoint. Default "
- "/sys/kernel/debug"))
- self.options.append(make_option("-D","--outdir", metavar="DIR",
- help="Directory to write output to. Required."))
- return self.options
-
- def get_usage(self):
- return "%prog [options]"
-
- def get_help(self):
- return "Trace block I/O activity during create tests."
-
- def do_command(self, options, args, base):
- if not options.device:
- base.parser.error("No device specified. Use -d.")
-
- self.device = options.device
-
- if not options.type:
- base.parser.error("No file system type specified. "
- "Use -t.")
- if not options.outdir:
- base.parser.error("No output directory specified. "
- "Use -D.")
-
- self.load_fs_plugin(options.type)
- self.trace(options, dump_before=False)
-
diff --git a/askant/askant/fs/__init__.py b/askant/askant/fs/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/askant/askant/sysfs.py b/askant/askant/sysfs.py
deleted file mode 100644
index 12b7a3b..0000000
--- a/askant/askant/sysfs.py
+++ /dev/null
@@ -1,86 +0,0 @@
-"""
-Provides access to sysfs data pertaining to storage partitions.
-"""
-
-import os
-import os.path
-
-class SysfsException(Exception):
- """
- An exception which is raised when things go wrong with Sysfs.
- """
- pass # No functionality to add over Exception class
-
-class Sysfs:
- """
- Provides access to sysfs data pertaining to storage partitions.
- """
- def __init__(self, partition, sysfs_path='/sys'):
- """
- Instantiate a Sysfs object. partition should be a path to a
- disc partition device e.g. /dev/sda3.
- """
- self.partition = partition
- self.device = self.__partition2parent()
- self.sysfs_path = sysfs_path
- line = self.__firstline(os.path.join(
- sysfs_path,'block',
- os.path.split(self.device)[1],
- os.path.split(self.partition)[1],
- 'start'))
- self.partition_start = int(line)
- line = self.__firstline(os.path.join(
- sysfs_path,'block',
- os.path.split(self.device)[1],
- 'queue',
- 'hw_sector_size'))
- self.dev_sector_size = int(line)
- line = self.__firstline(os.path.join(
- sysfs_path,'block',
- os.path.split(self.device)[1],
- os.path.split(self.partition)[1],
- 'size'))
- self.partition_size = int(line)
-
- def __firstline(self, fname):
- """
- Read the first line of a file
- """
- try:
- fobj = open(fname, 'r')
- except IOError:
- raise SysfsException(
- 'Could not open file "%s" for reading. Please '
- 'check your kernel is 2.6.25 or later and '
- 'sysfs is mounted.' % fname)
- line = fobj.readline()
- fobj.close()
- return line
-
- def __partition2parent(self):
- """
- Return the name of a partition device's parent device
- e.g. /dev/sda3 -> /dev/sda
- """
- # This may need thinking about a bit more -
- # nothing in life can be this simple.
- return self.partition.rstrip('0123456789')
-
-
- def get_partition_start_sector(self):
- """
- Look up the start sector of the partition.
- """
- return self.partition_start
-
- def get_dev_sector_size(self):
- """
- Look up the size of the device's sectors.
- """
- return self.dev_sector_size
-
- def get_partition_size(self):
- """
- Look up the size of the partition.
- """
- return self.partition_size
diff --git a/askant/fsplugins/__init__.py b/askant/fsplugins/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/askant/fsplugins/gfs2/gfs2.c b/askant/fsplugins/gfs2/gfs2.c
deleted file mode 100644
index bac9c35..0000000
--- a/askant/fsplugins/gfs2/gfs2.c
+++ /dev/null
@@ -1,405 +0,0 @@
-/**
- * main.c - Functions for parsing the on-disk structure of a GFS2 fs.
- * Written by Andrew Price
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <string.h>
-#include <fcntl.h>
-#include <libgfs2.h>
-#include <errno.h>
-
-void (*report_func)(long int blk, char *type, long int parent, char *fn);
-
-/* Define how the block information is reported */
-#define report(b,p,t,n) \
- ((*report_func)((long int)b, t, (long int)p,n))
-/* (printf("%lu\t%s\t%lu\t%s\n", (long int)b, t, (long int)p, f)) */
-#define report_data(b,p) report(b,p,"D","")
-#define report_leaf(b,p) report(b,p,"L","")
-#define report_indir(b,p) report(b,p,"i","")
-#define report_inode(b,p,n) report(b,p,"I",n)
-
-struct blk_extended {
- uint64_t parent_blk;
- char *fname;
- char *blk;
-};
-
-/* FIXME: A static length block stack is most likely a stupid idea */
-struct blkstack {
- int top;
- struct blk_extended blocks[1024];
-};
-
-struct blkstack blk_stack;
-struct gfs2_sb sb;
-uint32_t blk_size;
-off_t max_seek;
-int fd;
-int flag_stop;
-
-/* prog_name and print_it() are needed to satisfy externs in libgfs2 */
-char *prog_name = "askant";
-
-void print_it(const char *label, const char *fmt, const char *fmt2, ...)
-{
- va_list args;
-
- va_start(args, fmt2);
- printf("%s = ", label);
- vprintf(fmt, args);
- printf("\n");
- va_end(args);
-}
-
-/**
- * Push a block onto the stack
- */
-static void push_blk(char *blk, uint64_t parent, char *fn)
-{
- struct blk_extended eblk;
-
- eblk.blk = blk;
- eblk.parent_blk = parent;
- eblk.fname = fn;
-
- blk_stack.top++;
- blk_stack.blocks[blk_stack.top] = eblk;
-}
-
-/**
- * Initialise the block stack
- */
-static int blk_stack_init(void)
-{
- blk_stack.top = -1;
- return 1;
-}
-
-/**
- * Pop a block from the stack
- */
-static struct blk_extended pop_blk(void)
-{
- return blk_stack.blocks[blk_stack.top--];
-}
-
-/**
- * Read the GFS2 superblock into the global sb variable.
- * Returns 1 on error, 0 on success.
- */
-static int read_gfs2_sb(void)
-{
- off_t offsetsb;
- off_t offsetres;
- unsigned char buffer[GFS2_BASIC_BLOCK];
- ssize_t readsz;
-
- offsetsb = GFS2_SB_ADDR * GFS2_BASIC_BLOCK;
- offsetres = lseek(fd, offsetsb, SEEK_SET);
-
- if (offsetres != offsetsb) {
- fprintf(stderr, "Could not seek to sb location on device.\n");
- return 0;
- }
-
- readsz = read(fd, buffer, GFS2_BASIC_BLOCK);
- if (readsz != GFS2_BASIC_BLOCK) {
- fprintf(stderr, "Could not read superblock.\n");
- return 0;
- }
-
- gfs2_sb_in(&sb, (char *)buffer);
- if (check_sb(&sb)) {
- fprintf(stderr, "Not a GFS2 filesystem.\n");
- return 0;
- }
-
- return 1;
-}
-
-/**
- * Read a block.
- * blk_offset must be a block number.
- * The returned pointer must be free'd.
- */
-static char *read_gfs2_blk(off_t blk_offset)
-{
- off_t offset;
- off_t offsetres;
- ssize_t readsz;
- char *buffer;
-
- buffer = (char *)malloc(blk_size);
- if (!buffer) {
- fprintf(stderr, "Could not allocate memory for block.\n");
- return NULL;
- }
-
- offset = blk_offset * blk_size;
-
- offsetres = lseek(fd, offset, SEEK_SET);
- if (offsetres != offset) {
- fprintf(stderr,
- "Could not seek to block location: %lu error: %s\n",
- (long int)blk_offset, strerror(errno));
- return NULL;
- }
-
- readsz = read(fd, buffer, blk_size);
- if (readsz != blk_size) {
- fprintf(stderr, "Could not read block: %lu\n",
- (long int)blk_offset);
- return NULL;
- }
-
- return buffer;
-}
-
-/**
- * Look at indirect pointers from a starting point in a block.
- */
-static void do_indirect(char *start, uint16_t height, uint64_t parent)
-{
- uint64_t ptr;
- unsigned int i;
- char *blk;
-
- for (i = 0; i < blk_size; i += sizeof(uint64_t)) {
- ptr = be64_to_cpu(*(uint64_t *)(start + i));
- if (ptr > 0 && ptr < (max_seek / blk_size)) {
- if (height == 1) {
- report_data(ptr, parent);
- } else if (height > 1) {
- blk = read_gfs2_blk(ptr);
- if (blk) {
- report_indir(ptr, parent);
- do_indirect(blk, height - 1, ptr);
- free(blk);
- }
- }
- } else {
- break;
- }
- }
-}
-
-/**
- * Parse count number of dirents from a starting point in a block.
- */
-static void do_dirents(char *dirents, char *end, uint64_t parent, uint64_t gparent)
-{
- struct gfs2_dirent dirent;
- char *di_blk;
- char *fname;
-
- while (dirents < end) {
- gfs2_dirent_in(&dirent, dirents);
- if (dirent.de_inum.no_addr &&
- dirent.de_inum.no_addr != parent &&
- dirent.de_inum.no_addr != gparent &&
- dirent.de_name_len > 0 &&
- dirent.de_name_len <= GFS2_FNAMESIZE) {
-
- fname = (char *)malloc(dirent.de_name_len + 1);
- if (!fname) {
- break;
- }
-
- memcpy(fname, dirents + sizeof(struct gfs2_dirent),
- dirent.de_name_len);
- fname[dirent.de_name_len] = '\0';
-
- di_blk = read_gfs2_blk(dirent.de_inum.no_addr);
- if (di_blk) {
- push_blk(di_blk, parent, fname);
- }
- }
- dirents += dirent.de_rec_len;
- }
-}
-
-/**
- * Examine the dirents in a leaf block.
- * If the leaf is chained, do the chained leaves too.
- */
-static void do_leaf(char *blk, uint64_t parent, uint64_t gparent)
-{
- struct gfs2_leaf leaf;
-
- while (blk) {
- gfs2_leaf_in(&leaf, blk);
- do_dirents(blk + sizeof(struct gfs2_leaf), blk + blk_size,
- parent, gparent);
- free(blk);
- if (!leaf.lf_next) {
- break;
- }
-
- blk = read_gfs2_blk(leaf.lf_next);
- }
-}
-
-/**
- * Parse leaf pointer data and examine the
- * dirents in the destination leaf blocks.
- */
-static void do_leaves(char *start, uint64_t parent, uint64_t gparent)
-{
- uint64_t ptr;
- uint64_t prev;
- unsigned int i;
- char *blk;
-
- prev = 0;
- for (i = 0; i < blk_size - sizeof(struct gfs2_dinode);
- i += sizeof(uint64_t)) {
- ptr = be64_to_cpu(*(uint64_t *)(start + i));
-
- if (ptr >= (max_seek / blk_size)) {
- break;
- }
-
- if (ptr && ptr != prev) {
- blk = read_gfs2_blk(ptr);
- if (blk) {
- report_leaf(ptr, parent);
- do_leaf(blk, parent, gparent);
- }
- prev = ptr;
- }
- }
-}
-
-/**
- * Parse inode data from a block
- */
-static void do_inode_blk(char *blk, uint64_t parent, char *fname)
-{
- struct gfs2_dinode di;
- char *data;
-
- gfs2_dinode_in(&di, blk);
- report_inode(di.di_num.no_addr, parent, fname);
-
- data = (char *)((struct gfs2_dinode *)blk + 1);
-
- if (di.di_height > 0) {
- /* Indirect pointers */
- do_indirect(data, di.di_height, di.di_num.no_addr);
- } else if (S_ISDIR(di.di_mode) && !(di.di_flags & GFS2_DIF_EXHASH)) {
- /* Stuffed directory */
- do_dirents(data, blk + blk_size, di.di_num.no_addr, parent);
- } else if (S_ISDIR(di.di_mode) &&
- (di.di_flags & GFS2_DIF_EXHASH) &&
- !(di.di_height)) {
- /* Directory, has hashtable, height == 0 */
- do_leaves(data, di.di_num.no_addr, parent);
- }
-
- /* free previously stacked block */
- free(fname);
- free(blk);
-}
-
-/**
- * Get the root dir block and parse the fs
- * using a stack to keep track of the unvisited
- * inode blocks.
- */
-static void parse_fs(void)
-{
- struct gfs2_inum *root_dir_inum;
- struct gfs2_inum *master_dir_inum;
- struct blk_extended blk;
- char *root_blk;
- char *master_blk;
-
- flag_stop = 0;
-
- root_dir_inum = &(sb.sb_root_dir);
- master_dir_inum = &(sb.sb_master_dir);
-
- root_blk = read_gfs2_blk(root_dir_inum->no_addr);
- master_blk = read_gfs2_blk(master_dir_inum->no_addr);
- if (!root_blk || !master_blk) {
- return;
- }
-
- push_blk(root_blk, root_dir_inum->no_addr, NULL);
- while (blk_stack.top >= 0 && !flag_stop) {
- blk = pop_blk();
- do_inode_blk(blk.blk, blk.parent_blk, blk.fname);
- }
-
- push_blk(master_blk, master_dir_inum->no_addr, NULL);
- while (blk_stack.top >= 0 && !flag_stop) {
- blk = pop_blk();
- /* TODO: Examine each block's magic number instead of assuming
- * they're inodes. Omitted for now due to time constraints and
- * the number of GFS2_METATYPE_*s which need catering for.
- */
- do_inode_blk(blk.blk, blk.parent_blk, blk.fname);
- }
-}
-
-/**
- * Raise a flag to stop the parse loop cleanly
- */
-void gfs2_stop(void)
-{
- flag_stop = 1;
-}
-
-/**
- * Parse a gfs2 file system on a given device
- */
-int gfs2_parse(char *dev, void (*func)(long int b, char *t, long int p, char *f))
-{
- report_func = func;
-
- if (!blk_stack_init()) {
- return 0;
- }
-
- if ((fd = open(dev, O_RDONLY)) < 0) {
- return 0;
- }
-
- if (!read_gfs2_sb()) {
- close(fd);
- return 0;
- }
-
- blk_size = sb.sb_bsize;
- max_seek = lseek(fd, 0, SEEK_END);
-
- parse_fs();
-
- close(fd);
-
- return 1;
-}
-
-/**
- * Return the block size of the gfs2 file system on a given device.
- */
-uint32_t gfs2_block_size(char *dev)
-{
- if ((fd = open(dev, O_RDONLY)) < 0) {
- return 0;
- }
-
- if (!read_gfs2_sb()) {
- close(fd);
- return 0;
- }
- close(fd);
- return sb.sb_bsize;
-}
diff --git a/askant/fsplugins/gfs2/gfs2.h b/askant/fsplugins/gfs2/gfs2.h
deleted file mode 100644
index 6c72eb0..0000000
--- a/askant/fsplugins/gfs2/gfs2.h
+++ /dev/null
@@ -1,3 +0,0 @@
-uint32_t gfs2_block_size(char *dev);
-int gfs2_parse(char *dev, void (*func)(long int b, char *t, long int p, char *f));
-void gfs2_stop(void);
diff --git a/askant/fsplugins/gfs2/gfs2module.c b/askant/fsplugins/gfs2/gfs2module.c
deleted file mode 100644
index c6a0584..0000000
--- a/askant/fsplugins/gfs2/gfs2module.c
+++ /dev/null
@@ -1,104 +0,0 @@
-#include <Python.h>
-
-#include "gfs2.h"
-
-static PyObject *report_func = NULL;
-
-void report_func_wrapper(long int blk, char *type, long int parent, char *fn)
-{
- PyObject *arglist;
- PyObject *result;
-
- arglist = Py_BuildValue("lsls", blk, type, parent, fn);
- if (!arglist) {
- return;
- }
- result = PyEval_CallObject(report_func, arglist);
- Py_DECREF(arglist);
- if (!result) {
- return;
- }
- Py_DECREF(result);
-}
-
-static PyObject *gfs2_set_report_hook(PyObject *self, PyObject *args)
-{
- PyObject *result = NULL;
- PyObject *temp;
-
- if (PyArg_ParseTuple(args, "O:set_callback", &temp)) {
- if (!PyCallable_Check(temp)) {
- PyErr_SetString(PyExc_TypeError, "parameter must be callable");
- return NULL;
- }
- Py_XINCREF(temp);
- Py_XDECREF(report_func);
- report_func = temp;
-
- Py_INCREF(Py_None);
- result = Py_None;
- }
- return result;
-}
-
-static PyObject *gfs2_parsefs(PyObject *self, PyObject *args)
-{
- char *dev;
-
- if (!PyArg_ParseTuple(args, "s:parsefs", &dev)) {
- return NULL;
- }
-
- if (!gfs2_parse(dev, &report_func_wrapper)) {
- return PyErr_SetFromErrno(PyExc_IOError);
- }
- Py_INCREF(Py_None);
-
- return Py_None;
-}
-
-static PyObject *gfs2_get_block_size(PyObject *self, PyObject *args)
-{
- char *dev;
- uint32_t blksize;
-
- if (!PyArg_ParseTuple(args, "s:get_block_size", &dev)) {
- return NULL;
- }
-
- blksize = gfs2_block_size(dev);
- if (!blksize) {
- return PyErr_SetFromErrno(PyExc_IOError);
- }
-
- PyObject *size = Py_BuildValue("I", blksize);
- if (!size) {
- return NULL;
- }
- Py_INCREF(size);
- return size;
-}
-
-static PyObject *gfs2_handle_sigint(PyObject *signum, PyObject *frame)
-{
- gfs2_stop();
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-static PyMethodDef GFS2Methods[] = {
- {"set_report_hook", gfs2_set_report_hook, METH_VARARGS,
- "Specify a hook function through which to report blocks."},
- {"parsefs", gfs2_parsefs, METH_VARARGS,
- "Parses the given block device as a GFS2 file system."},
- {"get_block_size", gfs2_get_block_size, METH_VARARGS,
- "Returns the file system block size."},
- {"handle_sigint", gfs2_handle_sigint, METH_VARARGS,
- "Handles signal SIGINT."},
- {NULL, NULL, 0, NULL}
-};
-
-PyMODINIT_FUNC initgfs2(void)
-{
- (void)Py_InitModule("gfs2", GFS2Methods);
-}
diff --git a/askant/scripts/askant b/askant/scripts/askant
deleted file mode 100755
index 3375b0c..0000000
--- a/askant/scripts/askant
+++ /dev/null
@@ -1,6 +0,0 @@
-#! /usr/bin/env python
-
-from askant import askant
-
-if __name__ == '__main__':
- askant.main()
diff --git a/askant/setup.py b/askant/setup.py
deleted file mode 100755
index 78daff2..0000000
--- a/askant/setup.py
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/usr/bin/env python
-
-from distutils.core import setup, Extension
-from askant import about
-
-setup(name="askant",
- version=about.version,
- description="File system performance analysis tool",
- author="Andrew Price",
- author_email="andy@andrewprice.me.uk",
- url="http://andrewprice.me.uk/projects/askant",
- packages = ['askant','askant.fs'],
- ext_modules = [Extension("askant.fs.gfs2",
- sources = ["fsplugins/gfs2/gfs2module.c","fsplugins/gfs2/gfs2.c"],
- libraries = ["gfs2"],
- include_dirs=['../gfs2/libgfs2', '../gfs2/include', '../make'],
- library_dirs=['../gfs2/libgfs2'])],
- scripts = ['scripts/askant'])
diff --git a/configure b/configure
index 867762a..858076a 100755
--- a/configure
+++ b/configure
@@ -86,6 +86,7 @@ my %options = (
fenceagentslibdir => \$fenceagentslibdir,
enable_crack_of_the_day => \$enable_crack_of_the_day,
enable_legacy_code => \$enable_legacy_code,
+ enable_contrib => \$enable_contrib,
enable_xen => \$enable_xen,
somajor => \$somajor,
sominor => \$sominor,
@@ -175,6 +176,7 @@ my $err = &GetOptions (\%options,
'fenceagentslibdir=s',
'enable_crack_of_the_day',
'enable_legacy_code',
+ 'enable_contrib',
'enable_xen',
'without_ccs',
'without_cman',
@@ -266,6 +268,7 @@ if ($help || !$err) {
print "\t\tUse --fence_agents=help for a list\n";
print "--fenceagentslibdir=\tspecify directory where to install common fence python lib. (Default: /usr/share/fence)\n";
print "--enable_crack_of_the_day\tEnable build of highly experimental features that rely on code that is not yet available for general use. (Default: no)\n";
+ print "--enable_contrib\tEnable build of community contributed code/tools. (Default: no)\n";
print "--enable_legacy_code\tEnable build of old/obsolete/unsupported code/tools. (Default: no)\n";
print "--enable_xen\tEnable building of Xen-specific pieces\n";
print "--without_ccs\tDisable ccs building (Default: enabled)\n";
@@ -595,6 +598,9 @@ if (!$enable_crack_of_the_day) {
print "\n********************************************************************************************\n";
$cflags="${cflags} -DEXPERIMENTAL_BUILD";
}
+if (!$enable_contrib) {
+ $enable_contrib="";
+}
if (!$enable_legacy_code) {
$enable_legacy_code="";
} else {
@@ -748,6 +754,7 @@ while (<IFILE>) {
$_ =~ s/\@FENCEAGENTSLIBDIR\@/$fenceagentslibdir/;
$_ =~ s/\@ENABLE_CRACK_OF_THE_DAY\@/$enable_crack_of_the_day/;
$_ =~ s/\@ENABLE_LEGACY_CODE\@/$enable_legacy_code/;
+ $_ =~ s/\@ENABLE_CONTRIB\@/$enable_contrib/;
$_ =~ s/\@ENABLE_XEN\@/$enable_xen/;
$_ =~ s/\@DISABLE_CCS\@/$without_ccs/;
$_ =~ s/\@DISABLE_CMAN\@/$without_cman/;
diff --git a/contrib/askant/INSTALL b/contrib/askant/INSTALL
new file mode 100644
index 0000000..ce26b82
--- /dev/null
+++ b/contrib/askant/INSTALL
@@ -0,0 +1,42 @@
+
+ How To Install Askant
+ ---------------------
+
+o Build Dependencies
+ - Python
+ - libgfs2 (part of the Red Hat cluster suite as a static lib)
+
+o Dependencies
+ - Python
+ - blktrace and blkparse with appropriate kernel config options enabled and
+ debugfs mounted
+
+o Intro
+ Askant uses Python's distutils for its standard installation procedure so the
+ setup.py script provides installation routines. Note that it doesn't provide
+ uninstallation routines so the best way to install it is to obtain a package
+ for your particular distribution and install that.
+
+o Installation
+ To install askant to the default location on your system, run as root:
+
+ ./setup.py install
+
+ If you wish to install it to a different root directory (e.g. /var/mychroot)
+ or prefix directory (e.g. /usr/local instead of /usr) see:
+
+ ./setup.py install --help
+
+ To find out everything else setup.py can do:
+
+ ./setup.py --help-commands
+
+o Use Without Installing
+ To use askant without installing it (e.g. if you want to test patches
+ quickly) it is necessary to carry out the build stage to compile the required
+ plugins. Do:
+
+ ./setup.py build
+ cd build/lib*
+ python askant/askant.py
+
diff --git a/contrib/askant/PLUGINAPI b/contrib/askant/PLUGINAPI
new file mode 100644
index 0000000..4c2eceb
--- /dev/null
+++ b/contrib/askant/PLUGINAPI
@@ -0,0 +1,65 @@
+
+ Writing File System Plugins for Askant
+ --------------------------------------
+
+0. Contents
+
+ 1. Intro
+ 2. API
+ 3. Reference
+
+
+1. Intro
+
+In order to gather file system data with which to enhance the data provided by
+blktrace, there must be specific code written for each file system type we wish
+to support. This is where file system plugins come along. The plugins must
+basically traverse an on-disk file system structure on a block device and report
+back their findings through a callback handed to them by askant. They must also
+expose a function which stops their traversal loop in case askant wishes to stop
+their execution prematurely.
+
+
+2. API
+
+A file system plugin must be written as a Python module. This means that C, C++
+and Python are suitable languages to write them in. They must expose a number of
+Python functions to askant:
+
+parsefs(dev)
+
+ This function is called when askant wants the plugin to parse the file
+ system on the device specified by the device, dev.
+
+get_block_size()
+
+ This function is called to ascertain the file system block size which is
+ required in order to map the disk sector numbers provided by blktrace to
+ file system block numbers. It takes no arguments and should return a Python
+ integer value.
+
+set_report_hook(func)
+
+ This function is called to pass in a report function which the plugin should
+ call to report the details of a file system block. func is a function which
+ accepts four arguments in this order:
+
+ Block number: a Python long integer
+ Block type: a Python string
+ Block number of parent: a Python long integer
+ Block file name: a Python string (should be "" for non-inode blocks)
+
+handle_sigint()
+
+ This function should make the plugin stop its parsing loop. This allows it
+ to be interrupted if askant catches a signal, for example. It takes no
+ arguments and its return value is not used.
+
+
+3. Reference
+
+o See fsplugins/gfs2/gfs2module.c for an example of writing a Python fs plugin
+ module in C.
+
+o http://www.python.org/doc/ext/intro.html shows how to extend Python with C or
+ C++.
diff --git a/contrib/askant/README b/contrib/askant/README
new file mode 100644
index 0000000..74e3219
--- /dev/null
+++ b/contrib/askant/README
@@ -0,0 +1,74 @@
+
+ Askant - A Block I/O Analysis Tool
+ ----------------------------------
+
+0. Contents
+
+ 1. Intro
+ 2. Commands and their options
+ 3. Examples
+
+
+1. Intro
+
+Askant allows you to gather file system block I/O data for performance analysis.
+It uses blktrace to trace block operations and adds file system specific data by
+parsing fs block information straight from the storage device. To do this it
+invokes functionality from file system parser plugins which traverse the on-disk
+structures and report details of the blocks they encounter through callbacks.
+
+Usage: askant <command> [options...]
+
+o See 'askant --commands' for a full list of commands.
+
+
+2. Commands and their options
+
+ dumpfs
+
+ Dumps the block data provided by an fs plugin from the given file system.
+
+ -d The block device which contains the file system under scrutiny
+ -t The type of the file system, i.e. the name of the fs plugin to load
+ -o Output file (optional). Stdout is used if '-' or omitted.
+ (Note that the output file should be on a different device to the one
+ specified with -d.)
+
+ unlinks
+
+ Does a dumpfs and runs blktrace until interrupted. It then blkparses the
+ blktrace data and adds the block data. This is suitable for tracing I/O
+ operations during an unlink test run.
+
+ -d The block device which contains the file system under scrutiny
+ -t The type of the file system, i.e. the name of the fs plugin to load
+ -g The path to the debugfs mount point, defaults to /sys/kernel/debug
+ -D The directory in which to store the block dumps and blktrace files.
+
+ creates
+
+ Runs blktrace until interrupted. It then does a dumpfs and blkparses the
+ blktrace data and adds the block data. This is suitable for tracing I/O
+ operations during a file creation test run.
+
+ -d The block device which contains the file system under scrutiny
+ -t The type of the file system, i.e. the name of the fs plugin to load
+ -g The path to the debugfs mount point, defaults to /sys/kernel/debug
+ -D The directory in which to store the block dumps and blktrace files.
+
+3. Examples
+
+o Parse the file system and dump information about each block to stdout e.g.:
+
+ # askant dumpfs -t gfs2 -d /dev/sda3
+
+o Trace 'unlinks' by dumping block information before the blktrace and matching
+ block data to provide a more detailed blkparse output e.g.:
+
+ # askant unlinks -t gfs2 -d /dev/sda3 -D /tmp/dir -g /mnt/debugfs
+
+o Trace 'creates' by dumping block information after the blktrace and matching
+ block data to provide a more detailed blkparse output e.g.:
+
+ # askant creates -t gfs2 -d /dev/sda3 -D /tmp/dir -g /mnt/debugfs
+
diff --git a/contrib/askant/askant/__init__.py b/contrib/askant/askant/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/contrib/askant/askant/about.py b/contrib/askant/askant/about.py
new file mode 100644
index 0000000..3d40a74
--- /dev/null
+++ b/contrib/askant/askant/about.py
@@ -0,0 +1,5 @@
+"""
+Version information for askant.
+"""
+
+version = "0.0.1"
diff --git a/contrib/askant/askant/askant.py b/contrib/askant/askant/askant.py
new file mode 100644
index 0000000..a4376b6
--- /dev/null
+++ b/contrib/askant/askant/askant.py
@@ -0,0 +1,24 @@
+"""
+The main entry point for askant.
+"""
+
+import sys
+import commands
+
+def main():
+
+ """Run askant"""
+
+ cmds = commands.Commands()
+ cmds.register_command(commands.DumpFSCommand())
+ cmds.register_command(commands.UnlinksCommand())
+ cmds.register_command(commands.CreatesCommand())
+
+ try:
+ cmds.process_argv(sys.argv)
+ except commands.NoFSPluginException, p:
+ print >>sys.stderr, "Plugin not found: %s" %p
+
+
+if __name__ == '__main__':
+ main()
diff --git a/contrib/askant/askant/blktrace.py b/contrib/askant/askant/blktrace.py
new file mode 100644
index 0000000..fdf0219
--- /dev/null
+++ b/contrib/askant/askant/blktrace.py
@@ -0,0 +1,93 @@
+"""
+Wrapper classes for running blktrace and blkparse and using their output
+"""
+
+import os
+import sys
+import time
+import signal
+from subprocess import Popen
+from subprocess import PIPE
+from sysfs import Sysfs
+
+class BlktraceException(Exception):
+
+ def __init__(self, val, msg):
+ Exception.__init__(self)
+ self.val = val
+ self.msg = msg
+
+ def __str__(self):
+ return self.msg
+
+class Blktrace:
+
+ def __init__(self, dev, debugfs='/sys/kernel/debug'):
+ self.dev = dev
+ self.debugfs = debugfs
+ self.sysfs = Sysfs(dev)
+ self.btpid = -1
+
+ def handle_sigint(self, sig, frame):
+ if self.btpid >= 0:
+ os.kill(self.btpid, signal.SIGTERM)
+
+ def trace(self, tracefile):
+
+ if not self.dev:
+ raise Exception("No device specified.")
+
+ btargs = ['blktrace',
+ '-d', self.dev,
+ '-r', self.debugfs,
+ '-o', tracefile]
+
+ blktrace = Popen(btargs, bufsize=1, stdout=PIPE,
+ stderr=open('/dev/null','w'))
+ self.btpid = blktrace.pid
+ btres = None
+ while btres is None:
+ time.sleep(1)
+ btres = blktrace.poll()
+
+ self.btpid = -1
+ if btres:
+ raise BlktraceException(btres,
+ 'blktrace exited with code ' + str(btres))
+
+
+
+ def parse(self, tracefile, getblk):
+
+ if not self.dev:
+ raise Exception("No device specified.")
+
+ offset = self.sysfs.get_partition_start_sector()
+
+ bpargs = ['blkparse', '-i', tracefile]
+ blkparse = Popen(bpargs, bufsize=1, stdout=PIPE)
+
+ bpres = None
+ while bpres is None:
+ output = blkparse.stdout.readline().strip()
+ if output:
+ chunks = output.split()
+ try:
+ # chunks[7] is the sector number
+ blk = list(getblk(int(chunks[7])))
+ print "%s %s %s" %\
+ (' '.join(blk[0:3]),\
+ output.strip(),
+ blk[3].strip())
+ except KeyError:
+ pass
+ except ValueError:
+ pass
+
+ bpres = blkparse.poll()
+
+ if bpres:
+ raise BlktraceException(bpres,
+ 'blkparse exited with code ' + str(bpres))
+
+
diff --git a/contrib/askant/askant/commands.py b/contrib/askant/askant/commands.py
new file mode 100644
index 0000000..ad4639f
--- /dev/null
+++ b/contrib/askant/askant/commands.py
@@ -0,0 +1,333 @@
+"""Command line parsing and processing for askant"""
+
+import fs
+import os
+import sys
+import time
+import about
+import signal
+import tempfile
+from sysfs import Sysfs, SysfsException
+from blktrace import Blktrace, BlktraceException
+from optparse import OptionParser, make_option
+
+class NoFSPluginException(Exception):
+
+ """A generic Exception class for when file system plugins are missing"""
+
+ pass # Nothing special about this exception
+
+class Commands:
+
+ """Provides a convenient interface to the askant commands"""
+
+ def __init__(self):
+
+ self.commands = {}
+ self.parser = OptionParser(usage="%prog COMMAND [options]",
+ version=about.version)
+
+
+ def register_command(self, command):
+ self.commands[str(command)] = command
+
+ def __lookup_command(self, command):
+ try:
+ return self.commands[command]
+ except KeyError, k:
+ return DefaultCommand()
+
+ def __parse_command(self, command, argv):
+ self.parser.prog = "%s %s" %\
+ (self.parser.get_prog_name(), str(command))
+ self.parser.set_usage(command.get_usage())
+ optlst = command.get_options()
+ for o in optlst:
+ self.parser.add_option(o)
+ self.parser.set_description(command.get_help())
+
+ if str(command) != "":
+ argv = argv[1:]
+
+ return self.parser.parse_args(argv)
+
+ def process_argv(self, argv):
+ try:
+ command = self.__lookup_command(argv[1])
+ except IndexError, i:
+ # This exits
+ self.parser.error("No command specified. Try --help")
+
+ options, args = self.__parse_command(command, argv[1:])
+
+ command.do_command(options, args, self)
+
+class Command:
+ def __init__(self):
+ self.options = []
+
+class DefaultCommand(Command):
+ def __str__(self):
+ return ""
+
+ def get_options(self):
+ self.options.append(make_option("-c","--commands",
+ action="store_true",
+ help="Lists available commands"))
+ return self.options
+
+ def get_usage(self):
+ return "%prog COMMAND [options] [args]"
+
+ def get_help(self):
+ return "Askant is a tool for tracing I/O activity in Linux "\
+ "and adding extra file system context by "\
+ "parsing file system data directly."
+
+ def do_command(self, options, args, base):
+ if options.commands:
+ base.parser.print_usage()
+ print "Available commands:"
+ keys = base.commands.keys()
+ keys.sort()
+ for k in keys:
+ print " %10s %s" %\
+ (k, base.commands[k].get_help())
+ print ""
+ print "For command-specific help, use "\
+ "%sCOMMAND --help" % base.parser.get_prog_name()
+ else:
+ base.parser.error("Command not found. Try --commands")
+
+
+class FSCommandTemplate(Command):
+ def __init__(self):
+ self.fsmod = None
+ self.block_table = {}
+ self.block_func = self.__report_hook
+ self.sector_size = None
+ self.offset = None
+ self.options = [
+ make_option("-d","--device", metavar="DEVICE",
+ help="The device to analyse."),
+ ]
+
+ def load_fs_plugin(self, fsname):
+ try:
+ self.fsmod = __import__("fs." + fsname,
+ globals(), locals(), ["fs"])
+ except ImportError, i:
+ raise NoFSPluginException(fsname)
+
+ def parse_fs(self, dev, hook=None):
+ if hook is None:
+ hook = self.block_func
+
+ sfs = Sysfs(dev)
+ offset = sfs.get_partition_start_sector()
+ sector_size = sfs.get_dev_sector_size()
+
+ self.fsmod.set_report_hook(hook)
+
+ try:
+ sfs = Sysfs(dev)
+ self.offset = sfs.get_partition_start_sector()
+ self.sector_size = sfs.get_dev_sector_size()
+ signal.signal(signal.SIGINT, self.fsmod.handle_sigint)
+ self.blk_size = self.fsmod.get_block_size(dev)
+ self.fsmod.parsefs(dev)
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
+ except IOError, i:
+ print >>sys.stderr, str(i)
+ sys.exit(1)
+
+ def set_outfile(self, outfile):
+ self.outfile = outfile
+
+ def trace(self, options, dump_before):
+
+ sfs = Sysfs(options.device)
+ self.offset = sfs.get_partition_start_sector()
+ self.sector_size = sfs.get_dev_sector_size()
+
+ tmstamp = time.strftime('%Y%m%d%H%M%S')
+ self.block_func = self.__report_hook
+ try:
+ self.outfile = open(os.path.join(options.outdir, "blocks-%s" %
+ tmstamp), 'w')
+ except IOError, e:
+ print >>sys.stderr, str(e)
+ sys.exit(1)
+
+ if dump_before:
+ print >>sys.stderr, "Gathering block data..."
+ self.parse_fs(options.device)
+ self.outfile.close()
+
+ if options.debugfs:
+ bt = Blktrace(options.device, options.debugfs)
+ else:
+ bt = Blktrace(options.device)
+
+ os.chdir(options.outdir) # Blktrace doesn't use absolute paths
+ print >>sys.stderr, "Tracing. Hit Ctrl-C to end..."
+ try:
+ signal.signal(signal.SIGINT, bt.handle_sigint)
+ bt.trace(tmstamp)
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
+ except BlktraceException, b:
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
+ print >>sys.stderr, str(b)
+ sys.exit(1)
+
+ if not dump_before:
+ print >>sys.stderr, "Gathering block data..."
+ self.parse_fs(options.device)
+ self.outfile.close()
+
+ print >>sys.stderr, "Matching blocks..."
+ blockdb = open(self.outfile.name, 'r')
+ blocks = {}
+ for l in blockdb.readlines():
+ s = l.split('\t')
+ blocks[int(s[0])] = tuple(s[1:])
+
+ bt.parse(os.path.join(options.outdir, tmstamp), blocks.__getitem__)
+ blockdb.close()
+
+ def __report_hook(self, blk, type, parent, fn):
+ if not fn:
+ fn = ""
+ try:
+ self.outfile.write("%d\t%d\t%s\t%d\t\"%s\"\n" % (
+ ((blk * self.blk_size)/self.sector_size) + self.offset,
+ blk, type, parent, fn))
+ except Exception, e:
+ print str(e)
+
+ def __dict_hook(self, blk, type, parent, fn):
+ self.block_table[
+ ((blk * self.blk_size)/self.sector_size)+self.offset] =\
+ (blk, type, parent, fn)
+
+
+class DumpFSCommand(FSCommandTemplate):
+ def __str__(self):
+ return "dumpfs"
+
+ def get_options(self):
+ self.options.append(make_option("-t","--type", metavar="FSTYPE",
+ help="The type of file system on the device."))
+ self.options.append(make_option("-o","--output", metavar="FILE",
+ default="-",
+ help="File to write output to or '-' for stdout "
+ "(default)"))
+ return self.options
+
+ def get_usage(self):
+ return "%prog [options]"
+
+ def get_help(self):
+ return "Dump fs block information in TSV format."
+
+ def do_command(self, options, args, base):
+
+ if not options.device:
+ base.parser.error("No device specified. Use -d.")
+
+ if not options.type:
+ base.parser.error("No file system type specified. "
+ "Use -t.")
+ try:
+ if options.output != "-":
+ self.set_outfile(open(options.output, 'w'))
+ else:
+ self.set_outfile(sys.stdout)
+ except IOError, e:
+ print >>sys.stderr,\
+ "Unable to open file for writing: %s" %\
+ options.output
+ return
+ except KeyError:
+ pass
+
+ self.load_fs_plugin(options.type)
+ try:
+ self.parse_fs(options.device)
+ except SysfsException, s:
+ base.parser.error(s)
+
+class UnlinksCommand(FSCommandTemplate):
+ def __str__(self):
+ return "unlinks"
+
+ def get_options(self):
+ self.options.append(make_option("-t","--type", metavar="FSTYPE",
+ help="The type of file system on the device."))
+ self.options.append(make_option("-g","--debugfs", metavar="PATH",
+ default="/sys/kernel/debug",
+ help="The path to the debugfs mountpoint. Default "
+ "/sys/kernel/debug"))
+ self.options.append(make_option("-D","--outdir", metavar="DIR",
+ help="Directory to write output to. Required."))
+ return self.options
+
+ def get_usage(self):
+ return "%prog [options]"
+
+ def get_help(self):
+ return "Trace block I/O activity during unlink tests."
+
+ def do_command(self, options, args, base):
+ if not options.device:
+ base.parser.error("No device specified. Use -d.")
+
+ self.device = options.device
+
+ if not options.type:
+ base.parser.error("No file system type specified. "
+ "Use -t.")
+ if not options.outdir:
+ base.parser.error("No output directory specified. "
+ "Use -D.")
+
+ self.load_fs_plugin(options.type)
+ self.trace(options, dump_before=True)
+
+class CreatesCommand(FSCommandTemplate):
+ def __str__(self):
+ return "creates"
+
+ def get_options(self):
+ self.options.append(make_option("-t","--type", metavar="FSTYPE",
+ help="The type of file system on the device."))
+ self.options.append(make_option("-g","--debugfs", metavar="PATH",
+ default="/sys/kernel/debug",
+ help="The path to the debugfs mountpoint. Default "
+ "/sys/kernel/debug"))
+ self.options.append(make_option("-D","--outdir", metavar="DIR",
+ help="Directory to write output to. Required."))
+ return self.options
+
+ def get_usage(self):
+ return "%prog [options]"
+
+ def get_help(self):
+ return "Trace block I/O activity during create tests."
+
+ def do_command(self, options, args, base):
+ if not options.device:
+ base.parser.error("No device specified. Use -d.")
+
+ self.device = options.device
+
+ if not options.type:
+ base.parser.error("No file system type specified. "
+ "Use -t.")
+ if not options.outdir:
+ base.parser.error("No output directory specified. "
+ "Use -D.")
+
+ self.load_fs_plugin(options.type)
+ self.trace(options, dump_before=False)
+
diff --git a/contrib/askant/askant/fs/__init__.py b/contrib/askant/askant/fs/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/contrib/askant/askant/sysfs.py b/contrib/askant/askant/sysfs.py
new file mode 100644
index 0000000..12b7a3b
--- /dev/null
+++ b/contrib/askant/askant/sysfs.py
@@ -0,0 +1,86 @@
+"""
+Provides access to sysfs data pertaining to storage partitions.
+"""
+
+import os
+import os.path
+
+class SysfsException(Exception):
+ """
+ An exception which is raised when things go wrong with Sysfs.
+ """
+ pass # No functionality to add over Exception class
+
+class Sysfs:
+ """
+ Provides access to sysfs data pertaining to storage partitions.
+ """
+ def __init__(self, partition, sysfs_path='/sys'):
+ """
+ Instantiate a Sysfs object. partition should be a path to a
+ disc partition device e.g. /dev/sda3.
+ """
+ self.partition = partition
+ self.device = self.__partition2parent()
+ self.sysfs_path = sysfs_path
+ line = self.__firstline(os.path.join(
+ sysfs_path,'block',
+ os.path.split(self.device)[1],
+ os.path.split(self.partition)[1],
+ 'start'))
+ self.partition_start = int(line)
+ line = self.__firstline(os.path.join(
+ sysfs_path,'block',
+ os.path.split(self.device)[1],
+ 'queue',
+ 'hw_sector_size'))
+ self.dev_sector_size = int(line)
+ line = self.__firstline(os.path.join(
+ sysfs_path,'block',
+ os.path.split(self.device)[1],
+ os.path.split(self.partition)[1],
+ 'size'))
+ self.partition_size = int(line)
+
+ def __firstline(self, fname):
+ """
+ Read the first line of a file
+ """
+ try:
+ fobj = open(fname, 'r')
+ except IOError:
+ raise SysfsException(
+ 'Could not open file "%s" for reading. Please '
+ 'check your kernel is 2.6.25 or later and '
+ 'sysfs is mounted.' % fname)
+ line = fobj.readline()
+ fobj.close()
+ return line
+
+ def __partition2parent(self):
+ """
+ Return the name of a partition device's parent device
+ e.g. /dev/sda3 -> /dev/sda
+ """
+ # This may need thinking about a bit more -
+ # nothing in life can be this simple.
+ return self.partition.rstrip('0123456789')
+
+
+ def get_partition_start_sector(self):
+ """
+ Look up the start sector of the partition.
+ """
+ return self.partition_start
+
+ def get_dev_sector_size(self):
+ """
+ Look up the size of the device's sectors.
+ """
+ return self.dev_sector_size
+
+ def get_partition_size(self):
+ """
+ Look up the size of the partition.
+ """
+ return self.partition_size
diff --git a/contrib/askant/fsplugins/__init__.py b/contrib/askant/fsplugins/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/contrib/askant/fsplugins/gfs2/gfs2.c b/contrib/askant/fsplugins/gfs2/gfs2.c
new file mode 100644
index 0000000..bac9c35
--- /dev/null
+++ b/contrib/askant/fsplugins/gfs2/gfs2.c
@@ -0,0 +1,405 @@
+/**
+ * main.c - Functions for parsing the on-disk structure of a GFS2 fs.
+ * Written by Andrew Price
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <fcntl.h>
+#include <libgfs2.h>
+#include <errno.h>
+
+void (*report_func)(long int blk, char *type, long int parent, char *fn);
+
+/* Define how the block information is reported */
+#define report(b,p,t,n) \
+ ((*report_func)((long int)b, t, (long int)p,n))
+/* (printf("%lu\t%s\t%lu\t%s\n", (long int)b, t, (long int)p, f)) */
+#define report_data(b,p) report(b,p,"D","")
+#define report_leaf(b,p) report(b,p,"L","")
+#define report_indir(b,p) report(b,p,"i","")
+#define report_inode(b,p,n) report(b,p,"I",n)
+
+struct blk_extended {
+ uint64_t parent_blk;
+ char *fname;
+ char *blk;
+};
+
+/* FIXME: A static length block stack is most likely a stupid idea */
+struct blkstack {
+ int top;
+ struct blk_extended blocks[1024];
+};
+
+struct blkstack blk_stack;
+struct gfs2_sb sb;
+uint32_t blk_size;
+off_t max_seek;
+int fd;
+int flag_stop;
+
+/* prog_name and print_it() are needed to satisfy externs in libgfs2 */
+char *prog_name = "askant";
+
+void print_it(const char *label, const char *fmt, const char *fmt2, ...)
+{
+ va_list args;
+
+ va_start(args, fmt2);
+ printf("%s = ", label);
+ vprintf(fmt, args);
+ printf("\n");
+ va_end(args);
+}
+
+/**
+ * Push a block onto the stack
+ */
+static void push_blk(char *blk, uint64_t parent, char *fn)
+{
+ struct blk_extended eblk;
+
+ eblk.blk = blk;
+ eblk.parent_blk = parent;
+ eblk.fname = fn;
+
+ blk_stack.top++;
+ blk_stack.blocks[blk_stack.top] = eblk;
+}
+
+/**
+ * Initialise the block stack
+ */
+static int blk_stack_init(void)
+{
+ blk_stack.top = -1;
+ return 1;
+}
+
+/**
+ * Pop a block from the stack
+ */
+static struct blk_extended pop_blk(void)
+{
+ return blk_stack.blocks[blk_stack.top--];
+}
+
+/**
+ * Read the GFS2 superblock into the global sb variable.
+ * Returns 1 on error, 0 on success.
+ */
+static int read_gfs2_sb(void)
+{
+ off_t offsetsb;
+ off_t offsetres;
+ unsigned char buffer[GFS2_BASIC_BLOCK];
+ ssize_t readsz;
+
+ offsetsb = GFS2_SB_ADDR * GFS2_BASIC_BLOCK;
+ offsetres = lseek(fd, offsetsb, SEEK_SET);
+
+ if (offsetres != offsetsb) {
+ fprintf(stderr, "Could not seek to sb location on device.\n");
+ return 0;
+ }
+
+ readsz = read(fd, buffer, GFS2_BASIC_BLOCK);
+ if (readsz != GFS2_BASIC_BLOCK) {
+ fprintf(stderr, "Could not read superblock.\n");
+ return 0;
+ }
+
+ gfs2_sb_in(&sb, (char *)buffer);
+ if (check_sb(&sb)) {
+ fprintf(stderr, "Not a GFS2 filesystem.\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * Read a block.
+ * blk_offset must be a block number.
+ * The returned pointer must be free'd.
+ */
+static char *read_gfs2_blk(off_t blk_offset)
+{
+ off_t offset;
+ off_t offsetres;
+ ssize_t readsz;
+ char *buffer;
+
+ buffer = (char *)malloc(blk_size);
+ if (!buffer) {
+ fprintf(stderr, "Could not allocate memory for block.\n");
+ return NULL;
+ }
+
+ offset = blk_offset * blk_size;
+
+ offsetres = lseek(fd, offset, SEEK_SET);
+ if (offsetres != offset) {
+ fprintf(stderr,
+ "Could not seek to block location: %lu error: %s\n",
+ (long int)blk_offset, strerror(errno));
+ return NULL;
+ }
+
+ readsz = read(fd, buffer, blk_size);
+ if (readsz != blk_size) {
+ fprintf(stderr, "Could not read block: %lu\n",
+ (long int)blk_offset);
+ return NULL;
+ }
+
+ return buffer;
+}
+
+/**
+ * Look at indirect pointers from a starting point in a block.
+ */
+static void do_indirect(char *start, uint16_t height, uint64_t parent)
+{
+ uint64_t ptr;
+ unsigned int i;
+ char *blk;
+
+ for (i = 0; i < blk_size; i += sizeof(uint64_t)) {
+ ptr = be64_to_cpu(*(uint64_t *)(start + i));
+ if (ptr > 0 && ptr < (max_seek / blk_size)) {
+ if (height == 1) {
+ report_data(ptr, parent);
+ } else if (height > 1) {
+ blk = read_gfs2_blk(ptr);
+ if (blk) {
+ report_indir(ptr, parent);
+ do_indirect(blk, height - 1, ptr);
+ free(blk);
+ }
+ }
+ } else {
+ break;
+ }
+ }
+}
+
+/**
+ * Parse count number of dirents from a starting point in a block.
+ */
+static void do_dirents(char *dirents, char *end, uint64_t parent, uint64_t gparent)
+{
+ struct gfs2_dirent dirent;
+ char *di_blk;
+ char *fname;
+
+ while (dirents < end) {
+ gfs2_dirent_in(&dirent, dirents);
+ if (dirent.de_inum.no_addr &&
+ dirent.de_inum.no_addr != parent &&
+ dirent.de_inum.no_addr != gparent &&
+ dirent.de_name_len > 0 &&
+ dirent.de_name_len <= GFS2_FNAMESIZE) {
+
+ fname = (char *)malloc(dirent.de_name_len + 1);
+ if (!fname) {
+ break;
+ }
+
+ memcpy(fname, dirents + sizeof(struct gfs2_dirent),
+ dirent.de_name_len);
+ fname[dirent.de_name_len] = '\0';
+
+ di_blk = read_gfs2_blk(dirent.de_inum.no_addr);
+ if (di_blk) {
+ push_blk(di_blk, parent, fname);
+ }
+ }
+ dirents += dirent.de_rec_len;
+ }
+}
+
+/**
+ * Examine the dirents in a leaf block.
+ * If the leaf is chained, do the chained leaves too.
+ */
+static void do_leaf(char *blk, uint64_t parent, uint64_t gparent)
+{
+ struct gfs2_leaf leaf;
+
+ while (blk) {
+ gfs2_leaf_in(&leaf, blk);
+ do_dirents(blk + sizeof(struct gfs2_leaf), blk + blk_size,
+ parent, gparent);
+ free(blk);
+ if (!leaf.lf_next) {
+ break;
+ }
+
+ blk = read_gfs2_blk(leaf.lf_next);
+ }
+}
+
+/**
+ * Parse leaf pointer data and examine the
+ * dirents in the destination leaf blocks.
+ */
+static void do_leaves(char *start, uint64_t parent, uint64_t gparent)
+{
+ uint64_t ptr;
+ uint64_t prev;
+ unsigned int i;
+ char *blk;
+
+ prev = 0;
+ for (i = 0; i < blk_size - sizeof(struct gfs2_dinode);
+ i += sizeof(uint64_t)) {
+ ptr = be64_to_cpu(*(uint64_t *)(start + i));
+
+ if (ptr >= (max_seek / blk_size)) {
+ break;
+ }
+
+ if (ptr && ptr != prev) {
+ blk = read_gfs2_blk(ptr);
+ if (blk) {
+ report_leaf(ptr, parent);
+ do_leaf(blk, parent, gparent);
+ }
+ prev = ptr;
+ }
+ }
+}
+
+/**
+ * Parse inode data from a block
+ */
+static void do_inode_blk(char *blk, uint64_t parent, char *fname)
+{
+ struct gfs2_dinode di;
+ char *data;
+
+ gfs2_dinode_in(&di, blk);
+ report_inode(di.di_num.no_addr, parent, fname);
+
+ data = (char *)((struct gfs2_dinode *)blk + 1);
+
+ if (di.di_height > 0) {
+ /* Indirect pointers */
+ do_indirect(data, di.di_height, di.di_num.no_addr);
+ } else if (S_ISDIR(di.di_mode) && !(di.di_flags & GFS2_DIF_EXHASH)) {
+ /* Stuffed directory */
+ do_dirents(data, blk + blk_size, di.di_num.no_addr, parent);
+ } else if (S_ISDIR(di.di_mode) &&
+ (di.di_flags & GFS2_DIF_EXHASH) &&
+ !(di.di_height)) {
+ /* Directory, has hashtable, height == 0 */
+ do_leaves(data, di.di_num.no_addr, parent);
+ }
+
+ /* free previously stacked block */
+ free(fname);
+ free(blk);
+}
+
+/**
+ * Get the root dir block and parse the fs
+ * using a stack to keep track of the unvisited
+ * inode blocks.
+ */
+static void parse_fs(void)
+{
+ struct gfs2_inum *root_dir_inum;
+ struct gfs2_inum *master_dir_inum;
+ struct blk_extended blk;
+ char *root_blk;
+ char *master_blk;
+
+ flag_stop = 0;
+
+ root_dir_inum = &(sb.sb_root_dir);
+ master_dir_inum = &(sb.sb_master_dir);
+
+ root_blk = read_gfs2_blk(root_dir_inum->no_addr);
+ master_blk = read_gfs2_blk(master_dir_inum->no_addr);
+ if (!root_blk || !master_blk) {
+ return;
+ }
+
+ push_blk(root_blk, root_dir_inum->no_addr, NULL);
+ while (blk_stack.top >= 0 && !flag_stop) {
+ blk = pop_blk();
+ do_inode_blk(blk.blk, blk.parent_blk, blk.fname);
+ }
+
+ push_blk(master_blk, master_dir_inum->no_addr, NULL);
+ while (blk_stack.top >= 0 && !flag_stop) {
+ blk = pop_blk();
+ /* TODO: Examine each block's magic number instead of assuming
+ * they're inodes. Omitted for now due to time constraints and
+ * the number of GFS2_METATYPE_*s which need catering for.
+ */
+ do_inode_blk(blk.blk, blk.parent_blk, blk.fname);
+ }
+}
+
+/**
+ * Raise a flag to stop the parse loop cleanly
+ */
+void gfs2_stop(void)
+{
+ flag_stop = 1;
+}
+
+/**
+ * Parse a gfs2 file system on a given device
+ */
+int gfs2_parse(char *dev, void (*func)(long int b, char *t, long int p, char *f))
+{
+ report_func = func;
+
+ if (!blk_stack_init()) {
+ return 0;
+ }
+
+ if ((fd = open(dev, O_RDONLY)) < 0) {
+ return 0;
+ }
+
+ if (!read_gfs2_sb()) {
+ close(fd);
+ return 0;
+ }
+
+ blk_size = sb.sb_bsize;
+ max_seek = lseek(fd, 0, SEEK_END);
+
+ parse_fs();
+
+ close(fd);
+
+ return 1;
+}
+
+/**
+ * Return the block size of the gfs2 file system on a given device.
+ */
+uint32_t gfs2_block_size(char *dev)
+{
+ if ((fd = open(dev, O_RDONLY)) < 0) {
+ return 0;
+ }
+
+ if (!read_gfs2_sb()) {
+ close(fd);
+ return 0;
+ }
+ close(fd);
+ return sb.sb_bsize;
+}
diff --git a/contrib/askant/fsplugins/gfs2/gfs2.h b/contrib/askant/fsplugins/gfs2/gfs2.h
new file mode 100644
index 0000000..6c72eb0
--- /dev/null
+++ b/contrib/askant/fsplugins/gfs2/gfs2.h
@@ -0,0 +1,3 @@
+uint32_t gfs2_block_size(char *dev);
+int gfs2_parse(char *dev, void (*func)(long int b, char *t, long int p, char *f));
+void gfs2_stop(void);
diff --git a/contrib/askant/fsplugins/gfs2/gfs2module.c b/contrib/askant/fsplugins/gfs2/gfs2module.c
new file mode 100644
index 0000000..c6a0584
--- /dev/null
+++ b/contrib/askant/fsplugins/gfs2/gfs2module.c
@@ -0,0 +1,104 @@
+#include <Python.h>
+
+#include "gfs2.h"
+
+static PyObject *report_func = NULL;
+
+void report_func_wrapper(long int blk, char *type, long int parent, char *fn)
+{
+ PyObject *arglist;
+ PyObject *result;
+
+ arglist = Py_BuildValue("lsls", blk, type, parent, fn);
+ if (!arglist) {
+ return;
+ }
+ result = PyEval_CallObject(report_func, arglist);
+ Py_DECREF(arglist);
+ if (!result) {
+ return;
+ }
+ Py_DECREF(result);
+}
+
+static PyObject *gfs2_set_report_hook(PyObject *self, PyObject *args)
+{
+ PyObject *result = NULL;
+ PyObject *temp;
+
+ if (PyArg_ParseTuple(args, "O:set_callback", &temp)) {
+ if (!PyCallable_Check(temp)) {
+ PyErr_SetString(PyExc_TypeError, "parameter must be callable");
+ return NULL;
+ }
+ Py_XINCREF(temp);
+ Py_XDECREF(report_func);
+ report_func = temp;
+
+ Py_INCREF(Py_None);
+ result = Py_None;
+ }
+ return result;
+}
+
+static PyObject *gfs2_parsefs(PyObject *self, PyObject *args)
+{
+ char *dev;
+
+ if (!PyArg_ParseTuple(args, "s:parsefs", &dev)) {
+ return NULL;
+ }
+
+ if (!gfs2_parse(dev, &report_func_wrapper)) {
+ return PyErr_SetFromErrno(PyExc_IOError);
+ }
+ Py_INCREF(Py_None);
+
+ return Py_None;
+}
+
+static PyObject *gfs2_get_block_size(PyObject *self, PyObject *args)
+{
+ char *dev;
+ uint32_t blksize;
+
+ if (!PyArg_ParseTuple(args, "s:get_block_size", &dev)) {
+ return NULL;
+ }
+
+ blksize = gfs2_block_size(dev);
+ if (!blksize) {
+ return PyErr_SetFromErrno(PyExc_IOError);
+ }
+
+ PyObject *size = Py_BuildValue("I", blksize);
+ if (!size) {
+ return NULL;
+ }
+ Py_INCREF(size);
+ return size;
+}
+
+static PyObject *gfs2_handle_sigint(PyObject *signum, PyObject *frame)
+{
+ gfs2_stop();
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef GFS2Methods[] = {
+ {"set_report_hook", gfs2_set_report_hook, METH_VARARGS,
+ "Specify a hook function through which to report blocks."},
+ {"parsefs", gfs2_parsefs, METH_VARARGS,
+ "Parses the given block device as a GFS2 file system."},
+ {"get_block_size", gfs2_get_block_size, METH_VARARGS,
+ "Returns the file system block size."},
+ {"handle_sigint", gfs2_handle_sigint, METH_VARARGS,
+ "Handles signal SIGINT."},
+ {NULL, NULL, 0, NULL}
+};
+
+PyMODINIT_FUNC initgfs2(void)
+{
+ (void)Py_InitModule("gfs2", GFS2Methods);
+}
diff --git a/contrib/askant/scripts/askant b/contrib/askant/scripts/askant
new file mode 100755
index 0000000..3375b0c
--- /dev/null
+++ b/contrib/askant/scripts/askant
@@ -0,0 +1,6 @@
+#! /usr/bin/env python
+
+from askant import askant
+
+if __name__ == '__main__':
+ askant.main()
diff --git a/contrib/askant/setup.py b/contrib/askant/setup.py
new file mode 100755
index 0000000..78daff2
--- /dev/null
+++ b/contrib/askant/setup.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+
+from distutils.core import setup, Extension
+from askant import about
+
+setup(name="askant",
+ version=about.version,
+ description="File system performance analysis tool",
+ author="Andrew Price",
+ author_email="andy@andrewprice.me.uk",
+ url="http://andrewprice.me.uk/projects/askant",
+ packages = ['askant','askant.fs'],
+ ext_modules = [Extension("askant.fs.gfs2",
+ sources = ["fsplugins/gfs2/gfs2module.c","fsplugins/gfs2/gfs2.c"],
+ libraries = ["gfs2"],
+ include_dirs=['../gfs2/libgfs2', '../gfs2/include', '../make'],
+ library_dirs=['../gfs2/libgfs2'])],
+ scripts = ['scripts/askant'])
diff --git a/make/defines.mk.input b/make/defines.mk.input
index 0f62d7d..d09adf6 100644
--- a/make/defines.mk.input
+++ b/make/defines.mk.input
@@ -69,6 +69,7 @@ fence_agents ?= @FENCE_AGENTS@
fenceagentslibdir ?= @FENCEAGENTSLIBDIR@
experimental_build ?= @ENABLE_CRACK_OF_THE_DAY@
legacy_code ?= @ENABLE_LEGACY_CODE@
+contrib_code ?= @ENABLE_CONTRIB@
enable_xen ?= @ENABLE_XEN@
without_gnbd-kernel/src ?= @DISABLE_GNBDKERNEL@
without_gfs-kernel/src/gfs ?= @DISABLE_GFSKERNEL@
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2008-08-20 13:22 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-08-20 19:26 master - build: create contrib/ top level section Andrew Price
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).