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

kl_util.c

/*
 * $Id: kl_util.c,v 1.1 2004/12/21 23:26:20 tjm Exp $
 *
 * This file is part of libklib.
 * A library which provides access to Linux system kernel dumps.
 *
 * Created by Silicon Graphics, Inc.
 * Contributions by IBM, NEC, and others
 *
 * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved.
 * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
 * Copyright 2000 Junichi Nomura, NEC Solutions <j-nomura@ce.jp.nec.com>
 *
 * This code is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version. See the file COPYING for more
 * information.
 */

#include <klib.h>

/* stuff needed for save_dump functionality */
#define BUFSZ 128
char tmp[BUFSZ];

#if DUMP_DEBUG
void dump_bp(void) {    /* Debug Breakpoint */
}
#endif

extern int __kl_dump_retrieve(char *dumpdev, char *dumpdir,
                        int progress, int local_debug);
extern int __kl_dump_erase(char *dumpdev);

/*
 * kl_dump_retrieve() -- Retrieve and save a crash dump from <dumpdev>
 *                       into <dumpdir>.  Returns 0 on success, 1 on
 *                       failure.  The return codes are this way to handle
 *                       exit codes in 'lcrash'.
 */
int
kl_dump_retrieve(char *dumpdev, char *dumpdir, int progress, int local_debug)
{
      int ret;

      ret = __kl_dump_retrieve(dumpdev, dumpdir, progress, local_debug);
      
      switch (ret) {
            case 0:     /* failed to retrieve dump */

                  /* fundamental error.  return 1 */
                  return (1);

            case 1:     /* Truncated Dump Retrieved */
            case 2:     /* Complete  Dump Retrieved */
            default:
                  /* erase the dump and fall through */
                  if (local_debug == 0) {
                        (void)__kl_dump_erase(dumpdev);
                  }
                  /* return success */
                  return (0);
      }

      /* default */
      return (1);
}

int
kl_dump_erase(char *dumpdev)
{
      int ret;

      /* since this is destructive, ask before proceeding */
      printf("Do you really want to erase the dump on %s? (yes/no): ", dumpdev);
      fgets(tmp, BUFSZ, stdin);
      if (tmp[strlen(tmp) - 1] == '\n')   /* remove newline left by fgets */
            tmp[strlen(tmp) - 1] = '\0';
      
      if (strlen(tmp) == 0 || strstr("yes", tmp) == 0) {
            printf("Dump not erased\n");
            return (1);
      }
      
      /* erase the dump and fall through */
      ret = __kl_dump_erase(dumpdev);
      
      /* return success */
      if (ret)
            printf("Dump erased\n");
      else
            printf("Failed to erase dump\n");
      return (0);
}

/*
 * Definitions for the do_math() routine.
 */
#define M_ADD      '+'
#define M_SUBTRACT '-'
#define M_MULTIPLY '*'
#define M_DIVIDE   '/'

/*
 * do_math() -- Calculate some math values based on a string argument
 *              passed into the function.  For example, if you use:
 *
 *              0xffffc000*2+6/5-3*19-8
 *
 *              And you will get the value 0xffff7fc0 back.  I could
 *              probably optimize this a bit more, but right now, it
 *              works, which is good enough for me.
 */
static uint64_t
do_math(char *str)
{
      int i = 0;
      char *buf, *loc;
      uint64_t value1, value2;
      syment_t *sp;

      buf = (char *)kl_alloc_block((strlen(str) + 1), K_TEMP);
      sprintf(buf, "%s", str);
      for (i = strlen(str); i >= 0; i--) {
            if ((str[i] == M_ADD) || (str[i] == M_SUBTRACT)) {
                  buf[i] = '\0';
                  value1 = do_math(buf);
                  value2 = do_math(&str[i+1]);
                  kl_free_block((void *)buf);
                  if (str[i] == M_SUBTRACT) {
                        return value1 - value2;
                  } else {
                        return value1 + value2;
                  }
            }
      }

      for (i = strlen(str); i >= 0; i--) {
            if ((str[i] == M_MULTIPLY) || (str[i] == M_DIVIDE)) {
                  buf[i] = '\0';
                  value1 = do_math(buf);
                  value2 = do_math(&str[i+1]);
                  kl_free_block((void *)buf);
                  if (str[i] == M_MULTIPLY) {
                        return (value1 * value2);
                  } else {
                        if (value2 == 0) {
                              /* handle divide by zero */
                              /* XXX -- set proper error code */
                              klib_error = 1;
                              return (0);
                        } else {
                              return (value1 / value2);
                        }
                  }
            }
      }

      /*
       * Otherwise, just process the value, and return it.
       */
      sp = kl_lkup_symname(buf);
      if (KL_ERROR) {
            KL_ERROR = 0;
            value2 = (uint64_t)strtoul(buf, &loc, 10);
            if (((!value2) && (buf[0] != '0')) || (*loc) ||
                  (!strncmp(buf, "0x", 2)) || (!strncmp(buf, "0X", 2))) {
                  value1 = GET_HEX_VALUE(buf);
            } else {
                  value1 = GET_DEC_VALUE(buf);
            }
      } else {
            value1 = (kaddr_t)sp->s_addr;
      }
      kl_free_block((void *)buf);
      return (value1);
}

/*
 * kl_get_value() -- Translate numeric input strings
 *
 *   A generic routine for translating an input string (param) in a
 *   number of dfferent ways. If the input string is an equation
 *   (contains the characters '+', '-', '/', and '*'), then perform
 *   the math evaluation and return one of the following modes (if
 *   mode is passed):
 *
 *   0 -- if the resulting value is <= elements, if elements (number
 *        of elements in a table) is passed.
 *
 *   1 -- if the first character in param is a pound sign ('#').
 *
 *   3 -- the numeric result of an equation.
 *
 *   If the input string is NOT an equation, mode (if passed) will be
 *   set in one of the following ways (depending on the contents of
 *   param and elements).
 *
 *   o When the first character of param is a pound sign ('#'), mode
 *     is set equal to one and the trailing numeric value (assumed to
 *     be decimal) is returned.
 *
 *   o When the first two characters in param are "0x" or "0X," or
 *     when when param contains one of the characers "abcdef," or when
 *     the length of the input value is eight characters. mode is set
 *     equal to two and the numeric value contained in param is
 *     translated as hexadecimal and returned.
 *
 *   o The value contained in param is translated as decimal and mode
 *     is set equal to zero. The resulting value is then tested to see
 *     if it exceeds elements (if passed). If it does, then value is
 *     translated as hexadecimal and mode is set equal to two.
 *
 *   Note that mode is only set when a pointer is passed in the mode
 *   paramater. Also note that when elements is set equal to zero, any 
 *   non-hex (as determined above) value not starting with a pound sign 
 *   will be translated as hexadecimal (mode will be set equal to two) --
 *   IF the length of the string of characters is less than 16 (kaddr_t).
 *
 */
int
kl_get_value(char *param, int *mode, int elements, uint64_t *value)
{
      char *loc;
      uint64_t v;

      kl_reset_error();

      /* Check to see if we are going to need to do any math
       */
      if (strpbrk(param, "+-/*")) {
            if (!strncmp(param, "#", 1)) {
                  v = do_math(&param[1]);
                  if (mode) {
                        *mode = 1;
                  }
            } else {
                  v = do_math(param);
                  if (mode) {
                        if (elements && (*value <= elements)) {
                              *mode = 0;
                        } else {
                              *mode = 3;
                        }
                  }
            }
      } else {
            if (!strncmp(param, "#", 1)) {
                  if (!strncmp(param, "0x", 2) 
                              || !strncmp(param, "0X", 2) 
                              || strpbrk(param, "abcdef")) {
                        v = kl_strtoull(&param[1], &loc, 16);
                  } else {
                        v = kl_strtoull(&param[1], &loc, 10);
                  }
                  if (loc) {
                        KL_ERROR = KLE_INVALID_VALUE;
                        return (1);
                  }
                  if (mode) {
                        *mode = 1;
                  }
            } else if (!strncmp(param, "0x", 2) || !strncmp(param, "0X", 2) 
                              || strpbrk(param, "abcdef")) {
                  v = kl_strtoull(param, &loc, 16);
                  if (loc) {
                        KL_ERROR = KLE_INVALID_VALUE;
                        return (1);
                  }
                  if (mode) {
                        *mode = 2; /* HEX VALUE */
                  }
            } else if (elements || (strlen(param) < 16) || 
                        (strlen(param) > 16)) {
                  v = kl_strtoull(param, &loc, 10);
                  if (loc) {
                        KL_ERROR = KLE_INVALID_VALUE;
                        return (1);
                  }
                  if (elements && (v >= elements)) {
                        v = GET_HEX_VALUE(param);
                        if (mode) {
                              *mode = 2; /* HEX VALUE */
                        }
                  } else if (mode) {
                        *mode = 0;
                  }
            } else {
                  v = kl_strtoull(param, &loc, 16);
                  if (loc) {
                        KL_ERROR = KLE_INVALID_VALUE;
                        return (1);
                  }
                  if (mode) {
                        *mode = 2; /* ASSUME HEX VALUE */
                  }
            }
      }
      *value = v;
      return (0);
}

