Re: [evlog-dev] [PATCH] Resolve kernel addresse to symbols in format "[symbolname +offset/size]"

Hien Q Nguyen <[email protected]> Fri, 10 Jan 2003 10:50:46 -0800
Newsgroups gmane.linux.kernel.event-logging
Message-ID <[email protected]>



Hi Yi,

Thanks for your patch, you have been a great contributor to our project.

On our enhancement list, we have stated that this feature (resolving kernel
address) would be added to the evlogd daemon.  However, we do have second
thought now on where would we want to implement this (evlogd or evlview).
There are pros and cons in both implementations, let me try to list some of
them

evlogd implementation
---------------------
Pros
----
1. Greatly help in the remote logging scenario, since the consolidator does
not need the system maps from the remote clients.

Cons
----
1. Taxing evlogd.
2. More of similar looks up in the future will tax evlogd even more.
3. Event data are modified before logged (if a wrong system map is
provided, however you did cover it with the -2 option)

evlview implementation
----------------------
Pros
----
1. Basically doing all the works offline (hmm, wrong choice of words).
2. Adding more of similar looks up in the future will not impact overall
system performance.
3. Event data are in it true form in the log, it is up to the interpreter
(elview in this case) to interpret it.

Cons
----
1. Need to provide a solution for remote log.


I would like to start some discussion on this topic. Please feel free to
add your pros/cons on the list.

One could argue that why could klogd live with its implementation. My
answer is because it has too (since syslog messages is a text base log), if
it has an option to resolve the kernel address offline I think it probably
would :-)

Best regards, Hien.


Hien Nguyen
[email protected]
(503) 578-4347


|---------+-------------------------------------------->
|         |           "Zhu, Yi" <[email protected]>     |
|         |           Sent by:                         |
|         |           [email protected]|
|         |           ceforge.net                      |
|         |                                            |
|         |                                            |
|         |           01/10/2003 04:43 AM              |
|         |                                            |
|---------+-------------------------------------------->
  >-----------------------------------------------------------------------------------------------|
  |                                                                                               |
  |       To:       "'[email protected]'"                                    |
  |        <[email protected]>                                               |
  |       cc:                                                                                     |
  |       Subject:  [evlog-dev] [PATCH] Resolve kernel addresse to symbols in format "[symbolname |
  |        +offset/size]"                                                                         |
  |                                                                                               |
  |                                                                                               |
  >-----------------------------------------------------------------------------------------------|




Hi,

I implement resolving kernel addresses to symbols in evlogd daemon in the
following patch according to Enhancements to existing features item 2.
Kernel symbols can be logged in strings as "[<aaaaaa>]", where aaaaaa is
the
address.  evlogd replaces these with "[symbolname+offset/size]" in the
output line - symbolname, offset, and size come from the kernel symbol
table. I also add additional 3 switch for evlogd which are the same as
klogd.

evlogd -x:  disable this feature
evlogd -2:  log the original message contains the kernel address as well
as message converted to symbols, so there will be two items
evlogd -k <system map file>:  user can specify the system map file used to
resolve the kernel address (of course, it should match the current kernel
version)

Files ksym.c and ksyms.h are borrowed from sysklogd-1.4.1. And I do some
tweaking bug fixing.

----------BEGIN------------
diff -Naur evlog/user/cmd/evlogd/evlogd.c
evlog-new/user/cmd/evlogd/evlogd.c
--- evlog/user/cmd/evlogd/evlogd.c  2002-12-20 09:51:41.000000000 +0800
+++ evlog-new/user/cmd/evlogd/evlogd.c    2003-01-10 19:22:49.000000000
+0800
@@ -54,6 +54,7 @@
 #include "evlog.h"
 #include "evl_common.h"
 #include "callback.h"
+#include "ksyms.h"

 #define __LIBRARY__
 #include <linux/unistd.h>
@@ -208,7 +209,10 @@
 static char *PidFile = "/var/run/evlogd.pid";

 extern int be_cnt;
