/* * This targets the 2.6.9-78.EL (on x86_64) */ #include #include #include #include #include #include #include #include #define TAG "dumpmm" #define _K /* Kernel address marker, essentially a comment */ /* * 1 - mmap * 2 - read */ #define ACCESS 2 /* * Conditionals for various boxes. * We are too lazy to read /proc/ksyms, so encode some constants, too. */ #if 0 /* r2 */ #undef CONFIG_SMP #undef CONFIG_DEBUG_SPINLOCK #define STATS 1 #define TOTAL_MAP_SIZE (899904*1024) #define PAGE_OFFSET 0xc0000000 #endif #if 1 /* simbelmyne w/2.6.9-78.EL */ #define CONFIG_SMP 1 #define CONFIG_NR_CPUS 8 #define CONFIG_DEBUG_SPINLOCK #undef CONFIG_DEBUG_SLAB #define TOTAL_MAP_SIZE (192232*1024) #define SYM_OFFSET 0xffffffff80000000 /* #define PAGE_OFFSET 0xffffffff80100000 */ #define PAGE_OFFSET 0x0000010000000000 #endif #ifdef CONFIG_DEBUG_SLAB #define DEBUG 1 #define STATS 1 #else #define DEBUG 0 #define STATS 0 #endif #ifdef CONFIG_SMP #define NR_CPUS CONFIG_NR_CPUS #else #define NR_CPUS 1 #endif unsigned long cache_cache; #if ACCESS == 1 /* * Mapping mechanics. * A := Offset * M := (void *) (kmap + A) * K := (unsigned long) (PAGE_OFFSET + A) */ void *kmap; #define A2M(a) (kmap + (unsigned long)(a)) #define A2K(a) (PAGE_OFFSET + (unsigned long)(a)) #define M2A(m) ((unsigned long)((char *)(m) - (char *)kmap)) #define M2K(m) A2K(M2A(m)) #define K2A(k) ((unsigned long)(k) - PAGE_OFFSET) #define K2M(k) A2M(K2A(k)) static unsigned int ld(unsigned long addr) { return *(unsigned int *)K2M(addr); } static unsigned long ldx(unsigned long addr) { return *(unsigned long *)K2M(addr); } #endif #if ACCESS == 2 /* */ int kfd; #define K2A(k) ((unsigned long)(k) - PAGE_OFFSET) static unsigned int ld(unsigned long addr) { unsigned int word; int rc; /* * The x86_64 has two maps. Kernel code uses pointers above * ffffffff80000000 with its CPU instructions, but /dev/kmem * only recognizes what's based off PAGE_OFFSET. Shift down. */ if (addr >= SYM_OFFSET) { addr -= SYM_OFFSET; addr += PAGE_OFFSET; } /* rc = lseek(kfd, K2A(addr), SEEK_SET); */ /* /dev/mem */ rc = lseek(kfd, addr, SEEK_SET); if (rc == (off_t)(-1)) { perror("lseek"); exit(1); } rc = read(kfd, (char *)&word, 4); if (rc < 0) { fprintf(stderr, TAG ": read error @0x%lx: %s\n", addr, strerror(errno)); exit(1); } if (rc < 4) { fprintf(stderr, TAG ": short read @0x%lx\n", addr); exit(1); } return word; } static unsigned long ldx(unsigned long addr) { unsigned long word; int rc; if (addr >= SYM_OFFSET) { addr -= SYM_OFFSET; addr += PAGE_OFFSET; } /* rc = lseek(kfd, K2A(addr), SEEK_SET); */ /* /dev/mem */ rc = lseek(kfd, addr, SEEK_SET); if (rc == (off_t)(-1)) { perror("lseek"); exit(1); } rc = read(kfd, (char *)&word, 8); if (rc < 0) { fprintf(stderr, TAG ": read error @0x%lx: %s\n", addr, strerror(errno)); exit(1); } if (rc < 8) { fprintf(stderr, TAG ": short read @0x%lx\n", addr); exit(1); } return word; } #endif /* * dst: Where to copy in our space * src: Kernel address * len: len */ static void copyout(char *dst, unsigned long src, int len) { union { unsigned int w; char s[4]; } b; while (len > 0) { b.w = ld(src); memcpy(dst, b.s, (len > 4)? 4: len); src += 4; dst += 4; len -= 4; } } /* * Structures stolen from the kernel. */ struct list_head { struct list_head *next, *prev; }; #define list_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) typedef struct { volatile int counter; } atomic_t; #ifdef CONFIG_SMP typedef struct { volatile unsigned int lock; #ifdef CONFIG_DEBUG_SPINLOCK unsigned magic; #endif } spinlock_t; #else #error "UP kernels not supported" #endif struct kmem_list3 { struct list_head slabs_partial; struct list_head slabs_full; struct list_head slabs_free; unsigned long free_objects; int free_touched; unsigned long next_reap; struct array_cache *shared; }; typedef unsigned long ksize_t; typedef struct kmem_cache_s kmem_cache_t; #define CACHE_NAMELEN 20 /* max name length for a slab cache */ struct kmem_cache_s { /* 1) per-cpu data, touched during every alloc/free */ struct array_cache *array[NR_CPUS]; unsigned int batchcount; unsigned int limit; /* 2) touched by every alloc & free from the backend */ struct kmem_list3 lists; /* NUMA: kmem_3list_t *nodelists[MAX_NUMNODES] */ unsigned int objsize; unsigned int flags; /* constant flags */ unsigned int num; /* # of objs per slab */ unsigned int free_limit; /* upper limit of objects in the lists */ spinlock_t spinlock; /* 3) cache_grow/shrink */ /* order of pgs per slab (2^n) */ unsigned int gfporder; /* force GFP flags, e.g. GFP_DMA */ unsigned int gfpflags; ksize_t colour; /* cache colouring range */ unsigned int colour_off; /* colour offset */ unsigned int colour_next; /* cache colouring */ kmem_cache_t *slabp_cache; unsigned int slab_size; unsigned int dflags; /* dynamic flags */ /* constructor func */ void (*ctor)(void *, kmem_cache_t *, unsigned long); /* de-constructor func */ void (*dtor)(void *, kmem_cache_t *, unsigned long); /* 4) cache creation/removal */ const char *name; struct list_head next; /* 5) statistics */ #if STATS unsigned long num_active; unsigned long num_allocations; unsigned long high_mark; unsigned long grown; unsigned long reaped; unsigned long errors; unsigned long max_freeable; atomic_t allochit; atomic_t allocmiss; atomic_t freehit; atomic_t freemiss; #endif #if DEBUG int dbghead; int reallen; #endif }; typedef unsigned short kmem_bufctl_t; /* * slab_t * * Manages the objs in a slab. Placed either at the beginning of mem allocated * for a slab, or allocated from an general cache. * Slabs are chained into three list: fully used, partial, fully free slabs. */ typedef struct slab_s { struct list_head list; unsigned long colouroff; void *s_mem; /* including colour offset */ unsigned int inuse; /* num of objs active in slab */ kmem_bufctl_t free; } slab_t; /* */ struct params { char *devname; char *slabname; }; unsigned long lookup_system_map(const char *mapname, const char *symbol); struct kmem_cache_s _K *find_slab_cache_by_name(char *sname); void print_full_objects(struct kmem_cache_s _K *cachep, int num); void hexdumpw(FILE *fp, unsigned long _K kaddr, int len); void parse_args(struct params *p, int argc, char **argv); void Usage(void); int main(int argc, char *argv[]) { struct params par; struct kmem_cache_s _K *cachep; unsigned int mm_size; // struct slab_s _K *slabp; #if ACCESS == 1 int kfd; #endif parse_args(&par, argc, argv); cache_cache = lookup_system_map(NULL, "cache_cache"); kfd = open(par.devname, O_RDONLY); if (kfd == -1) { fprintf(stderr, TAG ": cannot open %s: %s\n", par.devname, strerror(errno)); exit(1); } #if ACCESS == 1 kmap = mmap(NULL, TOTAL_MAP_SIZE, PROT_READ, MAP_SHARED, kfd, 0); if (kmap == MAP_FAILED) { fprintf(stderr, TAG ": cannot map %s: %s\n", par.devname, strerror(errno)); exit(1); } printf("map @0x%x[0x%x]\n", (int)kmap, TOTAL_MAP_SIZE); #endif /* self-test */ #if 0 { unsigned long _K log_buf; log_buf = lookup_system_map(NULL, "log_buf"); printf("log_buf = 0x%lx\n", log_buf); log_buf = ldx(log_buf); if (log_buf == 0) { fprintf(stderr, TAG ": zero in log_buf\n"); exit(1); } printf("*log_buf = 0x%lx\n", log_buf); hexdumpw(stdout, log_buf, 0x40); } #endif cachep = find_slab_cache_by_name(par.slabname); if (cachep == NULL) { fprintf(stderr, TAG ": cache %s not found\n", par.slabname); exit(1); } /* printf("sizeof(mm_struct) == %d (0x%x)\n", sizeof(struct mm_struct), sizeof(struct mm_struct)); */ mm_size = ld((unsigned long) &cachep->objsize); printf("size of slab object == %d (0x%x)\n", mm_size, mm_size); print_full_objects(cachep, 2); return 0; } #if 0 objp = slabp->s_mem + slabp->free*cachep->objsize; slabp->free=slab_bufctl(slabp)[slabp->free]; #endif /* */ void print_full_objects(struct kmem_cache_s _K *cachep, int num) { int i, j; unsigned int objsize, objinuse; unsigned char _K *mem; struct list_head _K *p; struct slab_s _K *slabp; objsize = ld((unsigned long) &cachep->objsize); p = (struct list_head *) ldx((unsigned long)&cachep->lists.slabs_full.next); for (i = 0; i < num; i++) { if (p == &cachep->lists.slabs_full) break; slabp = list_entry(p, slab_t, list); printf("full slab %d\n", i); hexdumpw(stdout, (unsigned long) slabp, sizeof(slab_t)); objinuse = ld((unsigned long)&slabp->inuse); mem = (unsigned char *) ldx((unsigned long)&slabp->s_mem); for (j = 0; j < objinuse; j++) { printf("object %d [%d(0x%x)]\n", j, objsize, objsize); hexdumpw(stdout, (unsigned long) mem, objsize); mem += objsize; } p = (struct list_head *) ldx((unsigned long)&p->next); } } /* * Load a value from the system map. */ unsigned long lookup_system_map(const char *mapname, const char *symbol) { struct utsname utsb; enum { MAPNAME_SIZE = 50 }; char implicit_mapname[MAPNAME_SIZE]; FILE *mapf; unsigned long val; char type; char line[80], sname[80]; int len; len = strlen(symbol); if (len == 0 || len >= 60) { fprintf(stderr, "Suspicious symbol size %d for \"%s\"\n", len, symbol); exit(1); } if (mapname == NULL) { if (uname(&utsb) != 0) { perror("uname"); exit(1); } snprintf(implicit_mapname, MAPNAME_SIZE, "/boot/System.map-%s", utsb.release); mapname = implicit_mapname; } if ((mapf = fopen(mapname, "r")) == NULL) { fprintf(stderr, "Cannot open %s: %s\n", mapname, strerror(errno)); exit(1); } while (fgets(line, 80, mapf) != NULL) { if (sscanf(line, "%lx %c %s\n", &val, &type, sname) != 3) continue; if (strcmp(sname, symbol) == 0) { fclose(mapf); /* printf("%s: %lx\n", symbol, val); */ return val; } } fprintf(stderr, "Cannot find symbol %s in %s\n", symbol, mapname); exit(1); return 0; } /* * This routine returns a kernel address in a pointer. copyout it to buf. XXX */ struct kmem_cache_s _K *find_slab_cache_by_name(char *sname) { struct list_head _K *p, *head; kmem_cache_t _K *cachep; char nameb[CACHE_NAMELEN]; unsigned long kn; /* char _K *kn */ head = &((kmem_cache_t *)cache_cache)->next; p = (struct list_head *) ldx((unsigned long)&head->next); while (p != NULL && p != head) { cachep = list_entry(p, kmem_cache_t, next); kn = ldx((unsigned long) &cachep->name); copyout(nameb, kn, CACHE_NAMELEN-1); nameb[CACHE_NAMELEN-1] = 0; if (strcmp(nameb, sname) == 0) { return cachep; } p = (struct list_head *) ldx((unsigned long)&p->next); } return NULL; } /* * Dump memory word by word. */ void hexdumpw(FILE *fp, unsigned long _K addr, int len) { int pos; int address_printed; if ((addr & 3) != 0) { len += 4 - (addr & 3); addr &= ~3; } fprintf(fp, "%08x: ", (int)addr); address_printed = 1; pos = 0; while (pos < (addr & 15)) { fprintf(fp, " "); pos += 4; } for (;;) { if (len <= 0) { if (pos != 0) fprintf(fp, "\n"); return; } if (!address_printed) { fprintf(fp, "%08x: ", (int)addr); address_printed = 1; } fprintf(fp, " %08x", ld(addr)); addr += 4; len -= 4; pos += 4; if (pos >= 16) { fprintf(fp, "\n"); address_printed = 0; pos = 0; } } } void parse_args(struct params *p, int argc, char **argv) { char *arg; p->devname = "/dev/kmem"; p->slabname = NULL; argv++; while ((arg = *argv++) != NULL) { if (arg[0] == '-') { switch (arg[1]) { case 's': if ((arg = *argv) == NULL) Usage(); argv++; Usage(); /* Not taking any options yet */ break; default: Usage(); } } else { if (p->slabname != NULL) Usage(); p->slabname = arg; } } if (p->slabname == NULL) Usage(); } void Usage() { fprintf(stderr, "Usage: dumpmm slab_name\n"); exit(1); }