/*************************************************************************** * * Copyright (C) 2001 International Business Machines * All rights reserved. * * This file is part of the GPFS mmfslinux kernel module. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *************************************************************************** */ /* @(#)64 1.24 src/avs/fs/mmfs/ts/kernext/gpl-linux/kdump.c, mmfs, avs_rgpfs24, rgpfs24s011a 3/14/07 10:53:37 */ /* Program to dump kernel memory by address or symbol on a running system. Can also get formatted dumps of some kernel data structures. Invoke as 'kdump /var/mmfs/tmp/complete.map.'. */ /* * Contents: * Init * CopyString * SymbolHash * LookupSymbolByName * AddUniqueSymbol * AddSymbol * SymbolAddrCompare * SortSymbols * LookupSymbolByAddr * ExactAddrToSymbol * ReadKernelMap * ReadKernel * GetSymbolValue * kMalloc * kFree * CondFree * DumpMemory * GetHex * PrintCxiNode_t * Usage * DoHelp * main * */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* String storage area. Strings are allocated at successively higher * addresses within this area, and are never deallocated. */ char* StringAreaP = NULL; #define STRING_AREA_SIZE (4*1024*1024) int StringAreaSizeLeft; /* Symbol storage area. Each symbol has an address, some flags, and * a name. Symbols are linked into a hash table by name. Although the * symbol array itself is not sorted, the auxiliary array SymsByAddrPP is * sorted by address to allow fast binary search by address. */ int NSymbols = 0; int SymbolTableSorted = 0; #define MAX_SYMBOLS (128*1024) struct Symbol* SymbolAreaP = NULL; struct Symbol** SymsByAddrPP = NULL; #define SYM_HASH_BUCKETS 4093 struct Symbol* SymbolHashAnchor[SYM_HASH_BUCKETS]; /* Stab area. Structure sizes and member offset for * dump data formatting. */ int NStabs = 0; #define MAX_STABS (128*1024) struct Stab *StabAreaP = NULL; #define STAB_HASH_BUCKETS 4093 struct Stab* StabHashAnchor[STAB_HASH_BUCKETS]; /* Handle of /dev/kmem */ int KmemHandle = -1; /* Bounds of the initial portion of kernel virtual memory. These are initialized to liberal values, then tightened by reading kernel variables. */ unsigned long LowKernelAddr = 4096; unsigned long HighKernelAddr = -1L; unsigned long HighSymbolAddr = 0; /* Head of list of vm_struct structs copied from the kernel. These define which areas of kernel virtual memory are legal to access through /dev/kmem. */ struct vmStruct* vmListHeadP = NULL; /* Are we reading a core file or /dev/kmem ? */ int isCore = 0; /* core file descriptor */ int coreFD = -1; int savefdout = -1; int savefdin = -1; /* 2.6.5 ppc64/x86_64 /dev/kmem can't read high memory */ #if LINUX_KERNEL_VERSION >= 2060900 \ || ( LINUX_KERNEL_VERSION > 2060000 && (defined(GPFS_ARCH_PPC64) || defined(GPFS_ARCH_X86_64))) #define USE_KDUMP0 #endif void cmdpclose(FILE *fp) { if (fp) { /* reconnect stdout/stdin */ if (savefdout >= 0) { fflush(stdout); dup2(savefdout, fileno(stdout)); close(savefdout); savefdout = -1; } if (savefdin >= 0) { fflush(stdin); dup2(savefdin, fileno(stdin)); close(savefdin); savefdin = -1; } fflush(fp); pclose(fp); fflush(NULL); } } FILE * cmdpopen(char *cmd) { FILE *fp; fp = popen(cmd, "w"); if (fp) { /* save current connections to stdout/stdin */ savefdout = dup(fileno(stdout)); savefdin = dup(fileno(stdin)); /* close stdout and redirect stdout messages to cmd */ if (dup2(fileno(fp), fileno(stdout)) < 0) { perror("cmdpopen: dup2()"); cmdpclose(fp); return NULL; } /* close stdin and redirect stdin messages to cmd */ if (dup2(fileno(fp), fileno(stdin)) < 0) { perror("cmdpopen: dup2()"); cmdpclose(fp); return NULL; } } return fp; } /* Initialize */ void Init() { int i; /* Initialize string area */ StringAreaP = (char*) malloc(STRING_AREA_SIZE); assert(StringAreaP != NULL); StringAreaSizeLeft = STRING_AREA_SIZE; /* Allocate storage for symbols and put one symbol in the * table at address 0 */ SymbolAreaP = (struct Symbol *)malloc(MAX_SYMBOLS*sizeof(struct Symbol)); assert(SymbolAreaP != NULL); for (i=0; i < SYM_HASH_BUCKETS ; i++) SymbolHashAnchor[i] = NULL; StabAreaP = (struct Stab *)malloc(MAX_STABS*sizeof(struct Stab)); assert(StabAreaP != NULL); for (i=0; i < STAB_HASH_BUCKETS ; i++) StabHashAnchor[i] = NULL; AddUniqueSymbol(0, 0, "NULL"); if (!isCore) { /* Open /dev/kmem */ #ifdef USE_KDUMP0 KmemHandle = open("/dev/kdump0", O_RDONLY, 0); #else KmemHandle = open("/dev/kmem", O_RDONLY, 0); #endif if (KmemHandle == -1) { #ifdef USE_KDUMP0 fprintf(stderr, "Cannot open /dev/kdump0\n"); #else fprintf(stderr, "Cannot open /dev/kmem\n"); #endif perror("open"); exit(1); } } } /* Copy a string into the string storage area and return a * pointer to the copy of the string */ static char * CopyString(const char* p) { char* newStrP = StringAreaP; int len = strlen(p); assert(len < StringAreaSizeLeft); strcpy(newStrP, p); StringAreaP += len+1; StringAreaSizeLeft -= len+1; return newStrP; } /* Hash function for symbol table names */ static int SymbolHash(const char* nameP) { int i; unsigned int hash = 0; int len = strlen(nameP); for (i=0 ; inameP, nameP) == 0) { DBG(printf("LookupSymbolByName: found '%s' addr 0x%lX\n", nameP, p->addr)); return p; } p = p->hashNextP; } return NULL; } /* Add a symbol to the table. The symbol name must not already be * present. Returns the address of the new symbol table entry. */ struct Symbol * AddUniqueSymbol(unsigned long addr, int flags, char* nameP) { struct Symbol* p; int h = SymbolHash(nameP); p = SymbolHashAnchor[h]; while (p != NULL) { if (strcmp(p->nameP, nameP) == 0) { fprintf(stderr, "Symbol %s already in table with addr 0x%lX\n" "Failed to add symbol at addr 0x%lX\n", nameP, p->addr, addr); assert(!"symbol not unique in AddUniqueSymbol"); } p = p->hashNextP; } assert(NSymbols < MAX_SYMBOLS); p = &SymbolAreaP[NSymbols]; NSymbols += 1; p->addr = addr; p->flags = flags; p->nameP = CopyString(nameP); p->hashNextP = SymbolHashAnchor[h]; SymbolHashAnchor[h] = p; SymbolTableSorted = 0; if (addr > HighSymbolAddr) HighSymbolAddr = addr; return p; } /* Add a symbol to the table. If the symbol name is already present, * append the address of the symbol to the symbol name to make it * unique. Returns the address of the new symbol table entry. */ struct Symbol * AddSymbol(unsigned long addr, int flags, char* nameP) { struct Symbol* p = LookupSymbolByName(nameP); char* uniqueNameP; int i; if (p == NULL) return AddUniqueSymbol(addr, flags, nameP); uniqueNameP = (char*) malloc(strlen(nameP) + 32); assert (uniqueNameP != NULL); sprintf(uniqueNameP, "%s.%lX", nameP, addr); for (i=1 ; ; i++) { p = LookupSymbolByName(uniqueNameP); if (p == NULL) break; sprintf(uniqueNameP, "%s.%lX.%d", nameP, addr, i); } p = AddUniqueSymbol(addr, flags, uniqueNameP); free(uniqueNameP); return p; } /* Stab processing. Creates structure name and member * offsets for dump formatting. */ static int StabHash(const char *nameP) { int i; unsigned int hash = 0; int len = strlen(nameP); for (i=0 ; inameP, nameP) == 0) { DBG(printf("LookupStabByName: found '%s'\n", nameP)); return p; } p = p->hashNextP; } return NULL; } struct Stab * AllocStab(int boff, int blen, char *nameP) { struct Stab* p; assert(NStabs < MAX_STABS); p = &StabAreaP[NStabs]; NStabs += 1; p->boff = (unsigned short)boff; p->blen = (unsigned short)blen; p->nameP = CopyString(nameP); p->memNextP = NULL; p->hashNextP = NULL; return p; } struct Stab * AddStab(struct Stab *stabP) { struct Stab *p; int h = StabHash(stabP->nameP); DBG(printf("AddStab: name %s\n", stabP->nameP)); p = StabHashAnchor[h]; while (p != NULL) { if (strcmp(p->nameP, stabP->nameP) == 0) { fprintf(stderr, "Stab %s already in table.\n", p->nameP); return NULL; } p = p->hashNextP; } stabP->hashNextP = StabHashAnchor[h]; StabHashAnchor[h] = stabP; return StabHashAnchor[h]; } /* Parse a stab string read from the output of "objdump --stabs " */ void ParseStabString(char *lineP, regex_t *stabPatP, regex_t *namePatP, regex_t *offPatP) { int rc; regmatch_t pmatch[8]; struct Stab *stabHeadP, *stabP, **tailPP; /* Match this line against struct definition regexp */ rc = regexec(stabPatP, lineP, 8, pmatch, 0); if (rc != 0) return; assert(pmatch[5].rm_so >= 0); /* Extract struct name and length from matched patterns and use them to create a Stab entry. */ lineP[pmatch[2].rm_eo] = '\0'; stabHeadP = AllocStab(0, atoi(&lineP[pmatch[4].rm_so]), &lineP[pmatch[2].rm_so]); AddStab(stabHeadP); tailPP = &stabHeadP->memNextP; /* Parse structure member definitions */ lineP += pmatch[5].rm_so; while (regexec(namePatP, lineP, 8, pmatch, 0) == 0) { /* Extract symbol name */ char *sym = &lineP[pmatch[1].rm_so]; lineP[pmatch[1].rm_eo] = '\0'; lineP += pmatch[0].rm_eo; if (*lineP == '\0') break; /* Skip over embedded struct definitions. We look for a double semicolon to mark the end of the struct. That's not correct if this struct has another embedded struct, but it's good enough. */ if (strncmp(lineP, "=s", 2) == 0) { while (true) { char *p = strchr(lineP, ';'); if (p == NULL) break; lineP = p + 1; if (*lineP == ';') { lineP++; break; } } } /* Extract offset and size */ rc = regexec(offPatP, lineP, 8, pmatch, 0); if (rc != 0) break; assert(pmatch[2].rm_so >= 0); lineP[pmatch[1].rm_eo] = '\0'; /* Create new stab entry for this struct member */ stabP = AllocStab(atoi(&lineP[pmatch[1].rm_so]), atoi(&lineP[pmatch[2].rm_so]), sym); *tailPP = stabP; tailPP = &stabP->memNextP; lineP += pmatch[0].rm_eo; } DBG(printf("%s len %d\n", stabHeadP->nameP, stabHeadP->blen); for (stabP = stabHeadP->memNextP; stabP != NULL; stabP = stabP->memNextP) printf("%4d %4d %s\n", stabP->boff, stabP->blen, stabP->nameP); ); } /* The kdump program is compiled with -gstabs+ and thus * we run objdump against it to retrieve all the stabs info. * Since kdump-kern.c also includes kernel headers we should * have stabs info for all the relevent kernel structures. */ int GenerateStabs(char *stabProg) { int rc; FILE *fP; regex_t stabPat, namePat, offPat; char buff[512]; char *lineP; /* Sample struct definition line in objdump output: 523 LSYM 0 0 00000000 38929 timezone:T(94,3)=s8tz_minuteswest:(0,1),0,32;tz_dsttime:(0,1),32,32;; */ /* Compile regexp for parsing a struct definition */ rc = regcomp(&stabPat, "^[0-9]+[ \t]+" "(LSYM|GSYM)[ \t]+" // Only LSYM or GSYM lines "[0-9]+[ \t]+" "[0-9]+[ \t]+" "[0-9a-zA-Z]+[ \t]+" "[0-9]+[ \t]+" "([a-zA-Z_][a-zA-Z0-9_]*)" // Symbol name ":T([0-9]+|\\([0-9]+,[0-9]+\\))=s" // Only struct definitions "([0-9]+)" // Size of struct "(.*)$", // Member definitions REG_EXTENDED); if (rc != 0) { regerror(rc, &stabPat, buff, sizeof(buff)); fprintf(stderr, "regcomp failed: %s\n", buff); return -1; } /* Compile regexp for extracting symbol name from member definitions */ rc = regcomp(&namePat, "([a-zA-Z_][a-zA-Z0-9_]*):" // Symbol name "([0-9]+|\\([0-9]+,[0-9]+\\))", // Type number REG_EXTENDED); if (rc != 0) { regerror(rc, &namePat, buff, sizeof(buff)); fprintf(stderr, "regcomp failed: %s\n", buff); return -1; } /* Compile regexp for extracting offset/size from member definition */ rc = regcomp(&offPat, "([0-9]+),([0-9]+);", // Bit offset, size REG_EXTENDED); if (rc != 0) { regerror(rc, &offPat, buff, sizeof(buff)); fprintf(stderr, "regcomp failed: %s\n", buff); return -1; } snprintf(buff, sizeof(buff), "/usr/bin/objdump --stabs %s", stabProg); DBG(printf("popen(%s)\n", buff)); fP = popen(buff, "r"); if (fP == NULL) { perror("popen()"); return -1; } lineP = malloc(1000000); assert(lineP != NULL); while (fgets(lineP, 1000000, fP) != NULL) ParseStabString(lineP, &stabPat, &namePat, &offPat); free(lineP); pclose(fP); regfree(&stabPat); regfree(&namePat); regfree(&offPat); return 0; } /* Comparision routine used by SortSymbols. Compares the addr fields * of the Symbol objects reachable through the pointers given as arguments. */ static int SymbolAddrCompare(const void* arg1P, const void* arg2P) { struct Symbol** s1PP = (struct Symbol**) arg1P; struct Symbol** s2PP = (struct Symbol**) arg2P; unsigned long addr1 = (*s1PP)->addr; unsigned long addr2 = (*s2PP)->addr; if (addr1 < addr2) return -1; if (addr1 > addr2) return 1; return 0; } /* Build an up-to-date copy of the SymsByAddrPP array and sort it in order by the addr fields of the associated symbols */ static void SortSymbols() { int i; if (SymbolTableSorted) return; if (SymsByAddrPP != NULL) free(SymsByAddrPP); SymsByAddrPP = (struct Symbol**) malloc(NSymbols * sizeof(struct Symbol**)); assert(SymsByAddrPP != NULL); for (i=0 ; iaddr; if (addrMid > addr) high = mid - 1; else low = mid; } return SymsByAddrPP[low]; } /* Look up a symbol by address. If an exact match for the address is found, return a pointer to the symbol name, otherwise return a pointer to an empty string. */ const char* ExactAddrToSymbol(unsigned long addr) { static const char* emptyStringP = ""; struct Symbol* sP; if (addr == 0) return emptyStringP; sP = LookupSymbolByAddr(addr); if (sP == NULL) return emptyStringP; else return sP->nameP; } /* Read in the map file containing kernel and kernel module symbols */ static void ReadKernelMap(const char* filenameP) { FILE* mapFileP; char lineP[4096]; char* p; int len; int n; unsigned long addr; char flagChar; int flags; char symbolName[4096]; mapFileP = fopen(filenameP, "r"); if (mapFileP == NULL) { fprintf(stderr, "Cannot open kernel map file %s\n", filenameP); exit(1); } for(;;) { p = fgets(lineP, sizeof(lineP), mapFileP); if (p != lineP) break; len = strlen(lineP); if (lineP[len-1] != '\n') { fprintf(stderr, "Line overflow reading map file: '%s'\n", lineP); exit(1); } /* Examples of input lines: c0100000 A _text c0314030 B vmlist c88cc364 T lockStateToString__5LkObjUxPc */ n = sscanf(lineP, "%lX %c %s\n", &addr, &flagChar, symbolName); if (n != 3) { fprintf(stderr, "Parse error in map file: '%s'\n", lineP); exit(1); } DBG(printf("map line: 0x%lX flag '%c' symbol '%s'\n", addr, flagChar, symbolName)); switch (flagChar) { case 't': case 'T': flags = FL_TEXT; break; case 'b': case 'B': flags = FL_BSS; break; case 'd': case 'D': flags = FL_DATA; break; case 'r': case 'R': flags = FL_RODATA; break; case 'A': flags = FL_SECTION; break; case '?': flags = FL_KSTRTAB; break; default: flags = 0; } #ifdef GPFS_ARCH_PPC64 if (addr < 0xC000000000000000) addr |= 0xd000000000000000L; #endif AddSymbol(addr, flags, symbolName); } fclose(mapFileP); } /* Read kernel memory after checking the address for validity. If the address is invalid, return -1, otherwise return 0. */ int ReadKernel(unsigned long addr, void* bufP, int len, int fMap) { long long desiredOffset; long long actualOffset; int lenRead; struct vmStruct* aListP; int fd; /* Check address for validity */ if (addr < LowKernelAddr) { DBG(printf("ReadKernel: addr 0x%lX < LowKernelAddr 0x%lX\n", addr, LowKernelAddr)); return -1; } if (addr > HighKernelAddr || isCore) { aListP = vmListHeadP; while (aListP != NULL) { if (addr >= aListP->startAddr && addr+len < aListP->startAddr+aListP->areaLen) { DBG(printf("ReadKernel: addr 0x%lX in range starting at 0x%lX\n", addr, aListP->startAddr)); goto addrOK; } aListP = aListP->nextAreaP; } DBG(printf("ReadKernel: addr 0x%lX out of range\n", addr)); return -1; } addrOK: if (isCore) { desiredOffset = aListP->file_offset + (addr - aListP->startAddr); fd = coreFD; } else { desiredOffset = addr - GetOffset(fMap); fd = KmemHandle; } actualOffset = lseek64(fd, desiredOffset, SEEK_SET); if (actualOffset != desiredOffset #ifdef GPFS_ARCH_PPC64 && actualOffset != (desiredOffset & 0x00000000FFFFFFFFL) ) #else ) #endif { fprintf(stderr, "offset set to 0x%llX instead of 0x%llX\n", actualOffset, desiredOffset); perror("llseek"); exit(1); } lenRead = read(fd, bufP, len); if (lenRead != len) { fprintf(stderr, "%d bytes read at addr 0x%lX\n", lenRead, addr); if (lenRead == -1) perror("read"); return -1; } return 0; } /* Read the current value of a kernel symbol. Returns 0 on success, -1 on failure. */ int GetSymbolValue(const char* nameP, void* bufP, int len) { int rc; struct Symbol* sP; sP = LookupSymbolByName(nameP); if (sP == NULL) return -1; rc = ReadKernel(sP->addr, bufP, len, 1); DBG(if (len == sizeof(long)) printf("GetSymbolValue: symbol %s has value 0x%lX\n", nameP, *((long*)bufP))); return rc; } /* Wrapper around malloc so it can be called from modules that include kernel files */ void* kMalloc(int len) { return malloc(len); } /* Wrapper around free so it can be called from modules that include kernel files */ void kFree(void* p) { free(p); } /* Conditional free. Free storage if p is not NULL. */ void CondFree(void* p) { if (p != NULL) free(p); } /* Dump a block of memory. Duplicate lines are supressed. */ void DumpMemory(char* p, int nBytes, unsigned long origin, int dumpFlags) { char* origP; union { unsigned char i1[16]; unsigned short i2[8]; unsigned int i4[4]; unsigned long long int i8[2]; } buf; int row, col, lineLen, oLen; char ch; Boolean showedDots; int i; char line[128]; origP = p; if (dumpFlags & DUMPMEM_ROUND) { p -= origin & 0x00000003; nBytes += origP - p; origin -= origP - p; } nBytes = 16 * ((nBytes+15) / 16); buf.i1[0] = *p + 1; showedDots = false; for (row=0 ; rowblen; kmemP = malloc(kmemLen); if (kmemP == NULL) return -1; rc = ReadKernel(addr, kmemP, kmemLen, 0); if (rc != 0) { fprintf(stderr, "Error reading from 0x%lX for %d bytes\n", addr, kmemLen); goto xerror; } maxsym = 0; for (stabP = stabHeadP->memNextP; stabP != NULL; stabP = stabP->memNextP) if (strlen(stabP->nameP) > maxsym) maxsym = strlen(stabP->nameP); if (maxsym > 30) maxsym = 30; indent = maxsym + 2; for (stabP = stabHeadP->memNextP; stabP != NULL; stabP = stabP->memNextP) { offchar = stabP->boff / 8; if (offchar >= kmemLen) continue; len = (stabP->blen + 7) / 8; if (offchar + len > kmemLen) len = kmemLen - offchar; printf(" %s ", stabP->nameP); pos = strlen(stabP->nameP) + 2; i = (pos < indent) ? indent - pos : (pos - indent) % 9 == 0 ? 0 : 9 - (pos - indent) % 9; printf("%*.0s", i, ""); pos += i; nwords = 0; p = kmemP + offchar; while (len > 0) { toCopy = (len > 4) ? 4 : len; memcpy(&buf, p, toCopy); p += toCopy; len -= toCopy; if (nwords >= 6 || pos > 70) { printf("\n%*.0s", indent, ""); pos = indent; nwords = 0; } if (nwords > 0) printf(" "); if (toCopy == 4) { printf("%08x", buf.i4); pos += 9; nwords++; } else if (toCopy == 2) { printf("%04x", buf.i2[0]); } else { for (i = 0; i < toCopy; i++) printf("%02x ", buf.i1[i]); } } printf("\n"); } xerror: free(kmemP); return rc; } /* Convert a hex string to a number. Sets *rcP to 0 if successful, nonzero otherwise */ static unsigned long GetHex(const char* argP, int* rcP) { char* p = NULL; unsigned long result; result = strtoul(argP, &p, 16); if (*p != '\0') *rcP = 1; else *rcP = 0; DBG(printf("strtoul argP 0x%lX arg '%s' result 0x%lX p 0x%lX *rcP %d\n", argP, argP, result, p, *rcP)); return result; } /* Print a GPFS cxiNode_t. Parameter may be NULL. */ void PrintCxiNode_t(void* parm, unsigned long addr) { cxiNode_t* cP = (cxiNode_t*) parm; if (cP == NULL) { fprintf(stderr, "NULL cxiNode_t\n"); return; } printf("cxiNode_t 0x%lX:\n", addr); printf(" osNodeP inode 0x%lX\n", cP->osNodeP); printf(" nType %s\n", cP->nType == cxiVNON ? "cxiVNON" : cP->nType == cxiVREG ? "cxiVREG" : cP->nType == cxiVDIR ? "cxiVDIR" : cP->nType == cxiVBLK ? "cxiVBLK" : cP->nType == cxiVCHR ? "cxiVCHR" : cP->nType == cxiVLNK ? "cxiVLNK" : cP->nType == cxiVSOCK ? "cxiVSOCK" : cP->nType == cxiVBAD ? "cxiVBAD" : cP->nType == cxiVFIFO ? "cxiVFIFO" : cP->nType == cxiVMPC ? "cxiVMPC" : "??"); printf(" mapReadCount %d\n", cP->mapReadCount); printf(" mapWriteCount %d\n", cP->mapWriteCount); printf(" readCount %d\n", cP->readCount); printf(" icValid 0x%X", cP->icValid); if (cP->icValid & CXI_IC_PERM) printf(" CXI_IC_PERM"); if (cP->icValid & CXI_IC_ATTR) printf(" CXI_IC_ATTR"); printf("\n"); printf(" xinfo 0x%X\n", cP->xinfo); printf(" nfsP 0x%lX\n", cP->nfsP); printf(" ctFlags %d\n", cP->ctFlags); } void openCore(char* corefile) { coreFD = open(corefile, O_RDONLY, 0); if (coreFD == -1) { fprintf(stderr, "Cannot open kernel core file %s\n", corefile); perror("open"); exit(1); } } void readCore(void* bufP, int len) { int lenActual; assert(coreFD != -1); lenActual = read(coreFD, bufP, len); if (len != lenActual) { fprintf(stderr, "Error trying to read %d bytes from core, only %d bytes read\n", len, lenActual); perror("read"); exit(1); } } void seekCore(off_t pos) { off_t curPos; assert(coreFD != -1); curPos = lseek(coreFD, pos, SEEK_SET); if (curPos != pos) { fprintf(stderr, "Error trying to seek to pos %X in core, current pos %X\n", pos, curPos); perror("seek"); exit(1); } } static void Usage() { fprintf(stderr, "Usage: kdump map-file [corefile]\n"); exit(1); } static void DoHelp() { printf("Commands:\n" " address_space addr - dump address_space\n" " cxiNode_t addr - dump cxiNode_t\n" " dentry addr - dump dcache dentry\n" " dentry_tree addr - show dcache dentry and all of its descendants\n" " fs addr - dump fs_struct \n" " inode addr - dump inode\n" " mm_struct addr - dump mm_struct\n" " page addr - dump struct page\n" " pgd addr vaddr - translate vaddr using pgd at addr\n" " super_block [addr] - dump super_block (if no addr: root superblock)\n" " task pid - dump task_struct for pid\n" " task_struct addr - dump task_struct\n" " vfsmount addr - dump vfsmount struct\n" " vfsmount_list addr - dump all vfsmount structs starting with addr\n" " vm_area_struct addr - dump vm_area_struct\n" " mem addr len - dump memory at addr\n" " mem addr stab - dump memory formatted to stab struct name\n" " mem symbol len - dump memory at address of symbol\n" " ps - display the list of running processes\n" " btp pid - display kernel stack traceback for process pid\n" " bta - display stack tracebacks for all processes\n" " q - quit\n"); } main(int argc, char* argvPP[]) { struct Symbol* sP; char lineP[1024]; char* p; char* cmdP; char* arg1P; char* arg2P; char* arg3P; char* arg4P; char* arg5P; unsigned long addr; unsigned long vaddr; unsigned long pid; int len; void* x; int rc; FILE *outfp = NULL; if (argc < 2) Usage(); if (argc == 3) ReadKernelCore(argvPP[2]); Init(); ReadKernelMap(argvPP[1]); GenerateStabs(argvPP[0]); KernInit(); for (;;) { if (outfp) { cmdpclose(outfp); outfp = NULL; } printf("> "); p = fgets(lineP, sizeof(lineP), stdin); if (p == NULL) break; p = lineP; while (isspace(*p)) p += 1; if (*p == '\0') continue; cmdP = strtok(lineP, " \t\n"); arg1P = strtok(NULL, " \t\n"); arg2P = strtok(NULL, " \t\n"); arg3P = strtok(NULL, " \t\n"); arg4P = strtok(NULL, " \t\n"); arg5P = strtok(NULL, " \t\n"); if (arg1P && *arg1P == '|' && arg2P) { outfp = cmdpopen(arg2P); arg1P = arg2P = arg3P = arg4P = arg5P = NULL; } else if (arg2P && *arg2P == '|' && arg3P) { outfp = cmdpopen(arg3P); arg2P = arg3P = arg4P = arg5P = NULL; } else if (arg3P && *arg3P == '|' && arg4P) { outfp = cmdpopen(arg4P); arg3P = arg4P = arg5P = NULL; } else if (arg4P && *arg4P == '|' && arg5P) { outfp = cmdpopen(arg5P); arg4P = arg5P = NULL; } if (strcmp(cmdP, "?") == 0) DoHelp(); else if (strcmp(cmdP, "q") == 0) break; else if (strcmp(cmdP, "quit") == 0) break; else if (strcmp(cmdP, "address_space") == 0) { if (arg1P == NULL) { DoHelp(); continue; } addr = strtoul(arg1P, NULL, 16); x = GetAddressSpace(addr); PrintAddressSpace(x, addr); CondFree(x); } else if (strcmp(cmdP, "cxiNode_t") == 0) { if (arg1P == NULL) { DoHelp(); continue; } addr = strtoul(arg1P, NULL, 16); x = GenericGet(addr, sizeof(cxiNode_t), 0); PrintCxiNode_t(x, addr); CondFree(x); } else if (strcmp(cmdP, "dentry") == 0) { if (arg1P == NULL) { DoHelp(); continue; } addr = strtoul(arg1P, NULL, 16); x = GetDentry(addr); PrintDentry(x, addr); FreeDentry(x); } else if (strcmp(cmdP, "dentry_tree") == 0) { if (arg1P == NULL) { DoHelp(); continue; } addr = strtoul(arg1P, NULL, 16); x = GetDentry(addr); PrintDentryTree(x, addr); FreeDentry(x); } else if (strcmp(cmdP, "inode") == 0) { if (arg1P == NULL) { DoHelp(); continue; } addr = strtoul(arg1P, NULL, 16); x = GetInode(addr); PrintInode(x, addr); CondFree(x); } else if (strcmp(cmdP, "vfsmount") == 0) { if (arg1P == NULL) { DoHelp(); continue; } addr = strtoul(arg1P, NULL, 16); x = GetVFSMount(addr); PrintVFSMount(x, addr); CondFree(x); } else if (strcmp(cmdP, "vfsmount_list") == 0) { if (arg1P == NULL) { DoHelp(); continue; } addr = strtoul(arg1P, NULL, 16); x = GetVFSMount(addr); PrintVFSMountList(x, addr); CondFree(x); } else if (strcmp(cmdP, "fs") == 0) { if (arg1P == NULL) { DoHelp(); continue; } addr = strtoul(arg1P, NULL, 16); x = GetFSStruct(addr); PrintFSStruct(x, addr); CondFree(x); } else if (strcmp(cmdP, "mem") == 0) { int fMap = 0; if (arg1P == NULL || arg2P == NULL) { DoHelp(); continue; } sP = LookupSymbolByName(arg1P); if (sP != NULL) { addr = sP->addr; fMap = 1; } else { addr = GetHex(arg1P, &rc); if (rc != 0) { fprintf(stderr, "Bad address %s\n", arg1P); continue; } } if (isalpha(arg2P[0])) DumpMemoryCast(addr, arg2P); else { len = strtol(arg2P, NULL, 0); x = malloc(len); if (x != NULL) { rc = ReadKernel(addr, x, len, fMap); if (rc != 0) fprintf(stderr, "Error reading from 0x%lX for %d bytes\n", addr, len); else DumpMemory((char*)x, len, addr, DUMPMEM_I4); free(x); } } } else if (strcmp(cmdP, "mm_struct") == 0) { if (arg1P == NULL) { DoHelp(); continue; } addr = strtoul(arg1P, NULL, 16); x = GetMMStruct(addr); PrintMMStruct(x, addr); CondFree(x); } else if (strcmp(cmdP, "page") == 0) { if (arg1P == NULL) { DoHelp(); continue; } addr = strtoul(arg1P, NULL, 16); x = GetPage(addr); PrintPage(x, addr); CondFree(x); } else if (strcmp(cmdP, "pgd") == 0) { if (arg1P == NULL || arg2P == NULL) { DoHelp(); continue; } addr = strtoul(arg1P, NULL, 16); vaddr = strtoul(arg2P, NULL, 16); VirtToReal(addr, vaddr); } else if (strcmp(cmdP, "task") == 0) { if (arg1P == NULL) { DoHelp(); continue; } pid = strtoul(arg1P, NULL, 10); x = GetTaskStructByPID(pid, &addr); if (x != NULL) { PrintTaskStruct(x, addr); CondFree(x); } } else if (strcmp(cmdP, "task_struct") == 0) { addr = strtoul(arg1P, NULL, 16); x = GetTaskStruct(addr); PrintTaskStruct(x, addr); CondFree(x); } else if (strcmp(cmdP, "super_block") == 0) { if (arg1P != NULL) { addr = strtoul(arg1P, NULL, 16); x = GetSuperBlock(addr); } else x = GetRootSuperBlock(&addr); PrintSuperBlock(x, addr); CondFree(x); } else if (strcmp(cmdP, "vm_area_struct") == 0) { if (arg1P == NULL) { DoHelp(); continue; } addr = strtoul(arg1P, NULL, 16); x = GetVMAreaStruct(addr); PrintVMAreaStruct(x, addr); CondFree(x); } else if (strcmp(cmdP, "ps") == 0) { DoPs(); } else if (strcmp(cmdP, "btp") == 0) { if (arg1P == NULL) { DoHelp(); continue; } pid = atoi(arg1P); BacktracePid(pid); } else if (strcmp(cmdP, "bta") == 0) { BacktraceAll(); } else fprintf(stderr, "Bad command '%s'\n", cmdP); } }