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

kl_hwgraph.c

/*
 * $Id: kl_hwgraph.c,v 1.1 2004/12/21 23:26:20 tjm Exp $
 *
 * This file is part of libhwconfig.
 * A library which provides access to Linux system kernel dumps.
 *
 * Created by Silicon Graphics, Inc.
 *
 * Copyright (C) 2003-2004 Silicon Graphics, Inc. All rights reserved.
 *
 * 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>
#include <kl_hwgraph.h>

#define SUPPORT_DEVFS 1

kaddr_t hwgraph_root_addr = 0;
vertex_t *hwgraph_root = (vertex_t *)NULL;
#ifdef SUPPORT_DEVFS
int DEVFS_ENTRY_SZ = 0;
int hwgraph_devfs = 0;
#endif
int DENTRY_SZ = 0;
int LABELCL_INFO_SZ = 0;
int LABEL_INFO_SZ = 0;

/* Function prototypes
 */
int init_hwgraph(int);

/* 
 * kl_alloc_path_rec()
 */
path_rec_t *
kl_alloc_path_rec(vertex_t *vertex, int aflg)
{
      path_rec_t *pr;

      pr = (path_rec_t*)kl_alloc_block(sizeof(path_rec_t), aflg);
      pr->vertex = vertex;
      pr->name = vertex->name;
      pr->next = pr->prev = pr;
      return(pr);
}

/*
 * kl_free_path_rec()
 */
void
kl_free_path_rec(path_rec_t *prec)
{
      kl_free_block((void *)prec);
}

/*
 * kl_free_path_records()
 */
void
kl_free_path_records(path_rec_t *path)
{
        path_rec_t *prec, *next;

        /* Make sure there actually is a path
         */
        if (!(prec = path)) {
                return;
        }
        do {
                next = prec->next;
            kl_free_path_rec(prec);
                prec = next;
        } while (prec && (prec != path));
}

/*
 * add_path_chunk()
 */
static void
add_path_chunk(path_t *path, int aflg)
{
        path_chunk_t *pcp;

        pcp = (path_chunk_t*)kl_alloc_block(sizeof(path_chunk_t), aflg);
        ENQUEUE(&path->pchunk, pcp);
}

/*
 * kl_alloc_path_table()
 */
path_t *
kl_alloc_path_table(int aflg)
{
        path_t *p;

        /* Allocate the path table
         */
        p = (path_t*)kl_alloc_block(sizeof(path_t), aflg);
        if (p) {
                /* Allocate the first path_chunk
                 */
                add_path_chunk(p, aflg);
        }
        return(p);
}

/*
 * kl_free_path_table()
 */
void
kl_free_path_table(path_t *p)
{
        int i;
        path_chunk_t *pc, *pcnext;

        pc = p->pchunk;
        do {
                for (i = 0; i <= pc->current; i++) {
                        kl_free_path_records(pc->path[i]);
                }
                pcnext = pc->next;
                kl_free_block((void *)pc);
                pc = pcnext;
        } while (pc != p->pchunk);
        kl_free_block((void *)p);
}

/*
 * kl_add_to_path()
 */
void
kl_add_to_path(path_rec_t *path, path_rec_t *new)
{
        ENQUEUE(&path, new);
}

/*
 * kl_clone_path()
 */
path_rec_t *
kl_clone_path(path_rec_t *path, int aflg)
{
        path_rec_t *new_path, *next, *p;

        new_path = (path_rec_t*)kl_alloc_block(sizeof(path_rec_t), aflg);
        new_path->vertex = path->vertex;
        new_path->name = path->name;
        new_path->next = new_path->prev = new_path;

        p = path->next;

        while (p != path) {
                next = (path_rec_t*)kl_alloc_block(sizeof(path_rec_t), aflg);
                next->vertex = p->vertex;
                next->name = p->name;
                kl_add_to_path(new_path, next);
                p = p->next;
        }
        return(new_path);
}