/* 
 * kl_get_struct()
 */
int
kl_get_struct(kaddr_t addr, int size, void *ptr, char *name)
{
      GET_BLOCK(addr, size, ptr);
      return(KL_ERROR);
}

/*
 * kl_get_structure()
 *
 * return structure name from address vaddr of dump
 * function allocates memory if *ptr==NULL,
 * this memory has to be released in the calling function using kl_free_block()
 */

int
kl_get_structure(kaddr_t vaddr, char * name, size_t * size, void **ptr)
{
      int  free_ptr = 0;

      if(!*size){
            *size = kl_struct_len(name);
            if(KL_ERROR){
                  return(1);
            }
      }

      if(*ptr == NULL){
            *ptr = kl_alloc_block(*size, K_TEMP);
            if (KL_ERROR) {
                  return(1);
            }
            free_ptr = 1;
      }

      if(vaddr){
            GET_BLOCK(vaddr, *size, *ptr);
            if (KL_ERROR) {
                  if(free_ptr){
                        kl_free_block(*ptr);
                        *ptr = NULL;
                  }
                  return(1);
            }
      } else {
            return(1);
      }

      return(0);
}

/*
 * linux_release()
 */
int
kl_linux_release(void)
{
      int i;
      char *b, *n, revstr[KL_UTS_LEN];
      int revision = 0;
      char *c;
      void *utsname;
      syment_t *sp;

      if (!(sp = kl_lkup_symname("system_utsname"))) {
            return(0);
      }
      utsname = kl_alloc_block(NEW_UTSNAME_SZ, K_TEMP);
      if (KL_ERROR) {
            return(0);
      }
      GET_BLOCK(sp->s_addr, NEW_UTSNAME_SZ, utsname);
      if (KL_ERROR) {
            kl_free_block(utsname);
            return(0);
      }

      /* Try to make sure that we have a valid utsname struct.
       * If we don't, most likely we are using the wrong map
       * file. To continue doesn't make sense).
       */
      c = K_PTR(utsname, "new_utsname", "release");
      if (!(*c)) {
            KL_ERROR = KLE_BAD_MAP_FILE;
            kl_free_block(utsname);
            return(0);
      }
      for (i = 0; i < KL_UTS_LEN; i++) {
            if (!c[i]) {
                  break;
            }
      }
      if (i == KL_UTS_LEN) {
            KL_ERROR = KLE_BAD_MAP_FILE;
            kl_free_block(utsname);
            return(0);
      }
      strncpy(revstr, K_PTR(utsname, "new_utsname", "release"), 
            KL_UTS_LEN);
      b = revstr;
      if (!(n = strchr(revstr, '.'))) {
            KL_ERROR = KLE_BAD_MAP_FILE;
            kl_free_block(utsname);
            return(0);
      }
      *n = 0;
      revision |= (atoi(b) << 16);
      b = n + 1;
      if (!(n = strchr(b, '.'))) {
            KL_ERROR = KLE_BAD_MAP_FILE;
            kl_free_block(utsname);
            return(0);
      }
      revision |= (atoi(b) << 8);
      b = n + 1;
      revision |= atoi(b);
      kl_free_block(utsname);
      return(revision);
}

/*
 * print information about dump -- what indicates what to print
 */
int
kl_print_dumpinfo(int what)
{
      char *archstr, endianstr[10]="unknown";
      unsigned char r[3];
      short msize[4];
      kaddr_t memsize;
      
      switch(KL_ARCH){
      case KL_ARCH_ALPHA:
            archstr = KL_ARCH_STR_ALPHA;
            break;
      case KL_ARCH_I386:
            archstr = KL_ARCH_STR_I386;
            break;
      case KL_ARCH_IA64:
            archstr = KL_ARCH_STR_IA64;
            break;
      case KL_ARCH_S390:
            archstr = KL_ARCH_STR_S390;
            break;
      case KL_ARCH_S390X:
            archstr = KL_ARCH_STR_S390X;
            break;
      case KL_ARCH_PPC64:
            archstr = KL_ARCH_STR_PPC64;
            break;
      case KL_ARCH_X86_64:
            archstr = KL_ARCH_STR_X86_64;
            break;
      default:
            archstr = KL_ARCH_STR_UNKNOWN;
      }

      switch(KL_BYTE_ORDER){
      case KL_BIG_ENDIAN:
            strcpy(endianstr,"big");
            break;
      case KL_LITTLE_ENDIAN:
            strcpy(endianstr,"little");
            break;
      }

      r[0] = (KL_LINUX_RELEASE >> 16) & 0xff;
      r[1] = (KL_LINUX_RELEASE >> 8) & 0xff;
      r[2] = KL_LINUX_RELEASE & 0xff;
      
      memsize = KL_HIGH_MEMORY - KL_PAGE_OFFSET;
      msize[0] = memsize >> 30 & 0x03ff; /* GByte */
      msize[1] = memsize >> 20 & 0x03ff; /* MByte */
      msize[2] = memsize >> 10 & 0x03ff; /* KByte */
      msize[3] = memsize & 0x03ff;       /*  Byte */
      

      switch(what){
      case KL_INFO_ENDIAN:
            fprintf(kl_stdout, "%s\n", endianstr);
            break;
      case KL_INFO_ARCH:
            fprintf(kl_stdout, "%s\n", archstr);
            break;
      case KL_INFO_PTRSZ:
            fprintf(kl_stdout, "%u\n", KL_PTRSZ);
            break;
      case KL_INFO_KRELEASE:
            fprintf(kl_stdout, "%hhu.%hhu.%hhu\n", r[0], r[1], r[2]);
            break;
      case KL_INFO_MEMSIZE:
            fprintf(kl_stdout, "%"FMT64"u\n", memsize);
            break;
      case KL_INFO_NUMCPUS:
            fprintf(kl_stdout, "%u\n", KL_NUM_CPUS);
            break;
      case KL_INFO_ALL:
            fprintf(kl_stdout, "\nDUMP INFORMATION:\n\n"
                  "     architecture: %s\n"
                  "       byte order: %s\n"
                  "     pointer size: %u\n"
                  "   bytes per word: %u\n\n"
                  "   kernel release: %hhu.%hhu.%hhu\n"
                  "      memory size: %"FMT64"u"
                  " (%huG %huM %huK %huByte)\n"
                  "   num phys pages: %"FMT64"u\n"
                  "   number of cpus: %u\n\n",
                  archstr, endianstr, KL_PTRSZ, KL_NBPW, r[0], r[1], r[2],
                  memsize, msize[0], msize[1], msize[2], msize[3],
                  NUM_PHYSPAGES, KL_NUM_CPUS);
            break;
      default:
            /* nothing to print, XXX set error code? */
            return(1);
      }

      return(0);
}

/*
 * kl_get_bit_value()
 *
 * x = byte_size, y = bit_size, z = bit_offset
 */
uint64_t
kl_get_bit_value(void *ptr, unsigned int x, unsigned int y, unsigned int z)
{
      uint64_t value=0, mask;

      assert((x<=8) && ((8*x) >= (z + y)));

      /* handle x bytes of buffer -- doing just memcpy won't work
       * on big endian architectures
       */
        switch (x) {
      case 5:
      case 6:
      case 7:
      case 8:
            x = 8;
            value = KL_GET_UINT64(ptr);
            break;
      case 3:
      case 4:
            x = 4;
            value = KL_GET_UINT32(ptr);
            break;
      case 2:
            value = KL_GET_UINT16(ptr);
            break;
      case 1:
            value = KL_GET_UINT8(ptr);
            break;
      default:
            /* FIXME: set KL_ERROR */
            return(0);
        }

      /* XXX -- assumption: bit fields are allocated
          o from left to right on big endian machines
          (most to least significant), (true for s390/s390x/ppc)
          o from right to left on little endian machines
          (least to most significant), (true for i386/ia64/ppc)
          o FIXME: correct handling of overlapping fields
      */
      if(IS_BIG_ENDIAN()){
            z = (8*x) - (z+y);
      }

      /* goto bit offset */
      value = value >> z;

      /* mask bit size bits */
      mask = (((uint64_t)1 << y) - 1);
      return (value & mask);
}

/*
 *  convert tod (s390 Time of day) to timeval
 */
void kl_s390tod_to_timeval(uint64_t todval, struct timeval *xtime)
{
    /* adjust todclock to 1970 */
    todval -= 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096);

    todval >>= 12;
    xtime->tv_sec  = todval / 1000000;
    xtime->tv_usec = todval % 1000000;
}

Generated by  Doxygen 1.6.0   Back to index