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

kl_cmp.c

/*
 * $Id: kl_cmp.c,v 1.3 2005/02/25 22:04:04 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 - 2005 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.
 */

/*
 * This file handles compression aspects of crash dump files
 * for ia64 based systems.  Most of this is taken from the
 * IRIX compression code, with exceptions to how the index
 * is created, because the file format is different with Linux.
 */

#include <klib.h>
#include <zlib.h>

extern off_t dump_header_offset;

/* XXX change format string when printing in_addr */

int cmp_debug = 0;
static int cache_count = 0;
static int cache_water_mark = CMP_HIGH_WATER_MARK, cache_flags = CMP_VM_CACHED;
static ptableentry *cache_head, *cache_tail, **page_table;
static ptableindex **page_index;  /* global page index */
static kl_dump_header_t *dh; /* dump header for compression library */

static void __cmppinsert(kaddr_t in_addr, char *buffer, int nbytes, int flags);
static int __cmppget(int fd, kaddr_t in_addr, char *buffer,
      unsigned int nbytes, unsigned int flags);
static ptableindex *__cmppindex(kaddr_t in_addr);
static int __cmppread(int fd, kaddr_t in_addr, char *buffer,
      unsigned int nbytes, unsigned int flags);
static void __cmpsaveindex(char *filename, int flags);
static int __cmpuncompress_page_rle(unsigned char *cbuf, unsigned char *ucbuf,
      int flags, kaddr_t blk_size, int *new_size);
static int __cmpuncompress_page_gzip(unsigned char *cbuf, unsigned char *ucbuf,
      int flags, kaddr_t blk_size, int *new_size);
static void __cmpcleanindex(void);
static int __cmploadindex(char *indexname);
static kaddr_t __cmpconvertaddr(kl_dump_page_t *dp);
static kaddr_t __cmpphashbase(kaddr_t in_addr, int hash);
static int __cmpphash(kaddr_t addr);
static int __cmpcheckpageheader(int fd, kl_dump_page_t *dp);
static int __cmppindexcreate(int fd, char *indexname, int flags);
static int __cmpcheckheader(int fd);


/*
 * __cmppinsert()
 *
 * Insert a directory entry / buffer into a table
 *
 * This is so it can be hashed on later. Note that this will also
 * contain caching flags in the future.
 */
static void
__cmppinsert(kaddr_t in_addr, char *buffer, int nbytes, int flags)
{
      int hash, thash;
      ptableentry *tmpptr, *tmpptr2;

      tmpptr = (ptableentry *)NULL;

      /* Since we know where the page is, and it is inserted, let's
       * go ahead and insert it into the cache (if we should...)
       */
      if (cache_flags == CMP_VM_CACHED) {
            if (cache_count > cache_water_mark) {

                  if (cmp_debug) {
                        fprintf(KL_ERRORFP,
                              "__cmppinsert(): Cleaning excess "
                              "page out of cache! (0x%"FMTPTR"x) "
                              "[%d]...\n", in_addr, cache_count);
                  }

                  tmpptr = cache_head;
                  if (tmpptr) {

                        /* First, take the first block off the list.
                         * It's easier to take off the head (and a
                         * tad bit faster...)  Keep a copy of it,
                         * because we have to remove it from the
                         * regular set of blocks.  We also want to
                         * use its data space for later (why malloc()
                         * if we don't need to?)
                         */
                        cache_head = cache_head->nextcache;
                        if (cache_head) {
                              cache_head->prevcache = (ptableentry *)NULL;
                        }
                        cache_count--;

                        tmpptr->nextcache = (ptableentry *)NULL;
                        tmpptr->prevcache = (ptableentry *)NULL;

                        /* Next, take the block out of the pointer
                         * list, because it's not needed.
                         */
                        if ((tmpptr->next == (ptableentry *)NULL) &&
                              (tmpptr->prev != (ptableentry *)NULL)) {

                              /* We are removing the last block on a list.
                               * Assuming it isn't the only block, just back up one.
                               */ 
                              tmpptr->prev->next = (ptableentry *)NULL;
                        } else if (tmpptr->prev == (ptableentry *)NULL) {

                              /* We are at the head of a list.  In this case,
                               * we have to find the page_table hash entry,
                               * move it forward, reset pointers, etc.
                               */
                              thash = __cmpphash(tmpptr->addr);
                              if (page_table[thash] == tmpptr) {
                                    page_table[thash] = page_table[thash]->next;
                                    if (page_table[thash] != (ptableentry *)NULL) {
                                          page_table[thash]->prev = (ptableentry *)NULL;
                                    }
                              } else {
                                    if (cmp_debug) {
                                          fprintf(KL_ERRORFP,
                                                "__cmppinsert(): hash table at head!\n");
                                    }
                              }
                        } else if ((tmpptr->next != (ptableentry *)NULL) &&
                              (tmpptr->prev != (ptableentry *)NULL)) {

                                    /* We're in the middle of a list.  Just set our
                                     * neighbor's pointers around us.
                                     */
                                    tmpptr2 = tmpptr->next;
                                    tmpptr->prev->next = tmpptr->next;
                                    tmpptr2->prev = tmpptr->prev;
                        }
                        tmpptr->cached = 0;
                  }
            }
      }

      /* At this point, we *might* have a valid buffer.  If we
       * do, use it!  Then, stick it back into the cache and the
       * hash table.
       */
      if (tmpptr == (ptableentry *)NULL) {
            if (cmp_debug) {
                  fprintf(KL_ERRORFP, 
                        "__cmppinsert(): Malloc occurred! [%d]\n",
                        cache_count);
            }
            tmpptr = (ptableentry *)malloc(sizeof(ptableentry));
            tmpptr->data = (char *)malloc(nbytes);
      }
      memcpy((void *)tmpptr->data, (const void *)buffer, nbytes);

      tmpptr->addr = __cmpphashbase(in_addr, 1);
      tmpptr->flags = flags;
      tmpptr->length = nbytes;
      tmpptr->next = (ptableentry *)NULL;
      tmpptr->prev = (ptableentry *)NULL;
      tmpptr->nextcache = (ptableentry *)NULL;
      tmpptr->prevcache = (ptableentry *)NULL;
      tmpptr->cached = 0;

      hash = __cmpphash(in_addr);

      /* Insert the page into the page table, as normal.
       * We do this whether we are caching or not.
       */
      if (page_table[hash] != (ptableentry *)NULL) {
            tmpptr->next = page_table[hash];
            page_table[hash]->prev = tmpptr;
            page_table[hash] = tmpptr;
      } else {
            page_table[hash] = tmpptr;
      }

      /* Now, if we are using a caching scheme, store the page
       * at the end of the chain.
       */
      if (cache_flags == CMP_VM_CACHED) {
            page_table[hash]->cached = 1;
            if (cmp_debug) {
                  fprintf(KL_ERRORFP, "__cmppinsert(): Inserting "
                        "page into cache! (0x%"FMTPTR"x) "
                        "[%d]...\n", in_addr, cache_count);
            }
            if (cache_tail != (ptableentry *)NULL) {
                  cache_tail->nextcache = tmpptr;
                  tmpptr->prevcache = cache_tail;
                  cache_tail = cache_tail->nextcache;
            } else {
                  cache_head = tmpptr;
                  cache_tail = tmpptr;
                  cache_head->nextcache = (ptableentry *)NULL;
                  cache_head->prevcache = (ptableentry *)NULL;
            }
            cache_count++;
      }
}

/*
 * __cmppget()
 *
 * Try to get the page of data hashed out.  
 * 
 * This will search through the hash table looking for our page. If
 * we find it, and it is all we need, copy it into the buffer and
 * return. Otherwise, read the rest from __cmppread() again and return.
 */
static int
__cmppget(int fd, kaddr_t in_addr, char *buffer,
      unsigned int nbytes, unsigned int flags)
{
      ptableentry *tmpptr;
      int hash;
      unsigned int offset = 0;
      unsigned int bytes_left = 0;

      /* First things first, snag the hash index for this item.
       */
      hash = __cmpphash(in_addr);

      /* Now, see if the item exists in the list.  If the first
       * pointer doesn't even exist, just return.
       */
      if ((hash < 0) || (hash >= NUM_BUCKETS) || (!page_table[hash])) {
            return -1;
      }

      /* Otherwise, we've got a valid pointer.  Look for the hashed
       * page in the table.
       */
      tmpptr = page_table[hash];
      while (tmpptr) {
            if ((in_addr >= tmpptr->addr) &&
                  (in_addr <= tmpptr->addr + KL_DUMP_PAGE_SIZE)) {

                        /* Check out if this page is on the cache list
                         * or not.  If it is, then go ahead and move it
                         * to the end of the list.  We only need to play
                         * with the cache list, we don't have to touch
                         * its position in the hash table.
                         */
                        if ((cache_flags == CMP_VM_CACHED) && (tmpptr->cached)) {

                              /* Move the page in the cache.  First, take it
                               * from where it is currently located.  Note that
                               * there is no reason to re-insert if we are
                               * already at the end of the list!
                               */
                              if (tmpptr->nextcache != (ptableentry *)NULL) {

                                    /* See if we are at the head of the list.
                                     */
                                    if (tmpptr->prevcache == (ptableentry *)NULL) {
                                          if (cache_head) {
                                                cache_head = cache_head->nextcache;
                                                if (cache_head) {
                                                      cache_head->prevcache =
                                                            (ptableentry *)NULL;
                                                }
                                          }
                                    } else {
                                          tmpptr->prevcache->nextcache = tmpptr->nextcache;
                                          tmpptr->nextcache->prevcache = tmpptr->prevcache;
                                    }
                                    tmpptr->nextcache = (ptableentry *)NULL;
                                    tmpptr->prevcache = (ptableentry *)NULL;

                                    /* Now, re-insert it at the end of the list.
                                     */
                                    if (cache_tail != (ptableentry *)NULL) {
                                          cache_tail->nextcache = tmpptr;
                                          tmpptr->prevcache = cache_tail;
                                          cache_tail = cache_tail->nextcache;
                                    } else {
                                          cache_head = tmpptr;
                                          cache_tail = tmpptr;
                                          cache_head->nextcache = (ptableentry *)NULL;
                                          cache_head->prevcache = (ptableentry *)NULL;
                                    }
                              }
                        }

                        /* If we have the buffer where the address is
                         * located, determine how much of it needs to
                         *  be copied, and from which offset.
                         */
                        offset = (int)(in_addr - tmpptr->addr);
                        bytes_left = (int)((tmpptr->addr + 
                                        KL_DUMP_PAGE_SIZE)-in_addr);
                        if ((in_addr + nbytes) >
                            (tmpptr->addr + KL_DUMP_PAGE_SIZE)) {
                              if (cmp_debug) {
                                    fprintf(KL_ERRORFP,
                                          "__cmppget(): reading"
                                          " more data "
                                          "(bytes_left = %d,"
                                          " nbytes = %d)\n",
                                          bytes_left, nbytes);
                              }
                              memcpy((void *)buffer, (const void *)
                                     (tmpptr->data + offset),
                                    bytes_left);
                              return(__cmppread(fd, tmpptr->addr +
                                            KL_DUMP_PAGE_SIZE,
                                            buffer + bytes_left,
                                            nbytes - bytes_left,
                                    flags));
                        }
                        if (cmp_debug) {
                              fprintf(KL_ERRORFP, "__cmppget():"
                                    " copying page of data "
                                    "(nbytes = %d, offset = %d,"
                                    " in_addr = 0x%"FMTPTR"x)\n",
                                    nbytes, offset, in_addr);
                        }
                        memcpy((void *)buffer,
                              (const void *)(tmpptr->data + offset),
                              nbytes);
                        return 1;
            }
            tmpptr = tmpptr->next;
      }
      return -1;
}

/*
 * __cmppindex()
 * 
 * This function is responsible for grabbing a page out of our index
 * list, and returning the directory entry header at that location.
 */
static ptableindex *
__cmppindex(kaddr_t in_addr)
{
      ptableindex *tmpptr;
      int hash;

      /* Grab the hash value based on the address passed in,
       * after the address is processed based on a 0x1000 page.
       */
      hash = __cmpphash(in_addr);

      /* Now, see if the page exists in the page table index.
       * We already know it isn't in the page table itself,
       * but if the page doesn't exist in the core dump, we
       * want to return -1.
       */
      if (cmp_debug) {
            fprintf(KL_ERRORFP, "__cmppindex(): hash = %6d, "
                  "addr = 0x%"FMTPTR"x\n", hash, in_addr);
      }
      tmpptr = page_index[hash];
      while (tmpptr != (ptableindex *)NULL) {
            if (cmp_debug) {
                  fprintf(KL_ERRORFP, "__cmppindex(): addr = "
                        "0x%"FMTPTR"x, "
                        "tmpptr->addr = 0x%"FMTPTR"x\n", 
                        in_addr, tmpptr->addr);
            }
            if ((in_addr >= tmpptr->addr) &&
                  (in_addr <= tmpptr->addr + KL_DUMP_PAGE_SIZE)) {

                  /* We found the address we want.  Return the
                   * directory entry pointer.
                   */
                  return (tmpptr);
            }
            tmpptr = tmpptr->next;
      }
      return ((ptableindex *)NULL);
}

/*
 * __cmppread()
 *
 * Read a page of a compressed core dump.
 */
static int
__cmppread(int fd, kaddr_t in_addr, char *buffer,
      unsigned int nbytes, unsigned int flags)
{
      char *ptr;
      static char *compr_page, *uncompr_page;
      int tmpflags, new_size = 0, len;
      static int first_time = 0;
      ptableindex *pindexitem;

      /* Print out a message that we are searching.
       */
      if (cmp_debug) {
            fprintf(KL_ERRORFP, 
                  "__cmppread(): initiating search for 0x%"FMTPTR"x\n", 
                        in_addr);
      }

      /* If we haven't created these pages yet, do so now.  This
       * changes because of KL_DUMP_PAGE_SIZE.
       */
      if (first_time == 0) {
            if ((compr_page = (char *)malloc(KL_DUMP_PAGE_SIZE)) ==
                (char *)NULL) {
                  fprintf(KL_ERRORFP, "__cmpuncompress_page: out of"
                        " memory!\n");
                  return (-1);
            }

            if ((uncompr_page = (char *)malloc(KL_DUMP_PAGE_SIZE)) ==
                (char *)NULL) {
                  fprintf(KL_ERRORFP, "__cmpuncompress_page: out of"
                        " memory!\n");
                  return (-1);
            }
            first_time = 1;
      }

      /* First things first: try to go through our dump page hash table
       * and get a dump page of data.  It might not contain the entire
       * set of data that we are looking for, but it will grab
       * any additional pages we might need.
       */
      if (__cmppget(fd, in_addr, buffer, nbytes, flags) >= 0) {
            if (cmp_debug) {
                  fprintf(KL_ERRORFP, 
                        "__cmppread(): found the item in the hash"
                        " table!\n");
            }
            return 1;
      }

      /* If we get to here, we didn't find the dump page in our hash table.
       * So, search through the compressed core dump index to see if
       * the address exists in the core dump's list.
       */
      if ((pindexitem = __cmppindex(in_addr)) == (ptableindex *)NULL) {
            if (cmp_debug) {
                  fprintf(KL_ERRORFP, 
                        "__cmppread(): dump page not found! "
                        "(0x%"FMTPTR"x)\n", in_addr);
            }
            KL_ERROR = KLE_PAGE_NOT_PRESENT;
            memset(buffer, 0, nbytes);
            return 0;
      } else {
            if (cmp_debug) {
                  fprintf(KL_ERRORFP, "__cmppread(): found the dump page"
                        " in the page index!\n");
            }
      }

      /* If we get to here, we have found the page we want.
       * So, let's seek to the location and read it out.
       */
      if (lseek(fd, pindexitem->coreaddr, SEEK_SET) < 0) {
            if (cmp_debug) {
                  fprintf(KL_ERRORFP, "__cmppread(): couldn't seek "
                        "to dump page 0x%"FMTPTR"x\n", in_addr);
            }
            return -1;
      }

      /* Now that we're here, let's grab the page.
       */
      tmpflags = KL_GET_UINT32(&pindexitem->dir.flags);
      len = read(fd, (void *)compr_page,
               KL_GET_UINT32(&pindexitem->dir.size));
      if (len != KL_GET_UINT32(&pindexitem->dir.size)) {
            if (cmp_debug) {
                  fprintf(KL_ERRORFP, "__cmppread(): warning: "
                        "uncompressed file may be incomplete.\n");
            }
            return 1;
      }

      /* If this is a raw page, go ahead and read in the whole thing.
       * Otherwise, uncompress it and read it in.
       */
      if (tmpflags & KL_DUMP_DH_RAW) {
            if (len != KL_DUMP_PAGE_SIZE) {
                  if (cmp_debug) {
                        fprintf(KL_ERRORFP, "__cmppread(): warning:"
                              " RAW page isn't "
                              "the right size (%d)!\n", len);
                  }
                  return -1;
            }

            /* Set the pointer to the appropriate char * data buffer, and
             * set the size appropriately.
             */
            ptr = compr_page;
            new_size = KL_DUMP_PAGE_SIZE;
            if (cmp_debug) {
                  fprintf(KL_ERRORFP, "0x%"FMTPTR"x: %d -> %d RAW, "
                        "writing %"FMT64"d bytes\n", pindexitem->addr, 
                        len, new_size, (kaddr_t) KL_DUMP_PAGE_SIZE);
            }
      } else {

            /* Uncompress the dump page.
             */
            switch (dh->dump_compress) {
                  case KL_DUMP_COMPRESS_RLE:
                        if (!__cmpuncompress_page_rle(
                              (unsigned char*)compr_page, 
                              (unsigned char*)uncompr_page,
                              tmpflags, len, &new_size)) {
                                    fprintf(KL_ERRORFP,
                                          "__cmppread(): "
                                          "invalid page "
                                          "decompression!\n");
                                    return (-1);
                        }
                        break;

                  case KL_DUMP_COMPRESS_GZIP:
                        if (!__cmpuncompress_page_gzip(
                              (unsigned char*)compr_page, 
                              (unsigned char*)uncompr_page,
                              tmpflags, len, &new_size)) {
                                    fprintf(KL_ERRORFP,
                                          "__cmppread(): "
                                          "invalid page "
                                          "decompression!\n");
                                    return (-1);
                        }
                        break;

                  default:
                        fprintf(KL_ERRORFP, "__cmppread(): "
                              "unknown compression type!\n");
                        return (-1);
            }

            /* Check to make sure that the length is right.  If the length
             * is not the same as KL_DUMP_PAGE_SIZE, something's wrong.
             */
            if (new_size != KL_DUMP_PAGE_SIZE) {
                  if (cmp_debug) {
                        fprintf(KL_ERRORFP, "__cmppread(): warning:"
                              " COMPRESSED page isn't the right"
                              " size (%d)!\n", new_size);
                  }
                  return -1;
            }

            /* Set the pointer to the appropriate char * data buffer.
             */
            ptr = uncompr_page;
            if (cmp_debug) {
                  fprintf(KL_ERRORFP, 
                        "0x%"FMTPTR"x: %d -> %d COMPRESSED, "
                        "writing %"FMT64"d bytes\n",
                        pindexitem->addr, len, new_size,
                        (kaddr_t)KL_DUMP_PAGE_SIZE);
            }
      }

      /* Now, insert the dump page into the hash table.
       */
      __cmppinsert(in_addr, ptr, new_size, tmpflags);

      /* Officially grab the page now that we have
       * placed it into the table.
       */
      if (__cmppget(fd, in_addr, buffer, nbytes, flags) >= 0) {
            if (cmp_debug) {
                  fprintf(KL_ERRORFP,
                        "__cmppread(): found the item in the hash "
                        "table second time!\n");
            }
            return 1;
      }

      /* Return that everything succeeded.
       */
      return 1;
}

/*
 * kl_paddr_in_dump()
 */
int
kl_paddr_in_dump(kaddr_t addr)
{
      ptableindex *idxp;

      idxp = __cmppindex(addr);
      if (idxp) {
            return(1);
      }
      return(0);
}

/*
 * kl_cmpreadmem() -- Read the compressed core dump 
 * 
 *   The core dump is read through to the size of the buffer. We will
 *   call __cmppread() as appropriate to get the page of data as we need
 *   it.
 */
int
kl_cmpreadmem(int fd, kaddr_t in_addr, char *buffer,
      unsigned int nbytes, unsigned int flags)
{
      int offset = 0;

      /* First, see if we want a size greater than KL_DUMP_PAGE_SIZE.
       */
      if (nbytes <= KL_DUMP_PAGE_SIZE) {
            if (cmp_debug) {
                  fprintf(KL_ERRORFP, "kl_cmpreadmem(): %d bytes, "
                        "0x%"FMTPTR"x (just a page)\n", 
                        nbytes, in_addr);
            }
            return (__cmppread(fd, in_addr, buffer, nbytes, flags)); 
      } else if (nbytes > KL_DUMP_PAGE_SIZE) {

            /* Otherwise, just read in the data KL_DUMP_PAGE_SIZE at a time.
             */
            while (offset < nbytes) {
                  if ((nbytes - offset) <= KL_DUMP_PAGE_SIZE) {

                        /* Read the leftovers.
                         */
                        if (cmp_debug) {
                              fprintf(KL_ERRORFP, "kl_cmpreadmem(): "
                                    "reading %d bytes, "
                                    "0x%"FMTPTR"x (leftovers)\n",
                                    nbytes - offset,
                                    in_addr + offset);
                        }
                        return (__cmppread(fd, in_addr + offset,
                                       buffer + offset,
                                       nbytes - offset, flags));
                  } else {

                        /* Read a page in at a time.
                         */
                        if (cmp_debug) {
                              fprintf(KL_ERRORFP, "kl_cmpreadmem(): "
                                    "reading %"FMT64"d bytes, "
                                    "0x%"FMTPTR"x (a new page)\n", 
                                    (kaddr_t)KL_DUMP_PAGE_SIZE,
                                    in_addr + offset);
                        }
                        if (__cmppread(fd, in_addr + offset, buffer
                                     + offset, KL_DUMP_PAGE_SIZE,
                                     flags)
                            < 0) {
                              return -1;
                        }
                  }

                  /* Increment the offset by KL_DUMP_PAGE_SIZE.
                   */
                  offset += KL_DUMP_PAGE_SIZE;
            }
            /* NOT REACHED */
            return 1;
      }
      /* NOT REACHED */
      return 1;
}

/*
 * __cmpsaveindex()
 *
 * Save the index for the compressed core dump.
 */
static void
__cmpsaveindex(char *filename, int flags)
{
      int i, ifd;
      ptableindex *tmpptr;
      dump_index_t dump_index;

      /* if flags are set here, we're running a report -- don't save */
      if (flags) {
            return;
      }

      if (cmp_debug) {
            fprintf(KL_ERRORFP, "\nAttempting to save index \"%s\" ... ",
                  filename);
      }

      if ((ifd = open(filename, O_RDWR|O_CREAT, 0644)) < 0) {
            fprintf(KL_ERRORFP,
                  "\n__cmpsaveindex(): open() of index \"%s\" failed!\n",
                  filename);
            return;
      }

      /* create the dump index */
      dump_index.magic_number = DUMP_INDEX_MAGIC;
      dump_index.version_number = DUMP_INDEX_VERSION;
      dump_index.timebuf.tv_sec = dh->time.tv_sec;
      dump_index.timebuf.tv_usec = dh->time.tv_usec;

      /* write the index header */
      if (write(ifd, (const void *)&dump_index,
            sizeof(dump_index_t)) < 0) {
                  if (cmp_debug) {
                        fprintf(KL_ERRORFP, "\n__cmpsaveindex(): "
                              "write() of index \"%s\" failed!\n",
                              filename);
                  }
                  (void)close(ifd);
                  unlink(filename);
                  return;
      }

      for (i = 0; i < NUM_BUCKETS; i++) {
            tmpptr = page_index[i];
            while (tmpptr) {
                  if (write(ifd, (const void *)tmpptr,
                        sizeof(ptableindex)) != sizeof(ptableindex)) {
                              fprintf(KL_ERRORFP,
                                    "\n__cmpsaveindex(): save of "
                                    "index \"%s\" failed!\n",
                                    filename);
                              (void)close(ifd);
                              unlink(filename);
                              return;
                  }
                  tmpptr = tmpptr->next;
            }
      }
      if (cmp_debug) {
            fprintf(KL_ERRORFP, "complete.\n");
      }
      (void)close(ifd);
}

/*
 * __cmpuncompress_page_rle() -- Uncompress a buffer of data using
 *                               the run length encoding mechanism
 *
 * Return the flags, size, and the number of bytes read. It also
 * verifies the compression type.
 */
static int
__cmpuncompress_page_rle(unsigned char *cbuf, unsigned char *ucbuf,
      int flags, kaddr_t blk_size, int *new_size)
{
      int i;
      unsigned char value, count, cur_byte;
      kaddr_t ri, wi;

      /* initialize the read / write indices */
      ri = wi = 0;

      /* otherwise decompress using run length encoding */
      while(ri < blk_size) {
            cur_byte = cbuf[ri++];
            if (cur_byte == 0) {
                  count = cbuf[ri++];
                  if (count == 0) {
                        ucbuf[wi++] = 0;
                  } else {
                        value = cbuf[ri++];
                        for (i = 0; i <= count; i++) {
                              ucbuf[wi++] = value;
                        }
                  }
            } else {
                  ucbuf[wi++] = cur_byte;
            }

            /* if our write index is beyond the page size, exit out */
            if (wi > KL_DUMP_PAGE_SIZE) {
                  if (cmp_debug) {
                        fprintf(KL_ERRORFP,
                              "ERROR: Attempted to decompress beyond"
                              " page boundaries: file corrupted!\n");
                  }
                  return (0);
            }
      }

      /* set return size to be equal to uncompressed size (in bytes) */
      *new_size = wi;
      return 1;
}

/*
 * __cmpuncompress_page_gzip() -- Uncompress a buffer of data using
 *                                the uncompress() gzip function.
 *
 * Return the flags, size, and the number of bytes read. It also
 * verifies the compression type.
 */
static int
__cmpuncompress_page_gzip(unsigned char *cbuf, unsigned char *ucbuf,
      int flags, kaddr_t blk_size, int *new_size)
{
      ulong retlen = KL_DUMP_PAGE_SIZE;

      /* use the gzip uncompress() function on a per-page basis */
      switch (uncompress(ucbuf, &retlen, cbuf, blk_size)) {
            case Z_OK:
                  *new_size = retlen;
                  if (retlen != KL_DUMP_PAGE_SIZE) {
                        fprintf(KL_ERRORFP,
                              "__cmpuncompress_page_gzip(): "
                              "returned invalid page length!\n");
                        return (0);
                  }
                  break;

            case Z_MEM_ERROR:
                  fprintf(KL_ERRORFP,
                        "__cmpuncompress_page_gzip(): Z_MEM_ERROR "
                        "(not enough memory)!\n");
                  return (0);

            case Z_BUF_ERROR:
                  fprintf(KL_ERRORFP,
                        "__cmpuncompress_page_gzip(): Z_BUF_ERROR "
                        "(not enough room in buffer output)!\n");
                  return (0);

            case Z_DATA_ERROR:
                  fprintf(KL_ERRORFP,
                        "__cmpuncompress_page_gzip(): Z_DATA_ERROR "
                        "(input data corrupted)!\n");
                  return (0);

            default:
                  /* unknown case? */
                  return (0);
      }

      /* return success */
      return (1);
}

/* 
 * __cmpcleanindex()
 *
 * Clean out the current compression index data structures.
 */
static void
__cmpcleanindex(void)
{
      int i;
      ptableindex *ptr1, *ptr2;

      for (i = 0; i < NUM_BUCKETS; i++) {
            if (page_index[i]) {
                  ptr1 = page_index[i];
                  while (ptr1) {
                        ptr2 = ptr1;
                        ptr1 = ptr1->next;
                        free(ptr2);
                  }
            }
      }
}

/*
 * __cmploadindex()
 *
 * Load the crash dump index from disk -- this is done so we
 * can cache the page contents and start up quickly, instead
 * of reading through the entire crash dump again.
 */
static int
__cmploadindex(char *indexname)
{
      int ifd, result;
      ptableindex *ptr1, *ptr2;
      dump_index_t dump_index;

      /* if we aren't given an index name, bail */
      if ((!indexname) || (indexname[0] == '\0')) {
            if (cmp_debug) {
                  fprintf(KL_ERRORFP, "__cmploadindex(): "
                        "no index filename given!\n");
            }
            return (-1);
      }

      /* open up the file if possible */
      if ((ifd = open(indexname, O_RDONLY)) < 0) {
            if (cmp_debug) {
                  fprintf(KL_ERRORFP, "__cmploadindex(): "
                        "cannot open() index file [%d]: %s!\n",
                        errno, strerror(errno));
            }
            return (-1);
      }

      /* grab the index header */
      if (read(ifd, (void *)&dump_index,
            sizeof(dump_index_t)) != sizeof(dump_index_t)) {
                  if (cmp_debug) {
                        fprintf(KL_ERRORFP, "__cmploadindex(): "
                              "read() on index file failed!\n");
                  }
                  (void)close(ifd);
                  return (-1);
      }

      /* make sure the dump index magic is valid */
      if (dump_index.magic_number != DUMP_INDEX_MAGIC) {
            if (cmp_debug) {
                  fprintf(KL_ERRORFP, "__cmploadindex(): "
                        "DUMP_INDEX_MAGIC in index file is wrong!\n");
            }
            (void)close(ifd);
            return (-1);
      }

      /* make sure the version number is up to date */
      if (dump_index.version_number < DUMP_INDEX_VERSION) {
            if (cmp_debug) {
                  fprintf(KL_ERRORFP, "__cmploadindex(): "
                        "DUMP_INDEX_VERSION in index file "
                        "out of date!\n");
            }
            (void)close(ifd);
            return (-1);
      }

      /* validate the seconds */
      if (dump_index.timebuf.tv_sec != dh->time.tv_sec) {
            if (cmp_debug) {
                  fprintf(KL_ERRORFP, "__cmploadindex(): "
                        "time (seconds) in index file wrong!\n");
            }
            (void)close(ifd);
            return (-1);
      }

      /* validate the microseconds (!) */
      if (dump_index.timebuf.tv_usec != dh->time.tv_usec) {
            if (cmp_debug) {
                  fprintf(KL_ERRORFP, "__cmploadindex(): "
                        "time (microseconds) in index file wrong!\n");
            }
            (void)close(ifd);
            return (-1);
      }

      if (cmp_debug) {
            fprintf(KL_ERRORFP,
                  "Attempting to load previous "
                  "index \"%s\" ... ", indexname);
      }

      /* okee, we're good to go -- try to load the index */
      result = 1;
      while (result) {
            ptr1 = (ptableindex *)malloc(sizeof(ptableindex));
            result = read(ifd, (void *)ptr1, sizeof(ptableindex));
            if (!result) {
                  if (cmp_debug) {
                        fprintf(KL_ERRORFP, "complete.\n");
                  }
                  (void)close(ifd);
                  return (1);
            } else if (result < 0) {
                  if (cmp_debug) {
                        fprintf(KL_ERRORFP, "__cmploadindex(): "
                              "index file read() failed!\n");
                  }
                  (void)close(ifd);
                  return (-1);
            } else if (result != sizeof(ptableindex)) {
                  if (cmp_debug) {
                        fprintf(KL_ERRORFP, "__cmploadindex(): "
                              "index file read() short!\n");
                  }
                  (void)close(ifd);
                  return (-1);
            }
            ptr1->next = (ptableindex *)NULL;
            ptr2 = page_index[ptr1->hash];
            page_index[ptr1->hash] = ptr1;
            page_index[ptr1->hash]->next = ptr2;
      }

      if (cmp_debug) {
            fprintf(KL_ERRORFP,
                  "__cmploadindex(): read() past end of index file!\n");
      }
      (void)close(ifd);
      return (-1);
}

/*
 * __cmpconvertaddr()
 *
 * Convert an address over to something that works for the compressed
 * page indexing scheme.
 */
static kaddr_t
__cmpconvertaddr(kl_dump_page_t *dp)
{
      kaddr_t paddr;

      if((KL_NBPW == 4) && IS_BIG_ENDIAN()){
            paddr = (kaddr_t)KL_GET_UINT32(((void*)&dp->address)+4);
      } else {
            if((KL_ARCH == KL_ARCH_I386) && (dp->address >= 0xffffffff)) {    
                  /*
                   * At this point, no idea whether dump file contains
                   * PAE enabled. Hence, get the 64 bit address as it is
                   * if the dump file contains. Otherwise, it reads 32
                   * bit address since the i386 ARCH supports and causes
                   * errorneous values.
                   */
                  paddr = (kaddr_t)dp->address;
            } else {
                  paddr = (kaddr_t)KL_GET_PTR(&dp->address);
            }
      }
      return (paddr);
}

/*
 * cmpphashbase()
 *
 * Compute the hash algorithm based on the 64 vs. 32 bit field.  The
 * hash flag tells us whether we are using the return value for hashing,
 * or for page offset data.
 */
static kaddr_t
__cmpphashbase(kaddr_t in_addr, int hash)
{
      in_addr &= KL_DUMP_PAGE_MASK;
      if (!hash){
            in_addr >>= KL_DUMP_PAGE_SHIFT;
      }
      return in_addr;

/*    return(in_addr & KL_PAGE_MASK); */
}

/*
 * __cmpphash()
 *
 * A hash function to determine the bucket for a given address.
 */
static int
__cmpphash(kaddr_t addr)
{
      return (__cmpphashbase(addr, 0) % NUM_BUCKETS);
}

/*
 * __cmpcheckpageheader()
 *
 * Check the page header from the crash dump.  Pretty generic.
 */
static int
__cmpcheckpageheader(int fd, kl_dump_page_t *dp)
{
      /* see if this is the end of the dump */
      if (KL_GET_UINT32(&dp->flags) & KL_DUMP_DH_END) {
            return (0);
      }

      return (1);
}

/*
 * __cmppindexcreate()
 *
 * Create a new compression index file.  This handles the new file
 * format different from IRIX.
 */
static int
__cmppindexcreate(int fd, char *indexname, int flags)
{
      int i, counter = 0, psize = sizeof(kl_dump_page_t);
      kaddr_t cur_addr = dump_header_offset+(off_t)KL_DUMP_BUFFER_SIZE;
      ptableindex *ptr1, *ptr2;

      if (cmp_debug) {
            fprintf(KL_ERRORFP, "cmppindexcreate(): Number of pages in "
                  "dump: %d\n", dh->num_dump_pages);
            fprintf(KL_ERRORFP, "cmppindexcreate(): Dump page size in "
                  "dump: %"FMT64"d\n", (kaddr_t)KL_DUMP_PAGE_SIZE);
/*          fprintf(KL_ERRORFP, "cmppindexcreate(): Dump page size in " */
/*                "dump: %d\n", dh->page_size); */
      }

      /* lseek() to the right location ... */
      if (lseek(fd, cur_addr, SEEK_SET) < 0) {
            if (cmp_debug) {
                  fprintf(KL_ERRORFP, "__cmppindexcreate(): lseek() "
                        "to page location failed!\n");
            }
            return (-1);
      }

      /* start reading out blocks of pages and storing them ... */
      while (1) {
            /* allocate a page table index point */
            ptr1 = (ptableindex *)calloc(1, sizeof(ptableindex));
            if (!ptr1) {
                  if (cmp_debug) {
                        fprintf(KL_ERRORFP, "__cmppindexcreate(): "
                              "calloc() of page index failed!\n");
                  }
                  return (-1);
            }

            /* get the next block */
            if ((i = read(fd, (char *)&(ptr1->dir), psize)) < 0) {
                  if (cmp_debug) {
                        fprintf(KL_ERRORFP, "__cmppindexcreate(): "
                              "read() of page index failed!\n");
                  }
                  free(ptr1);
                  return (-1);
            }

            /* did we get a full dump page? */
            if (i != psize) {
                  free(ptr1);
                  return (1);
            }

            /* increment the current address */
            cur_addr += psize;

            /* get the page header */
            if (!(__cmpcheckpageheader(fd, &(ptr1->dir)))) {
                  free(ptr1);
                  return (1);
            }

            /* insert the page table index */
            ptr1->addr = __cmpconvertaddr(&(ptr1->dir));
            ptr1->coreaddr = cur_addr;
            ptr1->hash = __cmpphash(ptr1->addr);
            ptr1->next = (ptableindex *)NULL;

            /* insert the pointer into the table */
            ptr2 = page_index[ptr1->hash];
            page_index[ptr1->hash] = ptr1;
            page_index[ptr1->hash]->next = ptr2;

            if (cmp_debug == 2) {
                  fprintf(KL_ERRORFP,
                        "__cmppindexcreate(): "
                        "addr = 0x%"FMTPTR"x, hash = %-6d, "
                        "counter = %-6d, cur_addr = 0x%"FMTPTR"x\n",
                        ptr1->addr, ptr1->hash, counter, cur_addr);
            }

            if (!flags && !(counter & 0xfff)) {
                  fprintf(KL_ERRORFP, ".");
            }

            /* seek to the next block */
            if (lseek(fd, KL_GET_UINT32(&ptr1->dir.size), SEEK_CUR) < 0) {
                  if (cmp_debug) {
                        fprintf(KL_ERRORFP, "__cmppindexcreate(): "
                              "lseek() to next page "
                              "index failed! errno=%d\n", errno);
                  }
                  free(ptr1);
                  return (-1);
            }

            /* increment the buffer offset and counter */
            cur_addr += KL_GET_UINT32(&ptr1->dir.size);
            counter++;
      }

      /* NOTREACHED */
      return (1);
}

/*
 * __cmpcheckheader()
 *
 * Make sure the dump header is valid.  We store a static copy here
 * in the compression library to handle our own internal needs for
 * verifying the index file, knowing the size of the header, stat
 * information, etc.
 *
 * NOTE: The fd passed in is open already by the time we get here.
 */
static int
__cmpcheckheader(int fd)
{
      /* Get a pointer to the common dump_header
       */
      if (!(dh = KL_DUMP_HEADER)) {
            return(-1);
      }

      /* make sure the MAGIC_NUMBER is valid 
       */
      if (dh->magic_number == KL_DUMP_MAGIC_S390SA){
                KLP->dump->core_type = s390_core;
        } else if ((dh->magic_number != KL_DUMP_MAGIC_NUMBER) &&
            (dh->magic_number != KL_DUMP_MAGIC_LIVE)) {
            if (cmp_debug) {
                  fprintf(KL_ERRORFP, "__cmpcheckheader(): "
                        "DUMP_MAGIC_NUMBER in dump header - WRONG!\n");
            }
            return (-1);
      }

      /* if the dump has <= 1 page, return 0 */
      if (dh->num_dump_pages <= 1) {
            return (0);
      }
      
      /* return success */
      return (1);
}

/*
 * kl_cmpinit()
 *
 * Initialize the compression code.  This will open the crash
 * dump, read in the dump header and the chunks of pages, and
 * store them into our hash table.
 */
int
kl_cmpinit(int fd, char *indexname, int flags)
{
      int mysize = 0, i = 0;
      char iname[BUFSIZ], *strtmp;

      /* configure the page entry tables */
      page_table = (ptableentry **)malloc((NUM_BUCKETS+1) *
            sizeof(ptableentry *));
      if (page_table == (ptableentry **)NULL) {
            fprintf(KL_ERRORFP, "Page table allocation failure!  "
                  "Exiting!\n");
            exit(1);
      }

      /* configure the page index tables */
      page_index = (ptableindex **)malloc((NUM_BUCKETS+1) *
            sizeof(ptableindex *));
      if (page_index == (ptableindex **)NULL) {
            fprintf(KL_ERRORFP, "Page index allocation failure!  "
                  "Exiting!\n");
            exit(1);
      }

      /* make sure the header is valid */
      mysize = __cmpcheckheader(fd);
      if (mysize < 0) {
            KL_ERROR = KLE_INVALID_DUMP_HEADER;
            return (-1);
      } else if (!mysize) {
            KL_ERROR = KLE_DUMP_HEADER_ONLY;
            return (-1);
      }

        if(KLP->dump->core_type == s390_core){
                /* no initialization needed */
                return 1;
        }

      /* see if we're given a filename */
      memset(iname, 0, BUFSIZ);
      if ((!indexname) || (indexname[0] == '\0')) {
            /* rip up the name */
            strtmp = (char *)strtok(KLP->dump->dump, ".");
            if (!strtmp) {
                  sprintf(iname, "index");
            } else {
                  strtmp = (char *)strtok(NULL, ".");
                  if (!strtmp) {
                        sprintf(iname, "index");
                  } else {
                        /* make sure we have a valid number */
                        mysize = 0;
                        while ((strtmp[i] >= '0') &&
                              (strtmp[i] <= '9')) {
                                    mysize *= 10;
                                    mysize += 
                                          (int)(strtmp[i] - '0');
                                    i++;
                        }
                  }
                  if (dh->magic_number == KL_DUMP_MAGIC_LIVE) {
                        sprintf(iname, "live_index.%d", mysize);
                  } else {
                        sprintf(iname, "index.%d", mysize);
                  }
            }
      } else {
            strncpy(iname, indexname, strlen(indexname));
      }

      /* try to load the index from disk */
      if (__cmploadindex(iname) < 0) {

            /* no disk image -- clean the current one */
            __cmpcleanindex();

            /* create a new index */
            if (__cmppindexcreate(fd, iname, flags) < 0) {

                  /* index couldn't be created -- bail! */
                  KL_ERROR = KLE_DUMP_INDEX_CREATION;
                  return (-1);
            }

            /* save the current index created */
            __cmpsaveindex(iname, flags);
      }

      /* return success */
      return (1);
}


