patch-2.2.0-pre5 linux/scripts/ksymoops/ksymoops.c

Next file: linux/scripts/ksymoops/ksymoops.h
Previous file: linux/scripts/ksymoops/io.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops/ksymoops.c linux/scripts/ksymoops/ksymoops.c
@@ -0,0 +1,678 @@
+/*
+	ksymoops.c.
+
+	Read a kernel Oops file and make the best stab at converting the code to
+	instructions and mapping stack values to kernel symbols.
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+*/
+
+#define VERSION "0.6e"
+
+/*
+
+	Tue Jan  5 19:26:02 EST 1999
+	Version 0.6e
+	Added to kernel.
+
+	Mon Jan  4 09:48:13 EST 1999
+	Version 0.6d
+	Add ARM support.
+
+	Thu Nov 26 16:37:46 EST 1998
+	Version 0.6c
+	Typo in oops_code.
+	Add -c option.
+	Add -1 option.
+	Report if options were specified or defaulted.
+
+	Fri Nov  6 10:38:42 EST 1998
+	Version 0.6b
+	Remove false warnings when comparing ksyms and lsmod.
+
+	Tue Nov  3 23:33:04 EST 1998
+	Version 0.6a
+	Performance inprovements.
+
+	Tue Nov  3 02:31:01 EST 1998
+	Version 0.6
+	Read lsmod (/proc/modules).
+	Ignore addresses 0-4095 when mapping address to symbol.
+	Discard default objects if -o specified.
+	Oops file must be regular.
+	Add "invalid operand" to Oops_print.
+	Move "Using_Version" copy to map.c.
+	Add Makefile defaults for vmlinux, ksyms, objects, System.map, lsmod.
+	Minor adjustment to re for ppc.
+	Minor adjustment to re for objdump lines with <_EIP+xxx>.
+	Convert from a.out to bfd, using same format as ksymoops.
+	Added MIPS.
+	PPC handling based on patches by "Ryan Nielsen" <ran@krazynet.com>
+
+	Wed Oct 28 23:14:55 EST 1998
+	Version 0.5
+	No longer read vmlinux by default, it only duplicates System.map.
+
+	Wed Oct 28 13:47:38 EST 1998
+	Version 0.4
+	Split into separate sources.
+
+	Mon Oct 26 00:01:47 EST 1998
+	Version 0.3c
+	Add alpha (arm) processing.
+
+	Mon Oct 26 00:01:47 EST 1998
+	Version 0.3b
+	Add sparc processing.
+	Handle kernel symbol versions.
+
+	Fri Oct 23 13:11:20 EST 1998
+	Version 0.3
+	Add -follow to find command for people who use symlinks to modules.
+	Add Version_ checking.
+
+	Thu Oct 22 22:28:30 EST 1998
+	Version 0.2.
+	Generalise text prefix handling.
+	Handle messages on Code: line.
+	Format addresses with leading zeroes.
+	Minor bug fixes.
+
+	Wed Oct 21 23:28:48 EST 1998
+	Version 0.1.  Rewrite from scratch in C.
+
+	CREDITS.
+	Oops disassembly based on ksymoops.cc,
+	  Copyright (C) 1995 Greg McGary <gkm@magilla.cichlid.com>
+	m68k code based on ksymoops.cc changes by
+	  Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
+ */
+
+#include "ksymoops.h"
+#include <ctype.h>
+#include <errno.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+char *prefix;
+char *path_nm = "/usr/bin/nm";			/* env KSYMOOPS_NM */
+char *path_find = "/usr/bin/find";		/* env KSYMOOPS_FIND */
+char *path_objdump = "/usr/bin/objdump";	/* env KSYMOOPS_OBJDUMP */
+int debug = 0;
+int errors = 0;
+int warnings = 0;
+
+SYMBOL_SET  ss_vmlinux;
+SYMBOL_SET  ss_ksyms_base;
+SYMBOL_SET *ss_ksyms_module;
+int         ss_ksyms_modules;
+SYMBOL_SET  ss_lsmod;
+SYMBOL_SET *ss_object;
+int         ss_objects;
+SYMBOL_SET  ss_system_map;
+
+SYMBOL_SET  ss_merged;   /* merged map with info from all sources */
+SYMBOL_SET  ss_Version;  /* Version_ numbers where available */
+
+/* Regular expression stuff */
+
+regex_t     re_nm;
+regmatch_t *re_nm_pmatch;
+regex_t     re_bracketed_address;
+regmatch_t *re_bracketed_address_pmatch;
+regex_t     re_unbracketed_address;
+regmatch_t *re_unbracketed_address_pmatch;
+
+static void usage(void)
+{
+	fprintf(stderr, "Version " VERSION "\n");
+	fprintf(stderr, "usage: %s\n", prefix);
+	fprintf(stderr,
+		"\t\t[-v vmlinux]\tWhere to read vmlinux\n"
+		"\t\t[-V]\t\tNo vmlinux is available\n"
+		"\t\t[-o object_dir]\tDirectory containing modules\n"
+		"\t\t[-O]\t\tNo modules is available\n"
+		"\t\t[-k ksyms]\tWhere to read ksyms\n"
+		"\t\t[-K]\t\tNo ksyms is available\n"
+		"\t\t[-l lsmod]\tWhere to read lsmod\n"
+		"\t\t[-L]\t\tNo lsmod is available\n"
+		"\t\t[-m system.map]\tWhere to read System.map\n"
+		"\t\t[-M]\t\tNo System.map is available\n"
+		"\t\t[-s save.map]\tSave consolidated map\n"
+		"\t\t[-d]\t\tIncrease debug level by 1\n"
+		"\t\t[-h]\t\tPrint help text\n"
+		"\t\t[-c code_bytes]\tHow many bytes in each unit of code\n"
+		"\t\t[-1]\t\tOne shot toggle (exit after first Oops)\n"
+		"\t\t<Oops.file\tOops report to decode\n"
+		"\n"
+		"\t\tAll flags can occur more than once.  With the exception "
+			"of -o\n"
+		"\t\tand -d which are cumulative, the last occurrence of each "
+			"flag is\n"
+		"\t\tused.  Note that \"-v my.vmlinux -V\" will be taken as "
+			"\"No vmlinux\n"
+		"\t\tavailable\" but \"-V -v my.vmlinux\" will read "
+			"my.vmlinux.  You\n"
+		"\t\twill be warned about such combinations.\n"
+		"\n"
+		"\t\tEach occurrence of -d increases the debug level.\n"
+		"\n"
+		"\t\tEach -o flag can refer to a directory or to a single "
+			"object\n"
+		"\t\tfile.  If a directory is specified then all *.o files in "
+			"that\n"
+		"\t\tdirectory and its subdirectories are assumed to be "
+			"modules.\n"
+		"\n"
+		"\t\tIf any of the vmlinux, object_dir, ksyms or system.map "
+		"options\n"
+		"\t\tcontain the string *r (*m, *n, *s) then it is replaced "
+		"at run\n"
+		"\t\ttime by the current value of `uname -r` (-m, -n, -s).\n"
+		"\n"
+		"\t\tThe defaults can be changed in the Makefile, current "
+		"defaults\n"
+		"\t\tare\n\n"
+		"\t\t\t"
+#ifdef DEF_VMLINUX
+		"-v " DEF_LINUX
+#else
+		"-V"
+#endif
+		"\n"
+		"\t\t\t"
+#ifdef DEF_OBJECTS
+		"-o " DEF_OBJECTS
+#else
+		"-O"
+#endif
+		"\n"
+		"\t\t\t"
+#ifdef DEF_KSYMS
+		"-k " DEF_KSYMS
+#else
+		"-K"
+#endif
+		"\n"
+		"\t\t\t"
+#ifdef DEF_LSMOD
+		"-l " DEF_LSMOD
+#else
+		"-L"
+#endif
+		"\n"
+		"\t\t\t"
+#ifdef DEF_MAP
+		"-m " DEF_MAP
+#else
+		"-M"
+#endif
+		"\n"
+		"\t\t\t-c %d\n"	/* DEF_CODE_BYTES */
+		"\t\t\tOops report is read from stdin\n"
+		"\n",
+	DEF_CODE_BYTES
+	       );
+}
+
+/* Check if possibly conflicting options were specified */
+static void multi_opt(int specl, int specu, char type, const char *using)
+{
+	if (specl && specu) {
+		fprintf(stderr,
+			"Warning - you specified both -%c and -%c.  Using '",
+			type, toupper(type));
+		++warnings;
+		if (using) {
+			fprintf(stderr, "-%c %s", type, using);
+			if (type == 'o')
+				fprintf(stderr, " ...");
+			fprintf(stderr, "'\n");
+		}
+		else
+			fprintf(stderr, "-%c'\n", toupper(type));
+	}
+	else if (specl > 1 && type != 'o') {
+		fprintf(stderr,
+			"Warning - you specified -%c more than once.  "
+			"Using '-%c %s'\n",
+			type, type, using);
+		++warnings;
+	}
+	else if (specu > 1) {
+		fprintf(stderr,
+			"Warning - you specified -%c more than once.  "
+			"Second and subsequent '-%c' ignored\n",
+			toupper(type), toupper(type));
+		++warnings;
+	}
+}
+
+/* If a name contains *r (*m, *n, *s), replace with the current value of
+ * `uname -r` (-m, -n, -s).  Actually uses uname system call rather than the
+ * uname command but the result is the same.
+ */
+static void convert_uname(char **name)
+{
+	char *p, *newname, *oldname, *replacement;
+	unsigned len;
+	int free_oldname = 0;
+	static char procname[] = "convert_uname";
+
+	if (!*name)
+		return;
+
+	while ((p = strchr(*name, '*'))) {
+		struct utsname buf;
+		int i = uname(&buf);
+		if (debug)
+			fprintf(stderr, "DEBUG: %s %s in\n", procname, *name);
+		if (i) {
+			fprintf(stderr,
+				"%s: uname failed, %s will not be processed\n",
+				prefix, *name);
+			perror(prefix);
+			++errors;
+			return;
+		}
+		switch (*(p+1)) {
+		case 'r':
+			replacement = buf.release;
+			break;
+		case 'm':
+			replacement = buf.machine;
+			break;
+		case 'n':
+			replacement = buf.nodename;
+			break;
+		case 's':
+			replacement = buf.sysname;
+			break;
+		default:
+			fprintf(stderr,
+				"%s: invalid replacement character '*%c' "
+				"in %s\n",
+				prefix, *(p+1), *name);
+			++errors;
+			return;
+		}
+		len = strlen(*name)-2+strlen(replacement)+1;
+		if (!(newname = malloc(len)))
+			malloc_error(procname);
+		strncpy(newname, *name, (p-*name));
+		strcpy(newname+(p-*name), replacement);
+		strcpy(newname+(p-*name)+strlen(replacement), p+2);
+		p = newname+(p-*name)+strlen(replacement);	/* no rescan */
+		oldname = *name;
+		*name = newname;
+		if (free_oldname)
+			free(oldname);
+		free_oldname = 1;
+		if (debug)
+			fprintf(stderr, "DEBUG: %s %s out\n", procname, *name);
+	}
+	return;
+}
+
+/* Report if the option was specified or defaulted */
+static void spec_or_default(int spec, int *some_spec) {
+	if (spec) {
+		printf(" (specified)\n");
+		if (some_spec)
+			*some_spec = 1;
+	}
+	else
+		printf(" (default)\n");
+}
+
+/* Parse the options.  Verbose but what's new with getopt? */
+static void parse(int argc,
+		  char **argv,
+		  char **vmlinux,
+		  char ***object,
+		  int *objects,
+		  char **ksyms,
+		  char **lsmod,
+		  char **system_map,
+		  char **save_system_map,
+		  char ***filename,
+		  int *filecount,
+		  int *spec_h,
+		  int *code_bytes,
+		  int *one_shot
+		 )
+{
+	int spec_v = 0, spec_V = 0;
+	int spec_o = 0, spec_O = 0;
+	int spec_k = 0, spec_K = 0;
+	int spec_l = 0, spec_L = 0;
+	int spec_m = 0, spec_M = 0;
+	int spec_s = 0;
+	int spec_c = 0;
+
+	int c, i, some_spec = 0;
+	char *p;
+
+	while ((c = getopt(argc, argv, "v:Vo:Ok:Kl:Lm:Ms:dhc:1")) != EOF) {
+		if (debug && c != 'd')
+			fprintf(stderr, "DEBUG: getopt '%c' '%s'\n", c, optarg);
+		switch(c) {
+		case 'v':
+			*vmlinux = optarg;
+			++spec_v;
+			break;
+		case 'V':
+			*vmlinux = NULL;
+			++spec_V;
+			break;
+		case 'o':
+			if (!spec_o) {
+				/* First -o, discard default value(s) */
+				for (i = 0; i < *objects; ++i)
+					free((*object)[i]);
+				free(*object);
+				*object = NULL;
+				*objects = 0;
+			}
+			*object = realloc(*object,
+				((*objects)+1)*sizeof(**object));
+			if (!*object)
+				malloc_error("object");
+			if (!(p = strdup(optarg)))
+				malloc_error("strdup -o");
+			else {
+				(*object)[(*objects)++] = p;
+				++spec_o;
+			}
+			break;
+		case 'O':
+			++spec_O;
+			for (i = 0; i < *objects; ++i)
+				free((*object)[i]);
+			free(*object);
+			*object = NULL;
+			*objects = 0;
+			break;
+		case 'k':
+			*ksyms = optarg;
+			++spec_k;
+			break;
+		case 'K':
+			*ksyms = NULL;
+			++spec_K;
+			break;
+		case 'l':
+			*lsmod = optarg;
+			++spec_l;
+			break;
+		case 'L':
+			*lsmod = NULL;
+			++spec_L;
+			break;
+		case 'm':
+			*system_map = optarg;
+			++spec_m;
+			break;
+		case 'M':
+			*system_map = NULL;
+			++spec_M;
+			break;
+		case 's':
+			*save_system_map = optarg;
+			++spec_s;
+			break;
+		case 'd':
+			++debug;
+			break;
+		case 'h':
+			usage();
+			++*spec_h;
+			break;
+		case 'c':
+			++spec_c;
+			errno = 0;
+			*code_bytes = strtoul(optarg, &p, 10);
+			/* Oops_code_values assumes that code_bytes is a
+			 * multiple of 2.
+			 */
+			if (!*optarg || *p || errno ||
+				(*code_bytes != 1 &&
+				 *code_bytes != 2 &&
+				 *code_bytes != 4 &&
+				 *code_bytes != 8)) {
+				fprintf(stderr,
+					"%s Invalid value for -c '%s'\n",
+					prefix, optarg);
+				++errors;
+				if (errno)
+					perror(" ");
+				*code_bytes = DEF_CODE_BYTES;
+			}
+			break;
+		case '1':
+			*one_shot = !*one_shot;
+			break;
+		case '?':
+			usage();
+			exit(2);
+		}
+	}
+
+	*filecount = argc - optind;
+	*filename = argv + optind;
+
+	/* Expand any requests for the current uname values */
+	convert_uname(vmlinux);
+	if (*objects) {
+		for (i = 0; i < *objects; ++i)
+			convert_uname(*object+i);
+	}
+	convert_uname(ksyms);
+	convert_uname(lsmod);
+	convert_uname(system_map);
+
+	/* Check for multiple options specified */
+	multi_opt(spec_v, spec_V, 'v', *vmlinux);
+	multi_opt(spec_o, spec_O, 'o', *object ? **object : NULL);
+	multi_opt(spec_k, spec_K, 'k', *ksyms);
+	multi_opt(spec_l, spec_L, 'l', *lsmod);
+	multi_opt(spec_m, spec_M, 'm', *system_map);
+
+	printf("Options used:");
+	if (*vmlinux)
+		printf(" -v %s", *vmlinux);
+	else
+		printf(" -V");
+	spec_or_default(spec_v || spec_V, &some_spec);
+	
+	printf("             ");
+	if (*objects) {
+		for (i = 0; i < *objects; ++i)
+			printf(" -o %s", (*object)[i]);
+	}
+	else
+		printf(" -O");
+	spec_or_default(spec_o || spec_O, &some_spec);
+
+	printf("             ");
+	if (*ksyms)
+		printf(" -k %s", *ksyms);
+	else
+		printf(" -K");
+	spec_or_default(spec_k || spec_K, &some_spec);
+
+	printf("             ");
+	if (*lsmod)
+		printf(" -l %s", *lsmod);
+	else
+		printf(" -L");
+	spec_or_default(spec_l || spec_L, &some_spec);
+
+	printf("             ");
+	if (*system_map)
+		printf(" -m %s", *system_map);
+	else
+		printf(" -M");
+	spec_or_default(spec_m || spec_M, &some_spec);
+
+	printf("             ");
+	printf(" -c %d", *code_bytes);
+	spec_or_default(spec_c, NULL);
+
+	if (*one_shot) {
+		printf("             ");
+		printf(" -1");
+	}
+
+	printf("\n");
+
+	if (!some_spec) {
+		printf(
+"You did not tell me where to find symbol information.  I will assume\n"
+"that the log matches the kernel and modules that are running right now\n"
+"and I'll use the default options above for symbol resolution.\n"
+"If the current kernel and/or modules do not match the log, you can get\n"
+"more accurate output by telling me the kernel version and where to find\n"
+"map, modules, ksyms etc.  ksymoops -h explains the options.\n"
+			"\n");
+		++warnings;
+	}
+}
+
+/* Read environment variables */
+static void read_env(const char *external, char **internal)
+{
+	char *p;
+	if ((p = getenv(external))) {
+		*internal = p;
+		if (debug)
+			fprintf(stderr,
+				"DEBUG: env override %s=%s\n",
+				external, *internal);
+	}
+	else {
+		if (debug)
+			fprintf(stderr,
+				"DEBUG: env default %s=%s\n",
+				external, *internal);
+	}
+}
+
+
+int main(int argc, char **argv)
+{
+	char *vmlinux = NULL;
+	char **object = NULL;
+	int objects = 0;
+	char *ksyms = NULL;
+	char *lsmod = NULL;
+	char *system_map = NULL;
+	char *save_system_map = NULL;
+	char **filename;
+	int filecount = 0;
+	int spec_h = 0;		/* -h was specified */
+	int code_bytes = DEF_CODE_BYTES;
+	int one_shot = 0;
+	int i, ret;
+
+	prefix = *argv;
+	setvbuf(stdout, NULL, _IONBF, 0);
+
+#ifdef DEF_VMLINUX
+	vmlinux = DEF_LINUX;
+#endif
+#ifdef DEF_OBJECTS
+	{
+		char *p;
+		object = realloc(object, (objects+1)*sizeof(*object));
+		if (!object)
+			malloc_error("DEF_OBJECTS");
+		if (!(p = strdup(DEF_OBJECTS)))
+			malloc_error("DEF_OBJECTS");
+		else
+			object[objects++] = p;
+	}
+#endif
+#ifdef DEF_KSYMS
+	ksyms = DEF_KSYMS;
+#endif
+#ifdef DEF_LSMOD
+	lsmod = DEF_LSMOD;
+#endif
+#ifdef DEF_MAP
+	system_map = DEF_MAP;
+#endif
+
+	parse(argc,
+	      argv,
+	      &vmlinux,
+	      &object,
+	      &objects,
+	      &ksyms,
+	      &lsmod,
+	      &system_map,
+	      &save_system_map,
+	      &filename,
+	      &filecount,
+	      &spec_h,
+	      &code_bytes,
+	      &one_shot
+	     );
+
+	if (spec_h && filecount == 0)
+		return(0);	/* just the help text */
+
+	if (errors)
+		return(1);
+
+	if (debug)
+		fprintf(stderr, "DEBUG: level %d\n", debug);
+
+	read_env("KSYMOOPS_NM", &path_nm);
+	read_env("KSYMOOPS_FIND", &path_find);
+	read_env("KSYMOOPS_OBJDUMP", &path_objdump);
+
+	re_compile_common();
+	ss_init_common();
+
+	read_vmlinux(vmlinux);
+	read_ksyms(ksyms);
+	/* No point in reading modules unless ksyms shows modules loaded */
+	if (ss_ksyms_modules) {
+		expand_objects(object, objects);
+		for (i = 0; i < ss_objects; ++i)
+			read_object(ss_object[i].source, i);
+	}
+	else if (objects)
+		printf("No modules in ksyms, skipping objects\n");
+	/* No point in reading lsmod without ksyms */
+	if (ss_ksyms_modules || ss_ksyms_base.used)
+		read_lsmod(lsmod);
+	else if (lsmod)
+		printf("No ksyms, skipping lsmod\n");
+	read_system_map(system_map);
+	merge_maps(save_system_map);
+
+	/* After all that work, it is finally time to read the Oops report */
+	ret = Oops_read(filecount, filename, code_bytes, one_shot);
+
+	if (warnings || errors) {
+		printf("\n");
+		if (warnings)
+			printf("%d warning%s ",
+			       warnings, warnings == 1 ? "" : "s");
+		if (warnings && errors)
+			printf("and ");
+		if (errors)
+			printf("%d error%s ", errors, errors == 1 ? "" : "s");
+		printf("issued.  Results may not be reliable.\n");
+		if (!ret)
+			return(1);
+	}
+
+	return(ret);
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov