Logo Search packages:      
Sourcecode: lcrash version File versions  Download package

main.c

/*
 * $Id: main.c,v 1.3 2005/02/23 16:53:56 tjm Exp $
 *
 * This file is part of lcrash, an analysis tool for Linux memory dumps.
 *
 * Created by Silicon Graphics, Inc.
 * Contributions by IBM, and others
 *
 * Copyright (C) 1999 - 2005 Silicon Graphics, Inc. All rights reserved.
 * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
 *
 * 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. See the file COPYING for more
 * information.
 */

#include <lcrash.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/utsname.h>

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <getopt.h>

/*
 * getopt_long stuff
 */
static struct option longopts[] = {
      {"bound", required_argument, 0, 'n'},      /* bounds value */
      {"debug", required_argument, 0, 'g'},      /* lcrash debug level */
      {"dump", required_argument, 0, 'd'},       /* dump file */
      {"dump-arch", required_argument, 0, 'a'},  /* architecture of dump */
      {"erase", no_argument, 0, 'e'},            /* erase option */
      {"failsafe", no_argument, 0, 'f'},         /* failsafe option */
      {"help", no_argument, 0, 'h'},             /* help */
      {"info", no_argument, 0, 'i'},             /* dump info */
      {"map", required_argument, 0, 'm'},        /* System.map */
      {"progress", no_argument, 0, 'p'},         /* progress flag */
      {"report", no_argument, 0, 'r'},           /* generate report */
      {"save", required_argument, 0, 's'},       /* save option */
      {"trace", required_argument, 0, 'T'},      /* trace level */
      {"types", required_argument, 0, 't'},      /* kerntypes file */
      {"version", no_argument, 0, 'v'},          /* version info */
      {0, 0, 0, 0}
};

#define OPTSTRING "a:d:efg:hI:iM:m:n:prs:t:T:v"

extern char *optarg;
extern int optind, opterr, optopt;

/*
 * function declarations
 */
void usage(const char*);

/*
 * global variables
 */
char ql_have_terminal;                  /* needed for qlcrash */
FILE *ofp;
int bounds = -1;                        /* bounds value with -n option       */
uint64_t lcrash_debug = 0;
uint64_t def_iter_threshold = 10000;   /* used when following pointers to */
uint64_t iter_threshold     = 10000;   /* avoid endless loops */
uint32_t trace_threshold = 1;

#define ARCH_STRING_ALPHA     KL_ARCH_STR_ALPHA
#define ARCH_STRING_ARM       KL_ARCH_STR_ARM
#define ARCH_STRING_I386      KL_ARCH_STR_I386 
#define ARCH_STRING_IA64      KL_ARCH_STR_IA64 
#define ARCH_STRING_PPC64     KL_ARCH_STR_PPC64
#define ARCH_STRING_S390      KL_ARCH_STR_S390 
#define ARCH_STRING_S390X     KL_ARCH_STR_S390X
#define ARCH_STRING_X86_64    KL_ARCH_STR_X86_64
#define ARCH_STRING_IA64_SN2        KL_ARCH_STR_IA64_SN2

#define DEFAULT_MAPFILE   "/boot/System.map"
#define DEFAULT_KERNTYPES "/boot/Kerntypes"
#define DEFAULT_DUMPFILE  "/dev/mem"

#define _LCRASH_USAGE \
"[OPTION]... [MAP DUMP KERNTYPES]\n"

#define _LCRASH_HELP \
"  -a, --dump-arch ARCH     architecture of dump\n"\
"                           (one of 'i386', 's390', 's390x', 'ia64', 'ppc64',\
                      'x86_64' \n"\
"                           default: same as host architecture)\n"\
"                           default: same as host architecture)\n"\
"  -d, --dump FILE          dump file or (when using -s or -e) dump device \n"\
"                           (default: /dev/mem)\n"\
"  -e, --erase              erase dump from dumpdev\n"\
"  -f, --failsafe           start lcrash in failsafe way\n"\
"                           (i.e. omit certain checks and initializations)\n"\
"  -g, --debug LEVEL        debug level for lcrash\n"\
"  -h, --help               display this help\n"\
"  -I PATH                  path for include files (for libsial)\n"\
"  -i, --info               display dump info\n"\
"  -M PATH                  path for sial macros (for libsial)\n"\
"  -m, --map FILE           System.map of kernel in system dump\n"\
"                           (default: /boot/System.map)\n"\
"  -n, --bound BOUND        non-negative bound (for map.0, map.1 etc.)\n"\
"  -p, --progress           show progress, when retrieving dump\n"\
"  -r, --report             generate report, do not run lcrash interactive\n"\
"  -s, --save DUMPDIR       save dump from dumpdev into DUMPDIR/dump.x\n"\
"  -T, --trace LEVEL        trace level\n"\
"  -t, --types FILE         kerntypes file\n"\
"                           (default: /boot/Kerntypes)\n"\
"  -v, --version            display version info\n"\
"  MAP DUMP KERNTYPES       Specify MAP, DUMP and KERNTYPES at end of line.\n"\
"                           This usage is deprecated!\n\n"\
"  If \'-m\', \'-d\' or \'-t\' are used, their arguments have priority over\n"\
"  the arguments specified with \'MAP DUMP KERNTYPES\'.\n"

/*
 * function definitions
 */
void
usage(const char *prog)
{
      fprintf(stderr, "Usage: %s "_LCRASH_USAGE, prog);
      fprintf(stderr, "Try '%s --help' for more information\n", prog);
      exit(1);
}

void
help(const char *prog)
{
      fprintf(stdout, "Usage: %s "_LCRASH_USAGE, prog);
      fprintf(stdout, "Help:\n"_LCRASH_HELP);
}

extern void rl_register_complete_func(rl_complete_func_t);

/* 
 * main()
 */