/*
 * Name: kl_compress_gzip()
 * Func: performs gzip compression
 *     - old:      input buffer (uncompressed)
 *     - old_size: size of input buffer
 *     - new:      output buffer (compressed)
 *     - new_size: max size of output buffer (IN)
 *     - RETVAL:   size of compressed output buffer (OUT)
 */
 
int
kl_compress_gzip(const char *old, uint32_t old_size, char *new, uint32_t new_size)
{
      int rc;
      unsigned long len = old_size;
      rc = compress(new, &len, old, new_size);
      switch(rc){
            case Z_OK:
                  rc = len;
                  break;
            case Z_MEM_ERROR:
                  fprintf(KL_ERRORFP,
                        "Z_MEM_ERROR (not enough memory)!\n");
                  rc = -1;
                  break;
            case Z_BUF_ERROR:
                  /* In this case the compressed output is bigger than the uncompressed */
                  rc = -1;
                  break;
            case Z_DATA_ERROR:
                  fprintf(KL_ERRORFP,
                        "Z_DATA_ERROR (input data corrupted)!\n");
                  rc = -1;
                  break;
            default:
                  fprintf(KL_ERRORFP,
                        "Z_UNKNOWN_ERROR (rc 0x%x unknown)!\n",rc);
                  rc = -1;
                  break;
      }
      return rc;
}


/*
 * Name: kl_compress_none()
 * Func: used for uncompressed dumps
 */
int
kl_compress_none(const char *old, uint32_t old_size, char *new, uint32_t new_size)
{
      return -1;
}
 
/*
 * Name: _kl_compress_rle()
 * Func: Compress a KL_DUMP_PAGE_SIZE page down to something more reasonable,
 *     if possible.  This is the same routine we use in IRIX.
 *
 *     XXX - this needs to be changed for greater than 32-bit systems.
 */
 
static int inline
_kl_compress_rle(const char *old, char *new, uint32_t size)
{
      int ri, wi, count = 0;
      u_char value = 0, cur_byte;
 
      /*
       * If the block should happen to "compress" to larger than the
       * buffer size, allocate a larger one and change cur_buf_size.
       */
 
      wi = ri = 0;
 
      while (ri < size) {
            if (!ri) {
                  cur_byte = value = old[ri];
                  count = 0;
            } else {
                  if (count == 255) {
                        if (wi + 3 > size) {
                              return size;
                        }
                        new[wi++] = 0;
                        new[wi++] = count;
                        new[wi++] = value;
                        value = cur_byte = old[ri];
                        count = 0;
                  } else {
                        if ((cur_byte = old[ri]) == value) {
                              count++;
                        } else {
                              if (count > 1) {
                                    if (wi + 3 > size) {
                                          return size;
                                    }
                                    new[wi++] = 0;
                                    new[wi++] = count;
                                    new[wi++] = value;
                              } else if (count == 1) {
                                    if (value == 0) {
                                          if (wi + 3 > size) {
                                                return size;
                                          }
                                          new[wi++] = 0;
                                          new[wi++] = 1;
                                          new[wi++] = 0;
                                    } else {
                                          if (wi + 2 > size) {
                                                return size;
                                          }
                                          new[wi++] = value;
                                          new[wi++] = value;
                                    }
                              } else { /* count == 0 */
                                    if (value == 0) {
                                          if (wi + 2 > size) {
                                                return size;
                                          }
                                          new[wi++] = value;
                                          new[wi++] = value;
                                    } else {
                                          if (wi + 1 > size) {
                                                return size;
                                          }
                                          new[wi++] = value;
                                    }
                              } /* if count > 1 */
 
                              value = cur_byte;
                              count = 0;
 
                        } /* if byte == value */
 
                  } /* if count == 255 */
 
            } /* if ri == 0 */
            ri++;
      }
      if (count > 1) {
            if (wi + 3 > size) {
                  return size;
            }
            new[wi++] = 0;
            new[wi++] = count;
            new[wi++] = value;
      } else if (count == 1) {
            if (value == 0) {
                  if (wi + 3 > size)
                        return size;
                  new[wi++] = 0;
                  new[wi++] = 1;
                  new[wi++] = 0;
            } else {
                  if (wi + 2 > size)
                        return size;
                  new[wi++] = value;
                  new[wi++] = value;
            }
      } else { /* count == 0 */
            if (value == 0) {
                  if (wi + 2 > size)
                        return size;
                  new[wi++] = value;
                  new[wi++] = value;
            } else {
                  if (wi + 1 > size)
                        return size;
                  new[wi++] = value;
            }
      } /* if count > 1 */
 
      value = cur_byte;
      count = 0;
      return wi;
}

/*
 * Name: kl_compress_rle()
 * Func: performs runlength encoding compression
 *     - old:      input buffer (uncompressed)
 *     - old_size: size of input buffer
 *     - new:      output buffer (compressed)
 *     - new_size: max size of output buffer (IN)
 *     - RETVAL:   size of compressed output buffer (OUT)
 */
int
kl_compress_rle(const char *old, uint32_t old_size, char *new, uint32_t size)
{
      int rc;
      rc = _kl_compress_rle(old, new, size);
      /* _kl_compress_rle() returns size if page cannot be comressed */
      if(rc >= size)
            rc = -1;
      return rc;
}


Generated by  Doxygen 1.6.0   Back to index