/* 
 * kl_delete_last_path()
 */
void
kl_delete_last_path(path_t *p)
{
      path_rec_t *prec  = PATH(p);

      PATH(p) = (path_rec_t *)NULL;
      p->pchunk->prev->current--;
      kl_free_path_records(prec);
}

/*
 * kl_push_path_rec()
 *
 *   Allocates a path_rec struct and pushes it onto the current (last)
 *   path.
 */
void
kl_push_path_rec(path_t *p, vertex_t *vp, int aflg)
{
      path_rec_t *prec;
      
      prec = kl_alloc_path_rec(vp, aflg);
      ENQUEUE(&PATH(p), prec);
}

/*
 * kl_pop_path_rec()
 *
 *   Removes the last path_rec from the current (last) path and then
 *   cleans up all associated memory.
 */
void
kl_pop_path_rec(path_t *p)
{
      path_rec_t *prec;

      prec = PATH(p)->prev;
        REMQUEUE(&PATH(p), prec);
      kl_free_path_rec(prec);
}

/*
 * kl_dup_current_path()
 */
void
kl_dup_current_path(path_t *pl, int aflg)
{
        path_rec_t *p, *np, *new_path = (path_rec_t*)NULL;
        path_chunk_t *pcp;

        p = PATH(pl);

        do {
            np = kl_alloc_path_rec(p->vertex, aflg);
                if (new_path) {
                        ENQUEUE(&new_path, np);
                } else {
                        np->next = np->prev = np;
                        new_path = np;
                }
                p = p->next;

        } while (p != PATH(pl));
        pcp = pl->pchunk->prev;
        if (pcp->current == (PATHS_PER_CHUNK - 1)) {
                add_path_chunk(pl, aflg);
                PATH(pl) = new_path;
        } else {
                pcp->path[pcp->current + 1] = new_path;
                pcp->current++;
        }
        pl->count++;
}

/*
 * kl_get_hw_pathnames()
 */
path_t *
kl_get_hw_pathnames(int aflg)
{
      int need_clone = 1;
      path_t *path;
      path_rec_t *prec;
      vertex_t *vp;

      if (!(vp = hwgraph_root)) {
            if (init_hwgraph(0)) {
                  return((path_t *)NULL);
            }
            vp = hwgraph_root;
      }

      /* Allocate the path table
       */
      path = kl_alloc_path_table(aflg);

      /* Allocate the root pathname element and "seed" the first 
       * path in the table.
       */
      prec = kl_alloc_path_rec(vp, aflg);
      PATH(path) = prec;
again:
      if(vp->v_children) {
            vp = (vertex_t *)vp->v_children;
            kl_push_path_rec(path, vp, aflg);
            goto again;
      }
      need_clone = 1;
parent:
      if (vp->v_next != vp->v_parent->children) {
            /* Clone the current path. Pop the last element off the
             * pathname and then continue on to the next item on the
             * list.
             */
            if (need_clone) {
                  kl_dup_current_path(path, aflg);
            }
            kl_pop_path_rec(path);
            vp = (vertex_t *)vp->v_next;
            kl_push_path_rec(path, vp, aflg);
            goto again;
      } else {
            if (!vp->v_children) {
                  kl_dup_current_path(path, aflg);
                  need_clone = 0;
            }
            if ((vp = (vertex_t *)vp->v_parent) && (vp != hwgraph_root)) {
                  kl_pop_path_rec(path);
                  goto parent;
            }
      }
      kl_delete_last_path(path);
      return(path);
}

/*
 * kl_find_vertex() -- traversal function that does not use recursion
 */
