Skip site navigation (1)Skip section navigation (2)
Date:      24 Feb 1999 22:04:50 -0600
From:      Joel Ray Holveck <joelh@gnu.org>
To:        current@FreeBSD.ORG
Subject:   Re: NFS Problems
Message-ID:  <861zjfcen1.fsf@detlev.UUCP>
In-Reply-To: Mike Smith's message of "Mon, 22 Feb 1999 14:20:42 -0800"
References:  <199902222220.OAA00897@dingo.cdrom.com>

next in thread | previous in thread | raw e-mail | index | archive | help
>> This reminds me; do we have a utility to reference wmesg strings back
>> to the code that sets them, a la TAGS?  Would this be useful?
> No, and yes respectively.

Okay, I've got an early version written.  It's got some fairly
substantial TODO's, and needs a fair bit of cleanup.  I would
appreciate any comments anybody has.

-----cut here-----
#! /usr/bin/perl -w

# Copyright (c) 1999 Joel Ray Holveck.  All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $Id$

# NAME
#      wtags - generate wchan tag file
#
# SYNOPSIS
#      wtags [-cegw] [-v] [path]
#
# DESCRIPTION
#      wtags scans a 4.4BSD kernel source tree and creates a database
#      listing all the wchan's which are explicitly specified to
#      tsleep(9) or similar functions.  This is useful for identifying
#      where in the kernel a process may be hung.
#
#      The source tree to be searched may be specified with path, or
#      the current directory is used.  Subdirectories are always
#      scanned, and symbolic links are always followed.
#
#      The options are as follows:
#
#      -c      Generate ctags(1)-compatible output.  Output is appended
#              to the file "tags" in the current directory.  This file
#              is typically used with vi(1).
#
#      -e      Generate etags(1)-compatible output.  Output is appended
#              to the file "TAGS" in the current directory.  This file
#              is typically used with Emacs(1).
#
#      -g      Generate gtags(1)-compatible output.  Output is appended
#              to the file "GSYMS" in the current directory.  This file
#              is typically used with the global(1) tags system by
#              Shigio Yamaguchi, which may be used with vi(1), Emacs(1),
#              or other systems.
#
#      -w      Generate a native file format (described below).  This
#              format is designed to be easily read by humans or machines,
#              but no utilities currently use it.  -w is the default if
#              no other output format is specified.
#
#      -v      Generate warnings for many cases when a possible call to
#              tsleep(9) or a related function is found, but a wchan
#              could not be isolated.  There are normally many of these
#              in correct code; one version of wtags produced 83 such
#              diagnostics on the 4.4BSD-Lite kernel.  See DIAGNOSTICS,
#              below.
#
#      wtags will only recognize string literals for wchan arguments.
#      A function (such as lf_setlock) which uses a string constant
#      instead, or one (such as ttread) which uses one of a few known
#      possibilities selected via :? or another mechanism, may have a
#      comment such as /* WCHAN: lockf */ on the line in question.
#      wtags will then use the indicated channel, and ignore any tsleep
#      call on that line.
#
# FILES
#      /sys    Traditional kernel source location
#      WTAGS   Default output file, used with -w or if no other
#              tag file is specified
#      TAGS    Emacs(1) tags output file, used with -e
#      tags    vi(1) tags file, used with -c
#      GSYMS   global(1) tags file, used with -g
#
# DIAGNOSTICS
#      If the -v option is specified, then whenever tsleep (or another
#      function that uses wchan) is written in the source file, but no
#      wchan can be found, a diagnostic is printed.  These diagnostics
#      do not always properly describe the issue.
#
#      There is one exception to this: if the item appears to be a
#      reference to tsleep from within a comment (using a heuristic),
#      then no diagnostic is printed.
#
# SEE ALSO
#      ps(1), etags(1), ctags(1), global(1), tsleep(9), glimpse(1)
#
# NOTES
#      wtags was designed under FreeBSD.  Any contributions to allow it
#      to work with other OS's would be appreciated.
#
#      wtags is written for Perl 5.  It may break under other versions
#      of Perl.
#
# HISTORY
#      wtags was originally written on 22 February, 1999 for FreeBSD 4.0 by
#      Joel Ray Holveck <joelh@gnu.org>.
#
# BUGS
#      -e and -g are not yet implemented.  Notes on the respective file
#      formats are in the source.
#
#      Presently, only calls to tsleep, asleep, ttysleep, and lockinit are
#      scanned.  A list of other calls to be scanned is in the source.
#
#      Mentions of tsleep in strings are not skipped, and will normally
#      generate a diagnostic (if -v is specified).