int
main(int argc, char **argv)
{
      char *M_val=0, *I_val=0;
      const char *prog;
      int c, longindex;
      int lc_flags=0, kl_flags=0;

      char namelist[LCRASH_OPT_MAX];
      char map[LCRASH_OPT_MAX];
      char dump[LCRASH_OPT_MAX];

      char arch_string[LCRASH_OPT_MAX];
      char dumpdir[LCRASH_OPT_MAX];

      int dumparch = KL_ARCH_UNKNOWN;

      memset(map, 0, LCRASH_OPT_MAX);
      memset(dump, 0, LCRASH_OPT_MAX);
      memset(namelist, 0, LCRASH_OPT_MAX);
      memset(arch_string, 0, LCRASH_OPT_MAX);
      memset(dumpdir, 0, LCRASH_OPT_MAX);


      /* first thing to do when using libklib: set out and err streams
       */
      kl_set_stdout(stdout);
      kl_set_stderr(stderr);

      ofp = stdout;

      if((prog = rindex(argv[0], '/'))){
            ++prog;
      } else {
            prog = argv[0];
      }

      while((c = getopt_long(argc, argv, OPTSTRING, longopts,
                         &longindex)) != -1) {
            switch (c) {
            case 'a': /* arch string to specify dump arch */
                  if(strlen(optarg) >= LCRASH_OPT_MAX){
                        fprintf(KL_ERRORFP,
                              "Error: argument too long\n");
                        return(1);
                  }
                  sprintf(arch_string, "%s", optarg);
                  break;
            case 'd': /* file location of dump file */
                  if(strlen(optarg) >= LCRASH_OPT_MAX){
                        fprintf(KL_ERRORFP,
                              "Error: argument too long\n");
                        return(1);
                  }
                  sprintf(dump, "%s", optarg);
                  break;
            case 'e': /* erase dump from dumpdev */
                  lc_flags |= LC_ERASE_FLG;
                  break;
            case 'f': /* failsafe option */
                  kl_flags |= KL_FAILSAFE_FLG;
                  break;
            case 'g': /* set debug level */
                  set_klib_dbg_components(optarg);
                  break;
            case 'h': /* help command */
                  help(prog);
                  return(0);
            case 'I': /* include directory for sial interpreter */
                  I_val=optarg;
                  break;
            case 'i': /* info command */
                  lc_flags |= LC_INFO_FLG;
                  break;
            case 'M': /* macro directory for sial interpreter */
                  M_val=optarg;
                  break;
            case 'm': /* file location of System.map */
                  if(strlen(optarg) >= LCRASH_OPT_MAX){
                        fprintf(KL_ERRORFP,
                              "Error: argument too long\n");
                        return(1);
                  }
                  sprintf(map, "%s", optarg);
                  break;
            case 'n': /* bounds value */
                  bounds = atoi(optarg);
                  lc_flags |= LC_BOUNDS_FLG;
                  break;
            case 'p': /* progress indicator for dump retrial */
                  lc_flags |= LC_PROGRESS_FLG;
                  break;
            case 'r': /* report flag */
                  lc_flags |= LC_REPORT_FLG;
                  break;
            case 's': /* save dump from dumpdev to dumpdir */
                  if(strlen(optarg) >= LCRASH_OPT_MAX){
                        fprintf(KL_ERRORFP,
                              "Error: argument too long\n");
                        return(1);
                  }
                  sprintf(dumpdir, "%s", optarg);
                  lc_flags |= LC_SAVE_FLG;
                  break;
            case 'T': /* set trace level */
                  trace_threshold = atoi(optarg);
                  kl_set_trace_threshold(trace_threshold);
                  break;
            case 't': /* file location of Kerntypes file */
                  if(strlen(optarg) >= LCRASH_OPT_MAX){
                        fprintf(KL_ERRORFP,
                              "Error: argument too long\n");
                        return(1);
                  }
                  sprintf(namelist, "%s", optarg);
                  break;
            case 'v': /* version information */
                  print_version_info(ofp);
                  return(0);
            default:
                  usage(prog);
            }
      }

      if((lc_flags & (LC_SAVE_FLG | LC_ERASE_FLG | LC_INFO_FLG)) &&
         (!dump[0])){
            fprintf(KL_ERRORFP, "Error: no dump device specified\n");
            return(1);
      }

      if(lc_flags & LC_INFO_FLG){
            exit(kl_print_dump_header(dump));
      }

      /* For compatibilty we accept parameters "mapfile dumpfile kerntypes",
       * but this is deprecated.
       */
      if(optind > -1 && argv[optind]){
            if((argc - optind) != 3){
                  usage(prog);
            }
            if (!map[0]) {
                  if(strlen(argv[optind]) >= LCRASH_OPT_MAX){
                        fprintf(KL_ERRORFP,
                              "Error: argument too long\n");
                        return(1);
                  }
                  sprintf(map, "%s", argv[optind]);
            } 
            if (!dump[0]) {
                  if(strlen(argv[optind+1]) >= LCRASH_OPT_MAX){
                        fprintf(KL_ERRORFP,
                              "Error: argument too long\n");
                        return(1);
                  }
                  sprintf(dump, "%s", argv[optind+1]);
            }
            if (!namelist[0]) {
                  if(strlen(argv[optind+2]) >= LCRASH_OPT_MAX){
                        fprintf(KL_ERRORFP,
                              "Error: argument too long\n");
                        return(1);
                  }
                  sprintf(namelist, "%s", argv[optind+2]);
            }
      }

      /* check for valid combinations of options
       */
      if((lc_flags & LC_REPORT_FLG) && (lc_flags & LC_SAVE_FLG)){
            fprintf(stderr,
                  "Error: -r and -s are invalid together!\n");
            usage(prog);
      }
      if((lc_flags & LC_REPORT_FLG) && (lc_flags & LC_ERASE_FLG)){
            fprintf(stderr,
                  "Error: -r and -e are invalid together!\n");
            usage(prog);
      }
      if((lc_flags & LC_SAVE_FLG) && (lc_flags & LC_ERASE_FLG)){
            fprintf(stderr,
                  "Error: -s and -e are invalid together!\n");
            usage(prog);
      }
      if((lc_flags & LC_BOUNDS_FLG) && (lc_flags & LC_SAVE_FLG)){
            fprintf(stderr,
                  "Error: -n and -s are invalid together!\n");
            usage(prog);
      }
      if((lc_flags & LC_BOUNDS_FLG) && (lc_flags & LC_ERASE_FLG)){
            fprintf(stderr,
                  "Error: -n and -e are invalid together!\n");
            usage(prog);
      }

      /* set input file names according to bounds value. If one of
       * the filenames was set via a command line argument (e.g., 
       * -m mapfile), then leave it. This might come in handy if,
       * for example, you want to specify a custom namelist but go 
       * with the devalut values for the other two filenames.
       * 
       */
      if (lc_flags & LC_BOUNDS_FLG) {
            if (bounds < 0) {
                  fprintf(stderr,
                        "Error: Argument for -n must be >= 0!\n");
                  usage(prog);
            }
            if (!map[0]) {
                  sprintf(map, "map.%d", bounds);
            }
            if (!dump[0]) {
                  sprintf(dump, "dump.%d", bounds);
            }
            if (!namelist[0]) {
                  sprintf(namelist, "kerntypes.%d", bounds);
            }
      }

      /* Make sure we have all three filenames. If we don't have any
       * filenames, then set up for a live system dump analysis. 
       * If we have some filenames but not all of them, then exit 
       * with an error...
       *
       * Note that We get the live filenames here so that we are able 
       * to print them out below. 
       */
      if (!map[0] && !dump[0] && !namelist[0]) {
            if (kl_get_live_filenames(map, dump, namelist)) {
                  fprintf(KL_ERRORFP,
                        "Error: could not get live dump filenames\n");
                  usage(prog);
            }
      } else if (!map[0] || !dump[0] || !namelist[0]) {
            fprintf(KL_ERRORFP,
                  "Error: missing one or more dump filenames\n");
            usage(prog);
      }

      /* print version info
       */
      if (lc_flags ^ (LC_REPORT_FLG|LC_ERASE_FLG|LC_SAVE_FLG)) {
            print_version_info(ofp);
      }

      /* set dumparch according to command line argument (if set)
       */
      if (arch_string[0]) {
            if(!(strcmp(arch_string, ARCH_STRING_ALPHA))){
                  dumparch = KL_ARCH_ALPHA;
            } else if(!(strcmp(arch_string, ARCH_STRING_ARM))){
                  dumparch = KL_ARCH_ARM;       
            } else if(!(strcmp(arch_string, ARCH_STRING_I386))){
                  dumparch = KL_ARCH_I386;
            } else if(!(strcmp(arch_string, ARCH_STRING_IA64))){
                  dumparch = KL_ARCH_IA64;
            } else if(!(strcmp(arch_string, ARCH_STRING_PPC64))){
                  dumparch = KL_ARCH_PPC64;
            } else if(!(strcmp(arch_string, ARCH_STRING_S390))){
                  dumparch = KL_ARCH_S390;
            } else if(!(strcmp(arch_string, ARCH_STRING_S390X))){
                  dumparch = KL_ARCH_S390X;
            } else if(!(strcmp(arch_string, ARCH_STRING_X86_64))){
                  dumparch = KL_ARCH_X86_64;
            } else if(!(strcmp(arch_string, ARCH_STRING_IA64_SN2))){
                  dumparch = KL_ARCH_IA64_SN2;
            } else {
                  fprintf(stderr, "Dump arch \"%s\" not supported "
                        "by this version of lcrash.\n", arch_string);
                  return(1);
            }

            /* now check to make sure that this lcrash can handle the dump 
             * architecture specified.
             */
            switch(dumparch){
#ifdef DUMP_ARCH_ALPHA
                  case KL_ARCH_ALPHA:
                        break;
#endif
#ifdef DUMP_ARCH_ARM
                  case KL_ARCH_ARM:
                        break;
#endif
#ifdef DUMP_ARCH_I386
                  case KL_ARCH_I386:
                        break;
#endif
#ifdef DUMP_ARCH_IA64
                  case KL_ARCH_IA64:
                  case KL_ARCH_IA64_SN2:
                        break;
#endif
#ifdef DUMP_ARCH_PPC64
                  case KL_ARCH_PPC64:
                        break;
#endif
#ifdef DUMP_ARCH_S390
                  case KL_ARCH_S390:
                        break;
#endif
#ifdef DUMP_ARCH_S390X
                  case KL_ARCH_S390X:
                        break;
#endif
#ifdef DUMP_ARCH_X86_64
                  case KL_ARCH_X86_64:
                        break;
#endif
                  default:
                        if(arch_string[0]){
                              fprintf(stderr, "\tDump arch \"%s\" "
                                    "not supported by this "
                                    "lcrash.\n", 
                                    arch_string);
                        } else {
                              fprintf(stderr, "Unknown dump "
                                    "architecture.\n");
                        }
                        return(1);
            }
      }

      /* retrieve dump
       */
      if (lc_flags & LC_SAVE_FLG) {
            exit(kl_dump_retrieve(dump, dumpdir,
                              lc_flags & LC_PROGRESS_FLG,
                              lcrash_debug));
      }
      
      /* erase dump 
       */
      if (lc_flags & LC_ERASE_FLG) {
            exit(kl_dump_erase(dump));
      }

      /* some initial output if not in report mode
       */
      if (lc_flags ^ LC_REPORT_FLG) {
            fprintf(ofp,
                  "      map = %s\n"
                  "     dump = %s\n", map, dump);
            if (namelist[0]) {
                  fprintf(ofp, "kerntypes = %s\n\n", namelist);
            } else {
                  fprintf(ofp, "\n");
            }
            fprintf(ofp, "\nPlease wait...\n");
            fflush(ofp);
      }

      /* Set up signal handler. In normal mode, we trap all signals and then
       * continue execution. If the lcrash_debug flag is set, we'll dump core
       * for fatal signals (SEGV, BUSERR, etc.).
       */
      if (lcrash_debug) {
            kl_sig_setup(KL_SIGFLG_CORE|KL_SIGFLG_LNGJMP);
      } else {
            kl_sig_setup(KL_SIGFLG_LNGJMP);
      }

      /* liballoc initialization and register common lcrash commands
       */
      init_liballoc(0, 0, 0);

      /* init libklib
       */
      kl_init_klib(map, dump, namelist, dumparch, kl_flags);
      if (KL_ERROR) {
            if (lc_flags ^ LC_REPORT_FLG) {
                   fprintf(KL_ERRORFP, "\n");
            }
            if ((KLEC_CLASS(KL_ERROR) == KLEC_CLASS(KLE_KLIB)) &&
                (KLEC_ECODE(KL_ERROR) == KLEC_ECODE(KLE_OPEN_ERROR))) {
                if (KLEC_TYPE(KL_ERROR) == KLEC_TYPE(KLE_DUMP)) {
                  fprintf(KL_ERRORFP, "%s: ", dump);
                } else if ((KLEC_CLASS(KL_ERROR) == KLEC_CLASS(KLE_MEM)) &&
                         (KLEC_ECODE(KL_ERROR) == KLEC_ECODE(KLE_BAD_MAP_FILE)) &&
                         (KLEC_TYPE(KL_ERROR) == KLEC_TYPE(KLE_MAP_FILE))) {
                  fprintf(KL_ERRORFP, "%s: ", map);
                }
            }
            kl_print_error();
            exit(1);
      }

      /* If arch was specified on the command line, check to see
       * if there is a mismatch with what is in dump header.
       */
      if (dumparch) {
            if (dumparch != KL_ARCH) {
                  fprintf(KL_ERRORFP, "\nNotice: Dump arch mispatch!\n");
                  fprintf(KL_ERRORFP, "        0x%x specified on "
                        "command line, 0x%x in dump header\n", 
                        dumparch, KL_ARCH);
            }
      } else {
            dumparch = KL_ARCH;
      }

      /* print some information about the dump
       */
      kl_print_dumpinfo(0);
      if (!STP) {
            exit(1);
      }

      /* register common lcrash commands
       */
      register_cmds(cmdset);

      /* init dump arch dependent lcrash stuff (e.g. register dump-arch
       * dependent lcrash commands)
       */
      dumparch_init(dumparch, ofp);

      /* run the report here
       */
      if (lc_flags & LC_REPORT_FLG) {
            exit(do_report(0, ofp));
      }

      /* check if we are connected to a terminal
       */
      if ((isatty(1) == 0) || (isatty(0) == 0)) {
            ql_have_terminal = 0;
      }
      else {
            ql_have_terminal = 1;
            /* initialise the rl functions */
            if(!rl_init(">> ", 0, 0)) {
                  exit(1);
            }
            /* register sub-commands-line completion fuction */
            rl_register_complete_func(complete_cmds);
      }
      
      /* fire up sial interpreter and load macros
       */
      init_sial(M_val, I_val);

      /* go into lcrash command line mode and execute commands
       */
      process_cmds();

      /* clean up and exit
      */
      free_temp_blocks();
      exit(0);
}


Generated by  Doxygen 1.6.0   Back to index