vertex_t *
kl_find_vertex(vertex_t *root, char *name)
{
      vertex_t *first_vp, *vp;

      init_hwgraph(0);

      if (!root) {
            first_vp = hwgraph_root;
      } else {
            first_vp = root;
      }

      vp = first_vp;
      while(vp) {
next_child:
            if (!strcmp(vp->name, name)) {
                  break;
            }
            if (vp->v_children) {
                  vp = (vertex_t *)vp->v_children;
                  goto next_child;
            }
next_parent_peer:
            vp = (vertex_t*)vp->v_next;
            if (vp == FIRST_CHILD(vp)) {
                  /* Make sure we take care of the case where the
                   * root node is an end node and there are no peers
                   * i.e., vp == vp->v_next;
                   */
                  if (vp == first_vp) {
                        return((vertex_t *)NULL);
                  }

                  /* Step back up to the parent and try the next
                   * peer.
                   */
                  vp = (vertex_t *)vp->v_parent;

                  if (vp == first_vp) {
                        /* We're back where we started and didn't
                         * find 'name' below the root vertex.
                         * Just return a NULL pointer.
                         */
                        return((vertex_t *)NULL);
                  }
                  goto next_parent_peer;
            }
      }
      return(vp);
}

/* 
 * kl_vertex_info_addr()
 */
kaddr_t
kl_vertex_info_addr(vertex_t *vp)
{
      kaddr_t addr;

#ifdef SUPPORT_DEVFS
      if (hwgraph_devfs) {
            addr = kl_kaddr(vp->dentry, "devfs_entry", "info");
            return(addr);
      }
#endif
      addr = kl_kaddr(vp->dentry, "dentry", "d_fsdata");
      return(addr);
}

#ifdef SUPPORT_DEVFS
/*
 * kl_get_vertex_devfs()
 */
vertex_t *
kl_get_vertex_devfs(kaddr_t vertex, int aflg)
{
        int namelen;
        void *devfsp;
        vertex_t *vp;

        /* Get the devfs_entry struct for this vertex
         */
        if (!DEVFS_ENTRY_SZ) {
                DEVFS_ENTRY_SZ = kl_struct_len("devfs_entry");
        }
        devfsp = kl_alloc_block(DEVFS_ENTRY_SZ, aflg);
        if (!devfsp) {
                return((vertex_t *)NULL);
        }
        GET_BLOCK(vertex, DEVFS_ENTRY_SZ, devfsp);
        if (KL_ERROR) {
                kl_free_block(devfsp);
                return((vertex_t *)NULL);
        }
        vp = kl_alloc_block(sizeof(vertex_t), aflg);
        if (!vp) {
                kl_free_block(devfsp);
                return((vertex_t *)NULL);
        }

        /* Get the vertex name
         */
        namelen = KL_UINT(devfsp, "devfs_entry", "namelen");
        vp->name = (char *)kl_alloc_block((namelen + 1), aflg);
        GET_BLOCK((vertex + kl_member_offset("devfs_entry", "name")),
                namelen, vp->name);
        vp->name[namelen] = 0;
        vp->vertex = vertex;
        vp->dentry = devfsp;
        return(vp);
}
#endif

/*
 * kl_get_vertex()
 */
vertex_t *
kl_get_vertex(kaddr_t vertex, int aflg)
{
      int namelen;
      void *dentry, *d_name;
      kaddr_t nameaddr;
      vertex_t *vp;

#ifdef SUPPORT_DEVFS
      if (hwgraph_devfs) {
            return(kl_get_vertex_devfs(vertex, aflg));
      }
#endif
      /* Get the dentry struct for this vertex
       */
      if (!DENTRY_SZ) {
            DENTRY_SZ = kl_struct_len("dentry");
      }
      dentry = kl_alloc_block(DENTRY_SZ, aflg);
      if (!dentry) {
            return((vertex_t *)NULL);
      }
      GET_BLOCK(vertex, DENTRY_SZ, dentry);
      if (KL_ERROR) {
            kl_free_block(dentry);
            return((vertex_t *)NULL);
      } 
      vp = kl_alloc_block(sizeof(vertex_t), aflg);
      if (!vp) {
            kl_free_block(dentry);
            return((vertex_t *)NULL);
      } 

      /* Get the vertex name
       */
      d_name = (void *)(dentry + kl_member_offset("dentry", "d_name"));
      namelen = KL_UINT(d_name, "qstr", "len");
      vp->name = (char *)kl_alloc_block((namelen + 1), aflg);
      nameaddr = kl_kaddr(d_name, "qstr", "name");
      GET_BLOCK(nameaddr, namelen, vp->name);
      vp->name[namelen] = 0;
      vp->vertex = vertex;
      vp->dentry = dentry;
      return(vp);
}