# TODO:
#
# 0. Fix multiline functionality.  Try scanning /sys/kern/subr_log.c for
# an example.
#
# 1. Add etags(1) support.  Since I use Emacs so often, this was my original
# goal.  I will describe the file format for Emacs's TAGS here:
# Each file is listed separately.  At the beginning of the file's entries
# is a formfeed, newline, the complete filename, and the number of bytes
# until the next entry, all terminated by a newline.  Example:
#   ^L
#   /usr/local/src/emacs-20.3/src/alloca.c,785
# The bytecount is from after the newline to before the next formfeed
# (or EOF).  Each item in the file is on a separate line.  If the tag has
# a name, then the line contains the search string, a del (\177), the
# tag name, a ^A (\001), the line number, a comma, and the byte number.
# This means that we will probably be using something along the lines of:
#   tsleep^?wait^A1132,29901
# This will make Emacs search for "tsleep" when the match is used.
# (If the item has no name, then the name and the ^A are elided.)
#
# 2. Add gtags(1) support.  Specifically, these should be added to GSYMS.
# I still need to do more research on the GSYMS file format.  It seems
# that each record includes the name of the tag, the filename, and a
# comma-separated list of line numbers where the tag appears.  If a tag
# appears in more than one file, then more than one entry is created.
#
# 3. Add support for more sleep calls.  I need to change out of
# hardcoded regexps first.  As of FreeBSD 4.0-CURRENT (24 Feb 1999), 
# the calls in question include usbd_bulk_transfer (from
# /sys/dev/usb/usbdi_util.c), waitforit (from /sys/i386/isa/matcd/matcd.c),
# wdsleep (from /sys/i386/isa/wd.c and /sys/pc98/pc98/wd.c), wtwait
# (from /sys/i386/isa/wt.c), BPF_SLEEP (from /sys/net/bpf.c), and
# vm_page_sleep and vm_page_asleep (both from /sys/vm/vm_page.c).

use strict;
use Getopt::Std;
use vars qw($opt_w $opt_c $opt_v);

# A hash indexing wchan strings to a list of locations.  The location
# format currently is filename\tlineno, but this may change (probably
# to append ",byteno") when etags support is added.
# Current example:
#    "slock"-> [ "/sys/kern/kern_lock.c\t557" ]
# future example:
#    "slock"-> [ "/sys/kern/kern_lock.c\t557,14263" ]
# it is also likely that etags support will use a completely different
# system.
my %wchan;

# a hash indexing location strings to the function name being used (eg,
# "kern_lock.c\t557"->"tsleep")
my %sleepcall;

# a hash relating sleep calls to the argument offset of the
# wchan string
# FIXME The regexes that scan for these calls should be built
# from this.
my %wchanarg;
$wchanarg{"tsleep"}=2;
$wchanarg{"asleep"}=2;
$wchanarg{"ttysleep"}=3;
$wchanarg{"lockinit"}=2;

# $prog_label is prefixed on all warnings and error messages.  to
# comply with standards, it should be "wtags: ", but leaving it empty
# allows you to use emacs's compile-mode with its output to check the
# source of each warning message against the offending kernel source.
# my $prog_label = "wtags: ";
my $prog_label = "";