-static int defaultPosixLog = 1;    /* posix log is default behavior */
+static int defaultPosixLog = 1;    /* posix log is default behavior */
+static int lookup_symbol = 1;      /* resolve kernel addresses to symbols
*/
+static int symbol_twice = 0; /* if 1, print original kernel address again
*/
+static char * system_map = NULL;/* your own System.map instead of the
default */

 main(int argc, char **argv)
 {
@@ -245,7 +249,7 @@
      void sigChild_handler();
      void NewSIGAction();

-     while ((c = getopt(argc, argv, "fum:")) != EOF) {
+     while ((c = getopt(argc, argv, "fum:x2k:")) != EOF) {
            switch (c) {
            case 'f':
                  bg = 0;
@@ -256,6 +260,15 @@
            case 'm':
                  max_sysuid = atoi(optarg);
                  break;
+           case 'x':
+                 lookup_symbol = 0;
+                 break;
+           case '2':
+                 symbol_twice = 1;
+                 break;
+           case 'k':   /* kernel symbol file to replace the default
*/
+                 system_map = optarg;
+                 break;
            }
      }

@@ -271,6 +284,12 @@
            fprintf(stderr, "evlogd: Already running.\n");
            exit(1);
      }
+     if ( lookup_symbol ) {
+           /* lookup symbols turned on */
+           lookup_symbol = (InitKsyms(system_map) == 1);
+           if (lookup_symbol == 0)
+                 fprintf(stderr, "Cannot find any symbols, turning
off symbol lookups\n");
+     }
      if (bg) {
            /*
             * Fork a child and let the parent exit. This guarentees
that
@@ -1503,52 +1522,192 @@
 int
 writeKernEvt(int sd, struct posix_log_entry *entry, const char *buf)
 {
+     enum parse_state_enum {
+           PARSING_TEXT,
+           PARSING_SYMSTART,    /* at < */
+           PARSING_SYMBOL,
+           PARSING_SYMEND       /* at ] */
+     };
+
      int n, ret=0;
      char writebuf[POSIX_LOG_ENTRY_MAXLEN];
      unsigned char c;
      sigset_t oldset;
      int sigsBlocked;
+     unsigned long value;
+     auto struct symbol sym;
+     auto char * symbol;
+     char * ptr = (char *)buf;
+     char * line = writebuf;
+     static enum parse_state_enum parse_state = PARSING_TEXT;
+     static char *sym_start;  /* points at the '<' of a symbol */
+     int len;
+     int oldlog_size;
+     int expanded = 0;
+

      /* Mask all signals so we don't get interrupted */
      sigsBlocked = (_evlBlockSignals(&oldset) == 0);
-
-     /* First write the header */
-     if ((n = write(sd, entry, REC_HDR_SIZE)) != REC_HDR_SIZE) {
-           /* socket is broken */
-           fprintf(stderr, "Failed to write the msg header to evlog
daemon.\n");
-           ret = EIO;
-           goto err_exit;
-     }

-     /* then write the variable message body */
 #ifdef POSIX_LOG_TRUNCATE
      if (entry->log_format == POSIX_LOG_STRING
          && (entry->log_flags & POSIX_LOG_TRUNCATE) != 0) {
+
+           /* First write the header */
+           if ((n = write(sd, entry, REC_HDR_SIZE)) != REC_HDR_SIZE) {
+                 /* socket is broken */
+                 fprintf(stderr, "Failed to write the msg header to
evlog daemon.\n");
+                 ret = EIO;
+                 goto err_exit;
+           }
            /*
             * buf contains a string that was truncated to
             * POSIX_LOG_ENTRY_MAXLEN bytes.  Stick a null character at
the
             * end of our copy of the buffer to make a null-terminated
-            * string.
+            * string. We don't try to resolve kernel address to symbol
+            * because there will be always no extra space here.
             */
            bcopy((void *)buf, (void *)writebuf, entry->log_size);
+           /* then write the variable message body */
            writebuf[POSIX_LOG_ENTRY_MAXLEN - 1] = '\0';
-           if ((n = write(sd, buf, entry->log_size)) !=
entry->log_size) {
+           if ((n = write(sd, writebuf, entry->log_size)) !=
entry->log_size) {
                  /* socket is broken */
                  fprintf(stderr, "Failed to write the msg body to
evlog daemon.\n");
                  ret = EIO;
                  goto err_exit;
            }
+           /*
+            * We don't try to resolve kernel addresses to symbols
because
+            * there will be always no extra space here.
+            */
            goto done_exit;
      }
 #endif
-     if (entry->log_size > 0) {
-           if ((n = write(sd, buf, entry->log_size)) !=
entry->log_size) {
-                 /* socket is broken */
-                 fprintf(stderr, "Failed to write the msg body to
evlog daemon.\n");
-                 ret = EIO;
-                 goto err_exit;
+
+     /*
+      * Kernel symbols show up in the input buffer as : "[<aaaaaa>]",
+      * where "aaaaaa" is the address.  These are replaced with
+      * "[symbolname+offset/size]" in the output line - symbolname,
+      * offset, and size come from the kernel symbol table.
+      *
+      * If a message is longer than POSIX_LOG_ENTRY_MAXLEN after we
reslove
+      * the kernel address to symbol, the symbol will not be expanded.
+      * (This should never happen, since the kernel should never generate
+      * messages that long.
+      *
+      * To preserve the original addresses, lines containing kernel
symbols
+      * are output twice, if symbol_twice == 1. Once with the symbols
+      * converted and again with the original text. Just in case somebody
+      * wants to run their own Oops analysis on the evlog, e.g. ksymoops.
+      */
+     if ( !lookup_symbol || (entry->log_format != POSIX_LOG_STRING) )
+           goto write_without_resolve;
+
+     TRACE ("Try resolving the kernel addresses to symbols\n");
+     while ( *ptr && (ptr <= buf + entry->log_size) ) {
+           switch ( parse_state )
+           {
+           case PARSING_TEXT:
+                 if ( *ptr == '[' )
+                       parse_state = PARSING_SYMSTART;
+                 break;
+           case PARSING_SYMSTART:
+                 if ( *ptr == '<' ) {
+                       parse_state = PARSING_SYMBOL;
+                       sym_start = line;
+                 } else
+                       parse_state = PARSING_TEXT;
+                 break;
+           case PARSING_SYMBOL:
+                 if ( *ptr == '>' )
+                       parse_state = PARSING_SYMEND;
+                 else if ( *ptr < '0' ||
+                         *ptr > 'f' ||
+                       ((*ptr > '9') && (*ptr < 'A')) ||
+                             ((*ptr > 'F') && (*ptr < 'a'))
+                       )
+                       parse_state = PARSING_TEXT;
+                 break;
+           case PARSING_SYMEND:
+                 if ( *ptr != ']' ) {
+                       parse_state = PARSING_TEXT;
+                       break;
+                 }
+                 *(line - 1) = '\0';
+                 value = strtoul(sym_start + 1, NULL, 16);
+                 *(line - 1) = '>';  /* put the '>' back */
+                 if ( (symbol = LookupSymbol(value, &sym)) == NULL )
{
+                       TRACE("Cannot find symbol for address
[<%lx>]\n", value);
+                       parse_state = PARSING_TEXT;
+                       break;
+                 }
+                 TRACE("Find symbol [%s] for address [<%lx>]\n",
symbol, value);
+                 if ( entry->log_size + strlen (symbol) + 10 >
+                      POSIX_LOG_ENTRY_MAXLEN ){
+                       /*
+                        * 10 is the approximate max length of the
extra
+                        * space. If we don't have enough space for
the
+                        * symbol, we do not convert it.
+                        */
+                       parse_state = PARSING_TEXT;
+                       break;
+                 }
+                 len = sprintf (sym_start, "%s+%d/%d",
+                              symbol, sym.offset, sym.size);
+                 line = sym_start + len;
+                 expanded = 1;
+                 parse_state = PARSING_TEXT;
+                 break;
+           default:  /* Can't get here! */
+                 parse_state = PARSING_TEXT;
            }
+           *line++ = *ptr++;
+     }
+     *line = '\0';
+
+     if ( !expanded )
+           goto write_without_resolve;
+
+     /* Now write the converted message */
+     /* First write the header */
+     oldlog_size = entry->log_size;
+     entry->log_size = strlen (writebuf) + 1;
+     if ((n = write(sd, entry, REC_HDR_SIZE)) != REC_HDR_SIZE) {
+           /* socket is broken */
+           fprintf(stderr, "Failed to write the msg header to evlog
daemon.\n");
+           ret = EIO;
+           goto err_exit;
+     }
+     if ((n = write(sd, writebuf, entry->log_size)) != entry->log_size) {
+           /* socket is broken */
+           fprintf(stderr, "Failed to write the msg body to evlog
daemon.\n");
+           ret = EIO;
+           goto err_exit;
+     }
+     entry->log_size = oldlog_size;
+
+     if ( !symbol_twice )
+           goto done_exit;  /* no need to log the original addresses */
+
+ write_without_resolve:
+     /* First write the header */
+     if ((n = write(sd, entry, REC_HDR_SIZE)) != REC_HDR_SIZE) {
+           /* socket is broken */
+           fprintf(stderr, "Failed to write the msg header to evlog
daemon.\n");
+           ret = EIO;
+           goto err_exit;
      }
+     if (entry->log_size <= 0)
+           goto done_exit;
+
+     /* then write the variable message body */
+     if ((n = write(sd, buf, entry->log_size)) != entry->log_size) {
+           /* socket is broken */
+           fprintf(stderr, "Failed to write the msg body to evlog
daemon.\n");
+           ret = EIO;
+           goto err_exit;
+     }
+
  done_exit:
      /* The daemon should tell the client that he finishes reading */
      read(sd, &c, sizeof(char));
diff -Naur evlog/user/cmd/evlogd/ksym.c evlog-new/user/cmd/evlogd/ksym.c
--- evlog/user/cmd/evlogd/ksym.c    1970-01-01 08:00:00.000000000 +0800
+++ evlog-new/user/cmd/evlogd/ksym.c      2003-01-10 19:35:44.000000000
+0800
@@ -0,0 +1,920 @@
+/*
+    ksym.c - functions for kernel address->symbol translation
+    Copyright (c) 1995, 1996  Dr. G.W. Wettstein <[email protected]>
+    Copyright (c) 1996 Enjellic Systems Development
+
+    This file is part of the sysklogd package, a kernel and system log
daemon.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ * This file contains functions which handle the translation of kernel
+ * numeric addresses into symbols for the klogd utility.
+ *
+ * Sat Oct 28 09:00:14 CDT 1995:  Dr. Wettstein
+ *   Initial Version.
+ *
+ * Fri Nov 24 12:50:52 CST 1995:  Dr. Wettstein
+ *   Added VERBOSE_DEBUGGING define to make debugging output more
+ *   manageable.
+ *
+ *   Added support for verification of the loaded kernel symbols.  If
+ *   no version information can be be found in the mapfile a warning
+ *   message is issued but translation will still take place.  This
+ *   will be the default case if kernel versions < 1.3.43 are used.
+ *
+ *   If the symbols in the mapfile are of the same version as the kernel
+ *   that is running an informative message is issued.  If the symbols
+ *   in the mapfile do not match the current kernel version a warning
+ *   message is issued and translation is disabled.
+ *
+ * Wed Dec  6 16:14:11 CST 1995:  Dr. Wettstein
+ *   Added /boot/System.map to the list of symbol maps to search for.
+ *   Also made this map the first item in the search list.  I am open
+ *   to CONSTRUCTIVE suggestions for any additions or corrections to
+ *   the list of symbol maps to search for.  Be forewarned that the
+ *   list in use is the consensus agreement between myself, Linus and
+ *   some package distributers.  It is a given that no list will suit
+ *   everyone's taste.  If you have rabid concerns about the list
+ *   please feel free to edit the system_maps array and compile your
+ *   own binaries.
+ *
+ *   Added support for searching of the list of symbol maps.  This
+ *   allows support for access to multiple symbol maps.  The theory
+ *   behind this is that a production kernel may have a system map in
+ *   /boot/System.map.  If a test kernel is booted this system map
+ *   would be skipped in favor of one found in /usr/src/linux.
+ *
+ * Thu Jan 18 11:18:31 CST 1996:  Dr. Wettstein
+ *   Added patch from beta-testers to allow for reading of both
+ *   ELF and a.out map files.
+ *
+ * Wed Aug 21 09:15:49 CDT 1996:  Dr. Wettstein
+ *   Reloading of kernel module symbols is now turned on by the
+ *   SetParanoiaLevel function.  The default behavior is to NOT reload
+ *   the kernel module symbols when a protection fault is detected.
+ *
+ *   Added support for freeing of the current kernel module symbols.
+ *   This was necessary to support reloading of the kernel module
symbols.
+ *
+ *   When a matching static symbol table is loaded the kernel version
+ *   number is printed.
+ *
+ * Mon Jun  9 17:12:42 CST 1997:  Martin Schulze
+ *   Added #1 and #2 to some error messages in order to being able
+ *   to divide them ([email protected])
+ *
+ * Fri Jun 13 10:50:23 CST 1997:  Martin Schulze
+ *   Changed definition of LookupSymbol to non-static because it is
+ *   used in klogd.c, too.
+ *
+ * Fri Jan  9 23:00:08 CET 1998: Martin Schulze <[email protected]>
+ *   Fixed bug that caused klogd to die if there is no System.map
available.
+ *
+ * Sun 29 Mar 18:14:07 BST 1998: Mark Simon Phillips
<[email protected]>
+ *   Switched to fgets() as gets() is not buffer overrun secure.
+ *
+ * Mon Apr 13 18:18:45 CEST 1998: Martin Schulze <[email protected]>
+ *   Modified loop for detecting the correct system map.  Now it won't
+ *   stop if a file has been found but doesn't contain the correct map.
+ *   Special thanks go go Mark Simon Phillips for the hint.
+ *
+ * Mon Oct 12 00:42:30 CEST 1998: Martin Schulze <[email protected]>
+ *   Modified CheckVersion()
+ *   . Use shift to decode the kernel version
+ *   . Compare integers of kernel version
+ *   . extract major.minor.patch from utsname.release via sscanf()
+ *   The reason lays in possible use of kernel flavours which
+ *   modify utsname.release but no the Version_ symbol.
+ *
+ * Sun Feb 21 22:27:49 EST 1999: Keith Owens <[email protected]>
+ *   Fixed bug that caused klogd to die if there is no sym_array
available.
+ *
+ * Tue Sep 12 23:48:12 CEST 2000: Martin Schulze <[email protected]>
+ *   Close symbol file in InitKsyms() when an error occurred.
+ */
+
+
+/* Includes. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <sys/utsname.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <string.h>
+#include "ksyms.h"
+
+#define VERBOSE_DEBUGGING 0
+#ifdef DEBUG2
+#define TRACE(fmt, args...)             fprintf(stdout, fmt, ##args)
+int debugging = 1;
+#else
+#define TRACE(fmt, args...)             /* fprintf(stdout, fmt, ##args) */
+int debugging = 0;
+#endif
+
+
+/* Variables static to this module. */
+struct sym_table
+{
+     unsigned long value;
+     char *name;
+};
+
+static int num_syms = 0;
+static int i_am_paranoid = 0;
+static char vstring[12];
+static struct sym_table *sym_array = (struct sym_table *) 0;
+
+static char *system_maps[] =
+{
+     "/boot/System.map",
+     "/System.map",
+     (char *) 0
+};
+
+
+/* Function prototypes. */
+static char * FindSymbolFile(void);
+static int AddSymbol(unsigned long, char*);
+static void FreeSymbols(void);
+static int CheckVersion(char *);
+static int CheckMapVersion(char *);
+
+
+/**************************************************************************

+ * Function:     InitKsyms
+ *
+ * Purpose:      This function is responsible for initializing and loading
+ *         the data tables used by the kernel address translations.
+ *
+ * Arguements:   (char *) mapfile
+ *
+ *               mapfile:->  A pointer to a complete path
+ *                           specification of the file containing
+ *                           the kernel map to use.
+ *
+ * Return: int
+ *
+ *         A boolean style context is returned.  The return value will
+ *         be true if initialization was successful.  False if not.
+
**************************************************************************/
+
+extern int InitKsyms(mapfile)
+
+     char *mapfile;
+
+{
+     auto char   type,
+                 sym[512];
+
+     auto int version = 0;
+
+     auto unsigned long int address;
+
+     auto FILE *sym_file;
+
+
+     /* Check and make sure that we are starting with a clean slate. */
+     if ( num_syms > 0 )
+           FreeSymbols();
+
+
+     /*
+      * Search for and open the file containing the kernel symbols.
+      */
+     if ( mapfile != (char *) 0 )
+     {
+           if ( (sym_file = fopen(mapfile, "r")) == (FILE *) 0 )
+           {
+                 TRACE("Cannot open map file: %s.", \
+                        mapfile);
+                 return(0);
+           }
+     }
+     else
+     {
+           if ( (mapfile = FindSymbolFile()) == (char *) 0 )
+           {
+                 TRACE("Cannot find map file.");
+                 if ( debugging )
+                       fputs("Cannot find map file.\n", stderr);
+                 return(0);
+           }
+
+           if ( (sym_file = fopen(mapfile, "r")) == (FILE *) 0 )
+           {
+                 TRACE("Cannot open map file.");
+                 if ( debugging )
+                       fputs("Cannot open map file.\n", stderr);
+                 return(0);
+           }
+     }
+
+
+     /*
+      * Read the kernel symbol table file and add entries for each
+      * line.  I suspect that the use of fscanf is not really in vogue
+      * but it was quick and dirty and IMHO suitable for fixed format
+      * data such as this.  If anybody doesn't agree with this please
+      * e-mail me a diff containing a parser with suitable political
+      * correctness -- GW.
+      */
+     while ( !feof(sym_file) )
+     {
+           if ( fscanf(sym_file, "%lx %c %s\n", &address, &type, sym)
+               != 3 )
+           {
+                 TRACE("Error in symbol table input (#1).");
+                 fclose(sym_file);
+                 return(0);
+           }
+           if ( VERBOSE_DEBUGGING && debugging )
+                 fprintf(stderr, "Address: %lx, Type: %c, Symbol:
%s\n",
+                       address, type, sym);
+
+           if ( AddSymbol(address, sym) == 0 )
+           {
+                 TRACE("Error adding symbol - %s.", sym);
+                 fclose(sym_file);
+                 return(0);
+           }
+
+           if ( version == 0 )
+                 version = CheckVersion(sym);
+     }
+
+
+     TRACE("Loaded %d symbols from %s.", num_syms, mapfile);
+     switch ( version )
+     {
+         case -1:
+           TRACE("Symbols do not match kernel version.");
+           num_syms = 0;
+           return(0);
+           break;
+
+         case 0:
+           TRACE("Cannot verify that symbols match " \
+                  "kernel version.");
+           break;
+
+         case 1:
+           TRACE("Symbols match kernel version %s.", vstring);
+           break;
+     }
+
+     fclose(sym_file);
+     return(1);
+}
+
+
+/**************************************************************************

+ * Function:     FindSymbolFile
+ *
+ * Purpose:      This function is responsible for encapsulating the search
+ *         for a valid symbol file.  Encapsulating the search for
+ *         the map file in this function allows an intelligent search
+ *         process to be implemented.
+ *
+ *         The list of symbol files will be searched until either a
+ *         symbol file is found whose version matches the currently
+ *         executing kernel or the end of the list is encountered.  If
+ *         the end of the list is encountered the first available
+ *         symbol file is returned to the caller.
+ *
+ *         This strategy allows klogd to locate valid symbol files
+ *         for both a production and an experimental kernel.  For
+ *         example a map for a production kernel could be installed
+ *         in /boot.  If an experimental kernel is loaded the map
+ *         in /boot will be skipped and the map in /usr/src/linux would
+ *         be used if its version number matches the executing kernel.
+ *
+ * Arguements:   None specified.
+ *
+ * Return: char *
+ *
+ *         If a valid system map cannot be located a null pointer
+ *         is returned to the caller.
+ *
+ *         If the search is succesful a pointer is returned to the
+ *         caller which points to the name of the file containing
+ *         the symbol table to be used.
+
**************************************************************************/
+
+static char * FindSymbolFile()
+
+{
+     auto char   *file = (char *) 0,
+                 **mf = system_maps;
+
+     auto struct utsname utsname;
+     static char symfile[100];
+
+     auto FILE *sym_file = (FILE *) 0;
+
+        if ( uname(&utsname) < 0 )
+        {
+                TRACE("Cannot get kernel version information.");
+                return(0);
+        }
+
+     if ( debugging )
+           fputs("Searching for symbol map.\n", stderr);
+
+     for (mf = system_maps; *mf != (char *) 0 && file == (char *) 0;
++mf)
+     {
+
+           sprintf (symfile, "%s-%s", *mf, utsname.release);
+           if ( debugging )
+                 fprintf(stderr, "Trying %s.\n", symfile);
+           if ( (sym_file = fopen(symfile, "r")) != (FILE *) 0 ) {
+                 if (CheckMapVersion(symfile) == 1)
+                       file = symfile;
+                 fclose(sym_file);
+           }
+           if (sym_file == (FILE *) 0 || file == (char *) 0) {
+                 sprintf (symfile, "%s", *mf);
+                 if ( debugging )
+                       fprintf(stderr, "Trying %s.\n", symfile);
+                 if ( (sym_file = fopen(symfile, "r")) != (FILE *) 0
) {
+                       if (CheckMapVersion(symfile) == 1)
+                             file = symfile;
+                       fclose(sym_file);
+                 }
+           }
+
+     }
+
+     /*
+      * At this stage of the game we are at the end of the symbol
+      * tables.
+      */
+     if ( debugging )
+           fprintf(stderr, "End of search list encountered.\n");
+     return(file);
+}
+
+
+/**************************************************************************

+ * Function:     CheckVersion
+ *
+ * Purpose:      This function is responsible for determining whether or
+ *         the system map being loaded matches the version of the
+ *         currently running kernel.
+ *
+ *         The kernel version is checked by examing a variable which
+ *         is of the form:   _Version_66347 (a.out) or Version_66437
(ELF).
+ *
+ *         The suffix of this variable is the current kernel version
+ *         of the kernel encoded in base 256.  For example the
+ *         above variable would be decoded as:
+ *
+ *               (66347 = 1*65536 + 3*256 + 43 = 1.3.43)
+ *
+ *         (Insert appropriate deities here) help us if Linus ever
+ *         needs more than 255 patch levels to get a kernel out the
+ *         door... :-)
+ *
+ * Arguements:   (char *) version
+ *
+ *               version:->  A pointer to the string which
+ *                           is to be decoded as a kernel
+ *                           version variable.
+ *
+ * Return: int
+ *
+ *                -1:->      The currently running kernel version does
+ *                     not match this version string.
+ *
+ *               0:->  The string is not a kernel version variable.
+ *
+ *               1:->  The executing kernel is of the same version
+ *                     as the version string.
+
**************************************************************************/
+
+static int CheckVersion(version)
+
+     char *version;
+
+
+{
+     auto int    vnum,
+                 major,
+                 minor,
+                 patch;
+
+#ifndef TESTING
+     int kvnum;
+     auto struct utsname utsname;
+#endif
+
+     static char *prefix = { "Version_" };
+
+
+     /* Early return if there is no hope. */
+     if ( strncmp(version, prefix, strlen(prefix)) == 0  /* ELF */ ||
+        (*version == '_' &&
+           strncmp(++version, prefix, strlen(prefix)) == 0 ) /* a.out
*/ )
+           ;
+     else
+           return(0);
+
+
+     /*
+      * Since the symbol looks like a kernel version we can start
+      * things out by decoding the version string into its component
+      * parts.
+      */
+     vnum = atoi(version + strlen(prefix));
+     patch = vnum & 0x000000FF;
+     minor = (vnum >> 8) & 0x000000FF;
+     major = (vnum >> 16) & 0x000000FF;
+     if ( debugging )
+           fprintf(stderr, "Version string = %s, Major = %d, " \
+                  "Minor = %d, Patch = %d.\n", version +
+                  strlen(prefix), major, minor, \
+                  patch);
+     sprintf(vstring, "%d.%d.%d", major, minor, patch);
+
+#ifndef TESTING
+     /*
+      * We should now have the version string in the vstring variable in
+      * the same format that it is stored in by the kernel.  We now
+      * ask the kernel for its version information and compare the two
+      * values to determine if our system map matches the kernel
+      * version level.
+      */
+     if ( uname(&utsname) < 0 )
+     {
+           TRACE("Cannot get kernel version information.");
+           return(0);
+     }
+     if ( debugging )
+           fprintf(stderr, "Comparing kernel %s with symbol table
%s.\n",\
+                  utsname.release, vstring);
+
+     if ( sscanf (utsname.release, "%d.%d.%d", &major, &minor, &patch) <
3 )
+     {
+           TRACE("Kernel send bogus release string `%s'.",
+                  utsname.release);
+           return(0);
+     }
+
+     /* Compute the version code from data sent by the kernel */
+     kvnum = (major << 16) | (minor << 8) | patch;
+
+     /* Failure. */
+     if ( vnum != kvnum )
+           return(-1);
+
+     /* Success. */
+#endif
+     return(1);
+}
+
+
+/**************************************************************************

+ * Function:     CheckMapVersion
+ *
+ * Purpose:      This function is responsible for determining whether or
+ *         the system map being loaded matches the version of the
+ *         currently running kernel.  It uses CheckVersion as
+ *         backend.
+ *
+ * Arguements:   (char *) fname
+ *
+ *               fname:->    A pointer to the string which
+ *                           references the system map file to
+ *                           be used.
+ *
+ * Return: int
+ *
+ *                -1:->      The currently running kernel version does
+ *                     not match the version in the given file.
+ *
+ *               0:->  No system map file or no version
information.
+ *
+ *               1:->  The executing kernel is of the same version
+ *                     as the version of the map file.
+
**************************************************************************/
+
+static int CheckMapVersion(fname)
+
+     char *fname;
+
+
+{
+     int   version;
+     FILE  *sym_file;
+     auto unsigned long int address;
+     auto char   type,
+                 sym[512];
+
+     if ( (sym_file = fopen(fname, "r")) != (FILE *) 0 ) {
+           /*
+            * At this point a map file was successfully opened.  We
+            * now need to search this file and look for version
+            * information.
+            */
+           TRACE("Inspecting %s", fname);
+
+           version = 0;
+           while ( !feof(sym_file) && (version == 0) )
+           {
+                 if ( fscanf(sym_file, "%lx %c %s\n", &address, \
+                           &type, sym) != 3 )
+                 {
+                       TRACE("Error in symbol table input (#2).");
+                       fclose(sym_file);
+                       return(0);
+                 }
+                 if ( VERBOSE_DEBUGGING && debugging )
+                       fprintf(stderr, "Address: %lx, Type: %c, " \
+                           "Symbol: %s\n", address, type, sym);
+
+                 version = CheckVersion(sym);
+           }
+           fclose(sym_file);
+
+           switch ( version )
+           {
+               case -1:
+                 TRACE("Symbol table has incorrect " \
+                       "version number.\n");
+                 break;
+
+               case 0:
+                 if ( debugging )
+                       fprintf(stderr, "No version information " \
+                             "found.\n");
+                 break;
+               case 1:
+                 if ( debugging )
+                       fprintf(stderr, "Found table with " \
+                             "matching version number.\n");
+                 break;
+           }
+
+           return(version);
+     }
+
+     return(0);
+}
+
+
+/**************************************************************************

+ * Function:     AddSymbol
+ *
+ * Purpose:      This function is responsible for adding a symbol name
+ *         and its address to the symbol table.
+ *
+ * Arguements:   (unsigned long) address, (char *) symbol
+ *
+ * Return: int
+ *
+ *         A boolean value is assumed.  True if the addition is
+ *         successful.  False if not.
+
**************************************************************************/
+
+static int AddSymbol(address, symbol)
+
+     unsigned long address;
+
+     char *symbol;
+
+{
+     /* Allocate the the symbol table entry. */
+     sym_array = (struct sym_table *) realloc(sym_array, (num_syms+1) * \
+                                    sizeof(struct sym_table));
+     if ( sym_array == (struct sym_table *) 0 )
+           return(0);
+
+     /* Then the space for the symbol. */
+     sym_array[num_syms].name = (char *)
malloc(strlen(symbol)*sizeof(char)\
+                                      + 1);
+     if ( sym_array[num_syms].name == (char *) 0 )
+           return(0);
+
+     sym_array[num_syms].value = address;
+     strcpy(sym_array[num_syms].name, symbol);
+     ++num_syms;
+     return(1);
+}
+
+
+/**************************************************************************

+ * Function:     LookupSymbol
+ *
+ * Purpose:      Find the symbol which is related to the given kernel
+ *         address.
+ *
+ * Arguements:   (long int) value, (struct symbol *) sym
+ *
+ *         value:->    The address to be located.
+ *
+ *         sym:->            A pointer to a structure which will be
+ *                     loaded with the symbol's parameters.
+ *
+ * Return: (char *)
+ *
+ *         If a match cannot be found a diagnostic string is printed.
+ *         If a match is found the pointer to the symbolic name most
+ *         closely matching the address is returned.
+
**************************************************************************/
+
+char * LookupSymbol(value, sym)
+
+     unsigned long value;
+
+     struct symbol *sym;
+
+{
+     auto int lp;
+
+     auto char *last;
+
+     if (!sym_array)
+           return((char *) 0);
+
+     last = sym_array[0].name;
+     sym->offset = 0;
+     sym->size = 0;
+     if ( value < sym_array[0].value )
+           return((char *) 0);
+
+     for(lp= 0; lp <= num_syms; ++lp)
+     {
+           if ( sym_array[lp].value > value )
+           {
+                 sym->offset = value - sym_array[lp-1].value;
+                 sym->size = sym_array[lp].value - \
+                       sym_array[lp-1].value;
+                 return(last);
+           }
+           last = sym_array[lp].name;
+     }
+
+     return((char *) 0);
+}
+
+
+/**************************************************************************

+ * Function:     FreeSymbols
+ *
+ * Purpose:      This function is responsible for freeing all memory which
+ *         has been allocated to hold the static symbol table.  It
+ *         also initializes the symbol count and in general prepares
+ *         for a re-read of a static symbol table.
+ *
+ * Arguements:  void
+ *
+ * Return: void
+
**************************************************************************/
+
+static void FreeSymbols()
+
+{
+     auto int lp;
+
+     /* Free each piece of memory allocated for symbol names. */
+     for(lp= 0; lp < num_syms; ++lp)
+           free(sym_array[lp].name);
+
+     /* Whack the entire array and initialize everything. */
+     free(sym_array);
+     sym_array = (struct sym_table *) 0;
+     num_syms = 0;
+
+     return;
+}
+
+
+/**************************************************************************

+ * Function:     LogExpanded
+ *
+ * Purpose:      This function is responsible for logging a kernel message
+ *         line after all potential numeric kernel addresses have
+ *         been resolved symolically.
+ *
+ * Arguements:   (char *) line, (char *) el
+ *
+ *         line:->     A pointer to the buffer containing the kernel
+ *               message to be expanded and logged.
+ *
+ *         el:-> A pointer to the buffer into which the expanded
+ *               kernel line will be written.
+ *
+ * Return: void
+
**************************************************************************/
+
+extern char * ExpandKadds(line, el)
+
+     char *line;
+
+     char *el;
+
+{
+     auto char   dlm,
+                 *kp,
+                 *sl = line,
+                 *elp = el,
+                 *symbol;
+
+     char num[15];
+     auto unsigned long int value;
+
+     auto struct symbol sym;
+
+
+     /*
+      * This is as handy a place to put this as anyplace.
+      *
+      * Since the insertion of kernel modules can occur in a somewhat
+      * dynamic fashion we need some mechanism to insure that the
+      * kernel symbol tables get read just prior to when they are
+      * needed.
+      *
+      * To accomplish this we look for the Oops string and use its
+      * presence as a signal to load the module symbols.
+      *
+      * This is not the best solution of course, especially if the
+      * kernel is rapidly going out to lunch.  What really needs to
+      * be done is to somehow generate a callback from the
+      * kernel whenever a module is loaded or unloaded.  I am
+      * open for patches.
+      */
+
+     /*
+      * Early return if there do not appear to be any kernel
+      * messages in this line.
+      */
+     if ( (num_syms == 0) ||
+          (kp = strstr(line, "[<")) == (char *) 0 )
+     {
+#ifdef __sparc__
+           if (num_syms) {
+                 /*
+                  * On SPARC, register dumps do not have the [< >]
characters in it.
+                  */
+                 static struct sparc_tests {
+                       char *str;
+                       int len;
+                 } tests[] = { { "PC: ", 4 },
+                             { " o7: ", 5 },
+                             { " ret_pc: ", 9 },
+                             { " i7: ", 5 },
+                             { "Caller[", 7 }
+                           };
+                 int i, j, ndigits;
+                 char *kp2;
+                 for (i = 0; i < 5; i++) {
+                       kp = strstr(line, tests[i].str);
+                       if (!kp) continue;
+                       kp2 = kp + tests[i].len;
+                       if (!isxdigit(*kp2)) continue;
+                       for (ndigits = 1; isxdigit(kp2[ndigits]);
ndigits++);
+                       if (ndigits != 8 && ndigits != 16) continue;
+                       /* On sparc64, all kernel addresses are in
first 4GB */
+                       if (ndigits == 16) {
+                             if (strncmp (kp2, "00000000", 8))
continue;
+                             kp2 += 8;
+                       }
+                       if (!i) {
+                             char *kp3;
+                             if (ndigits == 16 && kp > line &&
kp[-1L] != 'T') continue;
+                             kp3 = kp2 + 8;
+                             if (ndigits == 16) {
+                                   if (strncmp (kp3, " TNPC:
00000000", 15) || !isxdigit(kp3[15]))
+                                         continue;
+                                   kp3 += 15;
+                             } else {
+                                   if (strncmp (kp3, " NPC: ",
6) || !isxdigit(kp3[6]))
+                                         continue;
+                                   kp3 += 6;
+                             }
+                             for (j = 0; isxdigit(kp3[j]); j++);
+                             if (j != 8) continue;
+                             strncpy(elp, line, kp2 + 8 - line);
+                             elp += kp2 + 8 - line;
+                             value = strtol(kp2, (char **) 0,
16);
+                             if ( (symbol = LookupSymbol(value,
&sym)) ) {
+                                   if (sym.size)
+                                         elp += sprintf(elp,
" (%s+%d/%d)", symbol, sym.offset, sym.size);
+                                   else
+                                         elp += sprintf(elp,
" (%s)", symbol);
+                             }
+                             strncpy(elp, kp2 + 8, kp3 - kp2);
+                             elp += kp3 - kp2;
+                             value = strtol(kp3, (char **) 0,
16);
+                             if ( (symbol = LookupSymbol(value,
&sym)) ) {
+                                   if (sym.size)
+                                         elp += sprintf(elp,
" (%s+%d/%d)", symbol, sym.offset, sym.size);
+                                   else
+                                         elp += sprintf(elp,
" (%s)", symbol);
+                             }
+                             strcpy(elp, kp3 + 8);
+                       } else {
+                             strncpy(elp, line, kp2 + 8 - line);
+                             elp += kp2 + 8 - line;
+                             value = strtol(kp2, (char **) 0,
16);
+                             if ( (symbol = LookupSymbol(value,
&sym)) ) {
+                                   if (sym.size)
+                                         elp += sprintf(elp,
" (%s+%d/%d)", symbol, sym.offset, sym.size);
+                                   else
+                                         elp += sprintf(elp,
" (%s)", symbol);
+                             }
+                             strcpy(elp, kp2 + 8);
+                       }
+                       return el;
+                 }
+           }
+#endif
+           strcpy(el, line);
+           return(el);
+     }
+
+     /* Loop through and expand all kernel messages. */
+     do
+     {
+           while ( sl < kp+1 )
+                 *elp++ = *sl++;
+
+           /* Now poised at a kernel delimiter. */
+             if ( (kp = strstr(sl, ">]")) == (char *) 0 )
+           {
+                 strcpy(el, sl);
+                 return(el);
+           }
+           dlm = *kp;
+           strncpy(num,sl+1,kp-sl-1);
+           num[kp-sl-1] = '\0';
+           value = strtoul(num, (char **) 0, 16);
+           if ( (symbol = LookupSymbol(value, &sym)) == (char *) 0 )
+                 symbol = sl;
+
+           strcat(elp, symbol);
+           elp += strlen(symbol);
+           if ( debugging )
+                 fprintf(stderr, "Symbol: %s = %lx = %s, %x/%d\n", \
+                       sl+1, value, \
+                       (sym.size==0) ? symbol+1 : symbol, \
+                       sym.offset, sym.size);
+
+           value = 2;
+           if ( sym.size != 0 )
+           {
+                 --value;
+                 ++kp;
+                 elp += sprintf(elp, "+%x/%d", sym.offset, sym.size);
+           }
+           strncat(elp, kp, value);
+           elp += value;
+           sl = kp + value;
+           if ( (kp = strstr(sl, "[<")) == (char *) 0 )
+                 strcat(elp, sl);
+     }
+     while ( kp != (char *) 0);
+
+     if ( debugging )
+           fprintf(stderr, "Expanded line: %s\n", el);
+     return(el);
+}
+
+
+/**************************************************************************

+ * Function:     SetParanoiaLevel
+ *
+ * Purpose:      This function is an interface function for setting the
+ *         mode of loadable module symbol lookups.  Probably overkill
+ *         but it does slay another global variable.
+ *
+ * Arguements:   (int) level
+ *
+ *         level:->    The amount of paranoia which is to be
+ *                     present when resolving kernel exceptions.
+ * Return: void
+
**************************************************************************/
+
+extern void SetParanoiaLevel(level)
+
+     int level;
+
+{
+     i_am_paranoid = level;
+     return;
+}
diff -Naur evlog/user/cmd/evlogd/ksyms.h evlog-new/user/cmd/evlogd/ksyms.h
--- evlog/user/cmd/evlogd/ksyms.h   1970-01-01 08:00:00.000000000 +0800
+++ evlog-new/user/cmd/evlogd/ksyms.h     2003-01-10 19:36:16.000000000
+0800
@@ -0,0 +1,35 @@
+/*
+    ksym.h - Definitions for symbol table utilities.
+    Copyright (c) 1995, 1996  Dr. G.W. Wettstein <[email protected]>
+    Copyright (c) 1996 Enjellic Systems Development
+
+    This file is part of the sysklogd package, a kernel and system log
daemon.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* Variables, structures and type definitions static to this module. */
+
+struct symbol
+{
+     char *name;
+     int size;
+     int offset;
+};
+
+
+/* Function prototypes. */
+extern int InitKsyms(char *);
+extern char * LookupSymbol(unsigned long, struct symbol *);
diff -Naur evlog/user/cmd/evlogd/Makefile
evlog-new/user/cmd/evlogd/Makefile
--- evlog/user/cmd/evlogd/Makefile  2002-12-16 10:35:13.000000000 +0800
+++ evlog-new/user/cmd/evlogd/Makefile    2003-01-10 19:18:30.000000000
+0800
@@ -8,7 +8,7 @@
 DEBUG= -g
 LIBS = ../../lib/libevl.a -lnsl -lfl -ldl -lpthread
 CFLAGS =  $(INCLUDEDIRS) $(LIBDIRS) -O $(DEBUG) $(EVL_WRITE_DIRECT)
$(CDEST)
-DOTOS = evlogd.o backendmgr.o
+DOTOS = evlogd.o backendmgr.o ksym.o
 PRODUCTS = evlogd evlogrmtd

 all: $(PRODUCTS) evlogd/test
------------END-------------


----------------------------------------------------------------------------

----------------------------------------------------
Opinions expressed are those of the author and do not represent Intel Corp.

Zhu Yi (Chuyee)
Intel China Software Lab (ICSL)
22nd Floor, ShanghaiMart Tower No. 2299 Yan'an Road(West) Shanghai 200336,
PRC
Tel: 8621-52574545-1261 Fax: 8621-62366119

GnuPG v1.0.6 (GNU/Linux)
http://cn.geocities.com/chewie_chuyee/gpg.txt
1024D/71C34820 C939 2B0B FBCE 1D51 109A  55E5 8650 DB90 71C3 4820




#### evlog-resolve-symbol-1.5.patch has been removed from this note on
January 10 2003 by Hien Q Nguyen




-------------------------------------------------------
This SF.NET email is sponsored by:
SourceForge Enterprise Edition + IBM + LinuxWorld = Something 2 See!
http://www.vasoftware.com