/*
 * kl_free_vertex()
 */
void
kl_free_vertex(vertex_t *vp)
{
      if (vp) {
            if (vp->dentry) {
                  kl_free_block(vp->dentry);
            }
            if (vp->name) {
                  kl_free_block(vp->name);
            }
            kl_free_block(vp);
      }
}

/*
 * kl_free_vertex_label()
 */
void
kl_free_vertex_label(vertex_label_t *vlabelp)
{
      if (vlabelp->name) {
            kl_free_block(vlabelp->name);
      }
      kl_free_block(vlabelp);
}

/*
 * kl_vertex_label()
 */
vertex_label_t *
kl_vertex_label(vertex_t *vp, char *name)
{
        int i, num_labels;
        char label[LABEL_LENGTH_MAX];
        kaddr_t info, label_list;
        void *infop, *labelp;
        uint64_t value, ldesc, linfo;
      vertex_label_t *vlabelp = (vertex_label_t *)NULL;

      infop = kl_alloc_block(LABELCL_INFO_SZ, K_TEMP);
      if (!infop) {
            return((vertex_label_t *)NULL);
      }
#ifdef SUPPORT_DEVFS
      if (hwgraph_devfs) {
            info = kl_kaddr(vp->dentry, "devfs_entry", "info");
      } else {
            info = kl_kaddr(vp->dentry, "dentry", "d_fsdata");
      }
#else
      info = kl_kaddr(vp->dentry, "dentry", "d_fsdata");
#endif
      GET_BLOCK(info, LABELCL_INFO_SZ, infop);
      if (KL_ERROR) {
            /* XXX -- print error */;
            return((vertex_label_t *)NULL);
      } 

      /* Check the magic number to ensure we have a
       * valid info struct...
       */
      if (*(uint64_t*)infop != INFO_MAGIC) {
#ifdef VERTEX_DEBUG
            fprintf(ofp, "BAD info magic number "
                  "(0x%lx)\n", *(uint64_t*)infop);
#endif
            kl_free_block(infop);
            return((vertex_label_t *)NULL);
      }

      num_labels = KL_UINT(infop, "labelcl_info_s", "num_labels");
      label_list = KL_UINT(infop, "labelcl_info_s", "label_list");
      kl_free_block(infop);

      labelp = kl_alloc_block(LABEL_INFO_SZ, K_TEMP);
      if (!labelp) {
            return((vertex_label_t *)NULL);
      }

      for (i = 0; i < num_labels; i++) {
            GET_BLOCK((label_list + (i *  LABEL_INFO_SZ)), 
                  LABEL_INFO_SZ, labelp);

            label[0] = 0;
            value = (uint64_t)kl_kaddr(labelp, "label_info_s", "name");
            if (value) {
                  GET_BLOCK(value, LABEL_LENGTH_MAX, label);
            }
            if (label[0] && !strcmp(label, name)) {
                  /* This is the one we're looking for...
                   */
                  ldesc = (uint64_t)kl_kaddr(labelp, 
                        "label_info_s", "desc");
                  linfo = (uint64_t)kl_kaddr(labelp, 
                        "label_info_s", "info");
                  vlabelp = (vertex_label_t *)
                        kl_alloc_block(sizeof(vertex_label_t), K_TEMP);
                  vlabelp->name = (char *)
                        kl_alloc_block(strlen(label) + 1, K_TEMP);
                  strcat(vlabelp->name, label);
                  vlabelp->desc = ldesc;
                  vlabelp->info = linfo;
                  break;
            }
      }
      kl_free_block(labelp);
      return(vlabelp);
}