sub scanfile {
    my ($file) = @_;
    open FILE, $file or warn "$prog_label$file: can't open: $!; skipped\n";
    while (<FILE>) {
	# FIXME Isn't recognized on subsequent lines
	m,/\*\s*WCHAN:\s*([A-Za-z0-9]+)\s*\*/, && goto EXPLICIT_WCHAN;
	/\b(tsleep|asleep|ttysleep|lockinit)(.*)/ || next;
	my $looks_like_comment;
	my $sleepcall = $1;	# Saved to add to %sleepcall
	my $rest = $2;

	# This matches either /* or a line starting with a * (for
	# boxed comments).
	# FIXME This doesn't work for calls within calls.
	$looks_like_comment = ((m,^\s*/?\*,) ? 1 : 0);
	$_ = $rest;

      GOT_SLEEP:
        # We keep the current arg number in $arg.  If we are in a
        # comment, then set $incomment to 1.  parens is the current
        # parens level.  Inside the tsleep call's parens is level 0.

	my ($curarg, $incomment, $parens, $lines_scanned) = (0, 0, -1, 0);
	while ($curarg < $wchanarg{$sleepcall}) {
	    
	    # Handle this first so it can next; to itself.
	    if ($incomment) {
		if (m,\*/(.*),) {
		    $_ = $1;
		    $incomment = 0;
		} else {
		    if (++$lines_scanned >= 5) {
			$opt_v and warn "$prog_label$file: $.: tsleep call over five lines; skipped\n";
			goto PARSE_FAILED;
		    }
		    $_ = <FILE>;
		    if (!defined $_) {
			$opt_v and warn "$prog_label$file: $.: early EOF hit; continuing";
			goto PARSE_FAILED;
		    }
		    next;
		}
	    }
	
	    # Parse out the following: /*, */, (, ), ',', and 'tsleep'.
	    # FIXME Doesn't exclude tokens within strings
	    if (m:.*?(/\*|\*/|\(|\)|,|\btsleep|\basleep|\bttysleep|\blockinit)(.*)$:) {
		if ($1 eq "/*") {
		    $incomment = $1;
		} elsif ($1 eq "*/") {
		    # End of comment hit
		    goto PARSE_FAILED;
		} elsif ($1 eq "(") {
		    ++$parens;
		} elsif ($1 eq ")") {
		    $parens--;
		    if ($parens < 0) {
			# Suppress the warning if there is a comment
			# that includes a reference to tsleep just
			# like this one does.
			if (!($looks_like_comment && $curarg == 0)) {
			    $opt_v and warn "$prog_label$file: $.: tsleep call with too few args; skipped\n";
			}
			goto PARSE_FAILED;
		    }
		} elsif ($1 eq ",") {
		    $parens == 0 and $curarg++;
		} elsif ($1 eq "*/") {
		    # There was a reference to tsleep within a comment.
		    goto PARSE_FAILED;
		} else {	# new tsleep call
		    $opt_v and warn "$prog_label$file: $.: tsleep called within tsleep\n";
		    $sleepcall = $1;
		    $_ = $2;
		    goto GOT_SLEEP;
		}
		$_ = $2;
	    } else {
		# This is taken out because it probably is not useful.
		# if ($parens == -1) {
		#     # This was probably a reference to tsleep, rather than
		#     # a call.
		#     goto PARSE_FAILED;
		# }
		$_ = <FILE>;	# get the next line
		if (!defined $_) {
		    $opt_v and warn "$prog_label$file: $.: early EOF hit; continuing";
		    goto PARSE_FAILED;
		}
		$looks_like_comment = ((m,^\s*/?\*,) ? 1 : 0); # mostly for switching to the next tsleep call
		if (++$lines_scanned >= 5) {
		    # This is probably a sign of a misparse, or incomplete code
		    # (say, in an #if 0 or something).
		    $opt_v and warn "$prog_label$file: $.: tsleep call over five lines; skipped\n";
		    goto PARSE_FAILED;
		}
	    }
	}
	
	# We are now looking at the correct argument.
	# FIXME Handle looking at a comment
	while (/^\s*$/) {
	    # End of line was after the comma; skip blank lines.
	    $_ = <FILE>;
	    if (!defined $_) {
		$opt_v and warn "$prog_label$file: $.: early EOF hit; continuing";
		goto PARSE_FAILED;
	    }
	}
	if (! m,^\s*"([^\042]*)",) { # Use octal to keep quotes matched for emacs
	    $opt_v and warn "$prog_label$file: $.: wmesg is not a string literal; skipped\n";
	    goto PARSE_FAILED;
	}
      EXPLICIT_WCHAN:
	my $wmesg = $1;
	if (defined $wchan{$wmesg}) {
	    push @{ $wchan{$wmesg} }, ("$file\t$.");
	} else {
	    $wchan{$wmesg} = [ "$file\t$." ];
	}
	$sleepcall{$wmesg} = $sleepcall;
      PARSE_FAILED:
    }
    close FILE;
}

sub scandir {
    my ($wd, $glob) = @_;
    my $file;
    my @flist;
    print "==> $wd\n";
    @flist = glob("$wd/$glob");
    defined @flist or warn "${prog_label}can't scan $wd: $!; skipped\n";
    foreach $file (@flist) {
	if (-d $file) {
	    scandir($file,$glob);
	} else {
	    $file =~ /.c$/ and scanfile($file);
	}
    }
}

getopts('cvw');
$opt_c or $opt_w = 1;		# -w is the default output format
$opt_w and (open WTAGS, ">WTAGS" or die "${prog_label}can't create WTAGS:$!\n");
$opt_c and (open CTAGS, ">>tags" or die "${prog_label}can't append to tags:$!\n");

# Use the output of `pwd` as the directory if nothing was specified on the
# command line.
my $wd = ((defined $ARGV[0])?$ARGV[0]:`pwd`);

$wd =~ s,/?\n?$,,;	      # Strip off any terminating slash and/or newline
scandir($wd, "*");
# FIXME This loop should be built and eval'd.
foreach my $wchan (sort keys %wchan) {
    $opt_w && print WTAGS "\f\n$wchan\n";
    foreach my $spot (@{ $wchan{$wchan} }) {
	# Should I use a regex for ctags instead?
        $opt_c && print CTAGS "$wchan\t$spot\n";
	$opt_w && print WTAGS "$spot\n";
    }
}

close CTAGS;
close WTAGS;
__END__
-----cut here-----

-- 
Joel Ray Holveck - joelh@gnu.org
   Fourth law of programming:
   Anything that can go wrong wi
sendmail: segmentation violation - core dumped


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-current" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?861zjfcen1.fsf>