/*
 * dentry_mode()
 */
unsigned int
dentry_mode(void *dentry)
{
      unsigned int mode = 0;
      kaddr_t d_inode;

      d_inode = kl_kaddr(dentry, "dentry", "d_inode");
      GET_BLOCK((d_inode + kl_member_offset("inode", "i_mode")), 
            sizeof(mode), &mode);
      return(mode);
}

/*
 * dentry_child()
 */
kaddr_t
dentry_child(vertex_t *vp)
{
      kaddr_t dentry_child = 0;
      kaddr_t d_subdirs, next, prev;

      d_subdirs = vp->vertex + kl_member_offset("dentry", "d_subdirs");
      prev = d_subdirs + kl_member_offset("list_head", "prev");
      next = KL_VREAD_PTR(prev);
      if (next != d_subdirs) {
            dentry_child = next - kl_member_offset("dentry", "d_child");
      }
      return (dentry_child);
}

/*
 * dentry_next()
 */
kaddr_t
dentry_next(vertex_t *rootvp, vertex_t *vp)
{
      int d_count;
      kaddr_t list_head, d_child, prev, next;
      kaddr_t next_dentry = 0, d_parent;
      vertex_t *listvp;

      /* Get the first element in the current list
       */
      if (vp->v_parent) {
            listvp = (vertex_t *)vp->v_parent->children;
      } else {
            listvp = rootvp;
      }
      list_head = listvp->vertex + kl_member_offset("dentry", "d_child");

      /* Get the pointer to the "next" element on the d_child list.
       * Actually, the elements have been put onto the list in reverse
       * order. That means that we need to step backwards using the
       * the prev pointer in the list_head. Makes things kind of 
       * messy.
       */
      d_parent = kl_kaddr(vp->dentry, "dentry", "d_parent");
      d_child = vp->vertex + kl_member_offset("dentry", "d_child");
      prev = d_child + kl_member_offset("list_head", "prev");
      next = KL_VREAD_PTR(prev);
again:
      if ((next - kl_member_offset("dentry", "d_subdirs")) == d_parent) {
            /* Step over the parent entry 
             */
            prev = (next + kl_member_offset("list_head", "prev"));
            next = KL_VREAD_PTR(prev);
      }
      if (next != list_head) {
            next_dentry = (next - kl_member_offset("dentry", "d_child"));
            /* Make sure the dentry is positive (count > 0)
             */
            GET_BLOCK(next_dentry, sizeof(int), &d_count);
            if (d_count == 0) {
                  /* Go to the next item on the list...
                   */
                  prev = (next + kl_member_offset("list_head", "prev"));
                  next = KL_VREAD_PTR(prev);
                  next_dentry = 0;
                  goto again;
            }
      }
      return (next_dentry);
}

#ifdef SUPPORT_DEVFS
/*
 * init_hwgraph_devfs()
 */
int
init_hwgraph_devfs(int flag)
{
      int mode;
      syment_t *sp;
      vertex_t *vp, *newvp;
      kaddr_t next_vertex;
      kaddr_t child_vertex;

      if (hwgraph_root) {
            if (flag == 1) {
                  /* XXX
                   *
                   * Free the current hwgraph and load a new one.
                   * This should only be done when analyzing a live
                   * system (the data in the dump will be static).
                   */
            } else {
                  return(0);
            }
      }
      LABELCL_INFO_SZ = kl_struct_len("labelcl_info_s");
      LABEL_INFO_SZ = kl_struct_len("label_info_s");

      if (!(sp = kl_lkup_symname("hwgraph_root"))) {
            return(1);
      }
      hwgraph_root_addr = KL_VREAD_PTR(sp->s_addr);
      if (!(hwgraph_root = kl_get_vertex(hwgraph_root_addr, K_PERM))) {
            return(1);
      }
      vp = hwgraph_root;
      while (vp) {
after_child:
            mode = KL_UINT(vp->dentry, "devfs_entry", "mode");
            if (IS_DIR(mode)) {
                  child_vertex =
                        kl_kaddr(K_PTR(vp->dentry, "devfs_entry", "u"),
                              "directory_type", "first");
                  if (child_vertex) {
                        /* Add child
                         */
                        newvp = kl_get_vertex(child_vertex, K_PERM);
                        ht_insert_child((htnode_t *)vp,
                              (htnode_t *)newvp, HT_AFTER);
                        vp = newvp;
                        goto after_child;
                  }
            }
after_next:
            next_vertex = kl_kaddr(vp->dentry, "devfs_entry", "next");
            if (next_vertex) {
                  newvp = kl_get_vertex(next_vertex, K_PERM);
                  ht_insert_peer((htnode_t *)vp,
                              (htnode_t *)newvp, HT_AFTER);
                  vp = newvp;
            } else {
                  vp = (vertex_t *)vp->v_parent;
                  if (vp == hwgraph_root) {
                        break;
                  }
                  goto after_next;
            }
            if (vp == hwgraph_root) {
                  break;
            }
      }
      return(0);
}
#endif

/*
 * init_hwgraph()
 */
int
init_hwgraph(int flag)
{
      int mode;
        syment_t *sp;
      vertex_t *vp, *newvp;
      kaddr_t next_vertex;
      kaddr_t child_vertex;

#ifdef SUPPORT_DEVFS
      if (!kl_lkup_symname("hwgfs_vfsmount")) {
            hwgraph_devfs = 1;
            return(init_hwgraph_devfs(flag));
      }
#endif

      if (hwgraph_root) {
            if (flag == 1) {
                  /* XXX
                   *
                   * Free the current hwgraph and load a new one.
                   * This should only be done when analyzing a live
                   * system (the data in the dump will be static).
                   */
            } else {
                  return(0);
            }
      }
      LABELCL_INFO_SZ = kl_struct_len("labelcl_info_s");
      LABEL_INFO_SZ = kl_struct_len("label_info_s");

      if (!(sp = kl_lkup_symname("hwgraph_root"))) {
            return(1);
      }
      hwgraph_root_addr = KL_VREAD_PTR(sp->s_addr);
      if (!(hwgraph_root = kl_get_vertex(hwgraph_root_addr, K_PERM))) {
            return(1);
      }
      vp = hwgraph_root;
      while (vp) {
after_child:
            mode = dentry_mode(vp->dentry);
            if (IS_DIR(mode)) {
                  /* Check to see if this directory has any children.
                   * If it does, then link the first one in and keep
                   * walking down the dir chain.
                   */
                  if ((child_vertex = dentry_child(vp))) {
                        /* Add child 
                         */
                        newvp = kl_get_vertex(child_vertex, K_PERM);
                        ht_insert_child((htnode_t *)vp, 
                              (htnode_t *)newvp, HT_AFTER);
                        vp = newvp;
                        goto after_child;
                  }

                  /* No more children. We now fall down and see if
                   * there are any more same-level entries.
                   */
            }
after_next:
            if ((next_vertex = dentry_next(hwgraph_root, vp))) {
                  newvp = kl_get_vertex(next_vertex, K_PERM);
                  ht_insert_peer((htnode_t *)vp, 
                              (htnode_t *)newvp, HT_AFTER);
                  vp = newvp;
            } else {
                  vp = (vertex_t *)vp->v_parent;
                  if (vp == hwgraph_root) {
                        break;
                  }
                  goto after_next;
            }
            if (vp == hwgraph_root) {
                  break;
            }
      }
      return(0);
}

Generated by  Doxygen 1.6.0   Back to index