diff --git a/Makefile.am b/Makefile.am index 31ec8819..5fedea36 100644 --- a/Makefile.am +++ b/Makefile.am @@ -156,7 +156,6 @@ proc_libprocps_la_SOURCES = \ proc/devname.h \ proc/escape.c \ proc/escape.h \ - proc/ksym.c \ proc/procps.h \ proc/pwcache.c \ proc/pwcache.h \ @@ -170,6 +169,7 @@ proc_libprocps_la_SOURCES = \ proc/sysinfo.h \ proc/version.c \ proc/version.h \ + proc/wchan.c \ proc/wchan.h \ proc/whattime.c \ proc/whattime.h diff --git a/proc/ksym.c b/proc/ksym.c deleted file mode 100644 index 36ee18a9..00000000 --- a/proc/ksym.c +++ /dev/null @@ -1,639 +0,0 @@ -/* - * ksym.c - kernel symbol handling - * Copyright 1998-2003 by Albert Cahalan - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "procps.h" -#include "alloc.h" -#include "version.h" -#include "sysinfo.h" /* smp_num_cpus */ -#include "wchan.h" // to verify prototypes - -#define KSYMS_FILENAME "/proc/ksyms" - -#if 0 -#undef KSYMS_FILENAME -#define KSYMS_FILENAME "/would/be/nice/to/have/this/file" -#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-hacked" -#define linux_version_code 131598 /* ? */ -#define smp_num_cpus 2 -#endif - -#if 0 -#undef KSYMS_FILENAME -#define KSYMS_FILENAME "/home/albert/ps/45621/ksyms-2.3.12" -#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.12" -#define linux_version_code 131852 /* 2.3.12 */ -#define smp_num_cpus 2 -#endif - -#if 0 -#undef KSYMS_FILENAME -#define KSYMS_FILENAME "/home/albert/ps/45621/ksyms-2.3.18ac8-MODVERS" -#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.18ac8-MODVERS" -#define linux_version_code 131858 /* 2.3.18ac8 */ -#define smp_num_cpus 2 -#endif - -#if 0 -#undef KSYMS_FILENAME -#define KSYMS_FILENAME "/home/albert/ps/45621/ksyms-2.3.18ac8-NOMODVERS" -#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.18ac8-NOMODVERS" -#define linux_version_code 131858 /* 2.3.18ac8 */ -#define smp_num_cpus 2 -#endif - -/* These are the symbol types, with relative popularity: - * ? w machine type junk for Alpha -- odd syntax - * ? S not for i386 - * 4 W not for i386 - * 60 R - * 100 A - * 125 r - * 363 s not for i386 - * 858 B - * 905 g generated by modutils? - * 929 G generated by modutils? - * 1301 b - * 2750 D - * 4481 d - * 11417 ? - * 13666 t - * 15442 T - * - * For i386, that is: "RArBbDd?tT" - */ - -#define SYMBOL_TYPE_CHARS "Tt?dDbBrARGgsWS" - -/* - * '?' is a symbol type - * '.' is part of a name (versioning?) - * "\t[]" are for the module name in /proc/ksyms - */ -#define LEGAL_SYSMAP_CHARS "0123456789_ ?.\n\t[]" \ - "abcdefghijklmnopqrstuvwxyz" \ - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - -/* System.map lines look like: - * hex num, space, one of SYMBOL_TYPE_CHARS, space, LEGAL_SYSMAP_CHARS, \n - * - * Alpha systems can start with a few lines that have the address replaced - * by space padding and a 'w' for the type. For those lines, the last space - * is followed by something like: mikasa_primo_mv p2k_mv sable_gamma_mv - * (just one of those, always with a "_mv", then the newline) - * - * The /proc/ksyms lines are like System.map lines w/o the symbol type char. - * When odd features are used, the name part contains: - * "(.*)_R(smp_|smp2gig_|2gig_)?[0-9a-fA-F]{8,}" - * It is likely that more crap will be added... - */ - -typedef struct symb { - unsigned KLONG addr; - const char *name; -} symb; - -/* These mostly rely on POSIX to make them zero. */ - -static symb hashtable[256]; - -static char *sysmap_data; -static unsigned sysmap_room; -static symb *sysmap_index; -static unsigned sysmap_count; - -static char *ksyms_data; -static unsigned ksyms_room = 4096; -static symb *ksyms_index; -static unsigned ksyms_count; -static unsigned idx_room; - -/*********************************/ - -/* Kill this: _R(smp_?|smp2gig_?|2gig_?)?[0-9a-f]{8,}$ - * We kill: (_R[^A-Z]*[0-9a-f]{8,})+$ - * - * The loop should almost never be taken, but it has to be there. - * It gets rid of anything that _looks_ like a version code, even - * if a real version code has already been found. This is because - * the inability to perfectly recognize a version code may lead to - * symbol mangling, which in turn leads to mismatches between the - * /proc/ksyms and System.map data files. - */ -#if 0 -static char *chop_version(char *arg){ - char *cp; - cp = strchr(arg,'\t'); - if(cp) *cp = '\0'; /* kill trailing module name first */ - for(;;){ - char *p; - int len = 0; - cp = strrchr(arg, 'R'); - if(!cp || cp<=arg+1 || cp[-1]!='_') break; - for(p=cp; *++p; ){ - switch(*p){ - default: - goto out; - case '0' ... '9': - case 'a' ... 'f': - len++; - continue; - case 'g' ... 'z': - case '_': - len=0; - continue; - } - } - if(len<8) break; - cp[-1] = '\0'; - } -out: - if(*arg=='G'){ - int len = strlen(arg); - while( len>8 && !memcmp(arg,"GPLONLY_",8) ){ - arg += 8; - len -= 8; - } - } - return arg; -} -#endif -static char *chop_version(char *arg){ - char *cp; - cp = strchr(arg,'\t'); - if(cp) *cp = '\0'; /* kill trailing module name first */ - for(;;){ - int len; - cp = strrchr(arg, 'R'); - if(!cp || cp<=arg+1 || cp[-1]!='_') break; - len=strlen(cp); - if(len<9) break; - if(strpbrk(cp+1,"ABCDEFGHIJKLMNOPQRSTUVWXYZ")) break; - if(strspn(cp+len-8,"0123456789abcdef")!=8) break; - cp[-1] = '\0'; - } - if(*arg=='G'){ - int len = strlen(arg); - while( len>8 && !memcmp(arg,"GPLONLY_",8) ){ - arg += 8; - len -= 8; - } - } - return arg; -} - -/***********************************/ - -static const symb *search(unsigned KLONG address, symb *idx, unsigned count){ - unsigned left; - unsigned mid; - unsigned right; - if(!idx) return NULL; /* maybe not allocated */ - if(address < idx[0].addr) return NULL; - if(address >= idx[count-1].addr) return idx+count-1; - left = 0; - right = count-1; - for(;;){ - mid = (left + right) / 2; - if(address >= idx[mid].addr) left = mid; - if(address <= idx[mid].addr) right = mid; - if(right-left <= 1) break; - } - if(address == idx[right].addr) return idx+right; - return idx+left; -} - -/*********************************/ - -/* allocate if needed, read, and return buffer size */ -static void read_file(const char *restrict filename, char **bufp, unsigned *restrict roomp) { - int fd = 0; - ssize_t done; - char *buf = *bufp; - ssize_t total = 0; - unsigned room = *roomp; - - if(!room) goto hell; /* failed before */ - if(!buf) buf = xmalloc(room); -open_again: - fd = open(filename, O_RDONLY|O_NOCTTY|O_NONBLOCK); - if(fd<0){ - switch(errno){ - case EINTR: goto open_again; - default: _exit(101); - case EACCES: /* somebody screwing around? */ - /* FIXME: set a flag to disable symbol lookup? */ - case ENOENT:; /* no module support */ - } - goto hell; - } - for(;;){ - done = read(fd, buf+total, room-total-1); - if(done==0) break; /* nothing left */ - if(done==-1){ - if(errno==EINTR) continue; /* try again */ - perror(""); - goto hell; - } - if(done==(ssize_t)room-total-1){ - char *tmp; - total += done; - /* more to go, but no room in buffer */ - room *= 2; - tmp = xrealloc(buf, room); - buf = tmp; - continue; - } - if(done>0 && done<(ssize_t)room-total-1){ - total += done; - continue; /* OK, we read some. Go do more. */ - } - fprintf(stderr,"%ld can't happen\n", (long)done); - /* FIXME: memory leak */ - _exit(42); - } - buf[total] = '\0'; // parse_ksyms() expects NUL-terminated file - *bufp = buf; - *roomp = room; - close(fd); - return; -hell: - free(buf); - *bufp = NULL; - *roomp = 0; /* this function will never work again */ - total = 0; - if(fd>0) close(fd); - return; -} - -/*********************************/ - -static int parse_ksyms(void) { - char *endp; - if(!ksyms_room || !ksyms_data) goto quiet_goodbye; - endp = ksyms_data; - ksyms_count = 0; - if(idx_room) goto bypass; /* some space already allocated */ - idx_room = 512; - for(;;){ - void *vp; - idx_room *= 2; - vp = xrealloc(ksyms_index, sizeof(symb)*idx_room); - ksyms_index = vp; -bypass: - for(;;){ - char *saved; - if(!*endp) return 1; - saved = endp; - ksyms_index[ksyms_count].addr = STRTOUKL(endp, &endp, 16); - if(endp==saved || *endp != ' ') goto bad_parse; - endp++; - saved = endp; - endp = strchr(endp,'\n'); - if(!endp) goto bad_parse; /* no newline */ - *endp = '\0'; - ksyms_index[ksyms_count].name = chop_version(saved); - ++endp; - if(++ksyms_count >= idx_room) break; /* need more space */ - } - } - - if(0){ -bad_parse: - fprintf(stderr, "Warning: "KSYMS_FILENAME" not normal\n"); - } -quiet_goodbye: - idx_room = 0; - free(ksyms_data); - ksyms_data = NULL; - ksyms_room = 0; - free(ksyms_index); - ksyms_index = NULL; - ksyms_count = 0; - return 0; -} - -/*********************************/ - -#define VCNT 16 - -static int sysmap_mmap(const char *restrict const filename, message_fn message) { - struct stat sbuf; - char *endp; - int fd; - char Version[32]; - fd = open(filename, O_RDONLY|O_NOCTTY|O_NONBLOCK); - if(fd<0) return 0; - if(fstat(fd, &sbuf) < 0) goto bad_open; - if(!S_ISREG(sbuf.st_mode)) goto bad_open; - if(sbuf.st_size < 5000) goto bad_open; /* if way too small */ - /* Would be shared read-only, but we want '\0' after each name. */ - endp = mmap(0, sbuf.st_size + 1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); - sysmap_data = endp; - while(*endp==' '){ /* damn Alpha machine types */ - if(strncmp(endp," w ", 19)) goto bad_parse; - endp += 19; - endp = strchr(endp,'\n'); - if(!endp) goto bad_parse; /* no newline */ - if(strncmp(endp-3, "_mv\n", 4)) goto bad_parse; - endp++; - } - if(sysmap_data == (caddr_t) -1) goto bad_open; - close(fd); - fd = -1; - sprintf(Version, "Version_%d", linux_version_code); - sysmap_room = 512; - for(;;){ - void *vp; - sysmap_room *= 2; - vp = xrealloc(sysmap_index, sizeof(symb)*sysmap_room); - sysmap_index = vp; - for(;;){ - char *vstart; - if(endp - sysmap_data >= sbuf.st_size){ /* if we reached the end */ - int i = VCNT; /* check VCNT times to verify this file */ - if(*Version) goto bad_version; - if(!ksyms_index) return 1; /* if can not verify, assume success */ - while(i--){ -#if 1 - const symb *findme; - const symb *map_symb; - /* Choose VCNT entries from /proc/ksyms to test */ - findme = ksyms_index + (ksyms_count*i/VCNT); - /* Search for them in the System.map */ - map_symb = search(findme->addr, sysmap_index, sysmap_count); - if(map_symb){ - if(map_symb->addr != findme->addr) continue; - /* backup to first matching address */ - while (map_symb != sysmap_index){ - if (map_symb->addr != (map_symb-1)->addr) break; - map_symb--; - } - /* search for name in symbols with same address */ - while (map_symb != (sysmap_index+sysmap_count)){ - if (map_symb->addr != findme->addr) break; - if (!strcmp(map_symb->name,findme->name)) goto good_match; - map_symb++; - } - map_symb--; /* backup to last symbol with matching address */ - message("{%s} {%s}\n",map_symb->name,findme->name); - goto bad_match; - } -good_match:; -#endif - } - return 1; /* success */ - } - sysmap_index[sysmap_count].addr = STRTOUKL(endp, &endp, 16); - if(*endp != ' ') goto bad_parse; - endp++; - if(!strchr(SYMBOL_TYPE_CHARS, *endp)) goto bad_parse; - endp++; - if(*endp != ' ') goto bad_parse; - endp++; - vstart = endp; - endp = strchr(endp,'\n'); - if(!endp) goto bad_parse; /* no newline */ - *endp = '\0'; - ++endp; - vstart = chop_version(vstart); - sysmap_index[sysmap_count].name = vstart; - if(*vstart=='V' && *Version && !strcmp(Version,vstart)) *Version='\0'; - if(++sysmap_count >= sysmap_room) break; /* need more space */ - } - } - -#ifdef BUILD_WITH_WHINE - if(0){ -bad_match: - message("Warning: %s does not match kernel data.\n", filename); - } -#endif - if(0){ -bad_version: - message("Warning: %s has an incorrect kernel version.\n", filename); - } - if(0){ -bad_alloc: - message("Warning: not enough memory available\n"); - } -#ifdef BUILD_WITH_WHINE - if(0){ -bad_parse: - message("Warning: %s not parseable as a System.map\n", filename); - } -#endif - if(0){ -bad_open: - message("Warning: %s could not be opened as a System.map\n", filename); - } - - sysmap_room=0; - sysmap_count=0; - free(sysmap_index); - sysmap_index = NULL; - if(fd>=0) close(fd); - if(sysmap_data) munmap(sysmap_data, sbuf.st_size + 1); - sysmap_data = NULL; - return 0; -} - -/*********************************/ - -static void read_and_parse(void){ - static time_t stamp; /* after data gets old, load /proc/ksyms again */ - if(time(NULL) != stamp){ - read_file(KSYMS_FILENAME, &ksyms_data, &ksyms_room); - parse_ksyms(); - memset((void*)hashtable,0,sizeof(hashtable)); /* invalidate cache */ - stamp = time(NULL); - } -} - -/*********************************/ - -static void default_message(const char *restrict format, ...) __attribute__((format(printf,1,2))); -static void default_message(const char *restrict format, ...) { - va_list arg; - - va_start (arg, format); - vfprintf (stderr, format, arg); - va_end (arg); -} - -/*********************************/ - -static int use_wchan_file; - -int open_psdb_message(const char *restrict override, message_fn message) { - static const char *sysmap_paths[] = { - "/boot/System.map-%s", - "/boot/System.map", - "/lib/modules/%s/System.map", - "/usr/src/linux/System.map", - "/System.map", - NULL - }; - struct stat sbuf; - struct utsname uts; - char path[128]; - const char **fmt = sysmap_paths; - const char *sm; - -#ifdef SYSMAP_FILENAME /* debug feature */ - override = SYSMAP_FILENAME; -#endif - - // first allow for a user-selected System.map file - if( - (sm=override) - || - (sm=getenv("PS_SYSMAP")) - || - (sm=getenv("PS_SYSTEM_MAP")) - ){ - if(!have_privs){ - read_and_parse(); - if(sysmap_mmap(sm, message)) return 0; - } - /* failure is better than ignoring the user & using bad data */ - return -1; /* ought to return "Namelist not found." */ - } - - // next try the Linux 2.5.xx method - if(!stat("/proc/self/wchan", &sbuf)){ - use_wchan_file = 1; // hack - return 0; - } - - // finally, search for the System.map file - uname(&uts); - path[sizeof path - 1] = '\0'; - do{ - int did_ksyms = 0; - snprintf(path, sizeof path - 1, *fmt, uts.release); - if(!stat(path, &sbuf)){ - if (did_ksyms++) read_and_parse(); - if (sysmap_mmap(path, message)) return 0; - } - }while(*++fmt); - /* TODO: Without System.map, no need to keep ksyms loaded. */ - return -1; -} - -/***************************************/ - -int open_psdb(const char *restrict override) { - return open_psdb_message(override, default_message); -} - -/***************************************/ - -static const char * read_wchan_file(unsigned pid){ - static char buf[64]; - const char *ret = buf; - ssize_t num; - int fd; - - snprintf(buf, sizeof buf, "/proc/%d/wchan", pid); - fd = open(buf, O_RDONLY); - if(fd==-1) return "?"; - num = read(fd, buf, sizeof buf - 1); - close(fd); - if(num<1) return "?"; // allow for "0" - buf[num] = '\0'; - - if(buf[0]=='0' && buf[1]=='\0') return "-"; - - // would skip over numbers if they existed -- but no - - // lame ppc64 has a '.' in front of every name - if(*ret=='.') ret++; - switch(*ret){ - case 's': if(!strncmp(ret, "sys_", 4)) ret += 4; break; - case 'd': if(!strncmp(ret, "do_", 3)) ret += 3; break; - case '_': while(*ret=='_') ret++; break; - } - return ret; -} - -/***************************************/ - -static const symb fail = { .name = "?" }; -static const char dash[] = "-"; -static const char star[] = "*"; - -#define MAX_OFFSET (0x1000*sizeof(long)) /* past this is generally junk */ - -/* return pointer to temporary static buffer with function name */ -const char * lookup_wchan(unsigned KLONG address, unsigned pid) { - const symb *mod_symb; - const symb *map_symb; - const symb *good_symb; - const char *ret; - unsigned hash; - - // can't cache it due to a race condition :-( - if(use_wchan_file) return read_wchan_file(pid); - - if(!address) return dash; - if(!~address) return star; - - read_and_parse(); - hash = (address >> 4) & 0xff; /* got 56/63 hits & 7/63 misses */ - if(hashtable[hash].addr == address) return hashtable[hash].name; - mod_symb = search(address, ksyms_index, ksyms_count); - if(!mod_symb) mod_symb = &fail; - map_symb = search(address, sysmap_index, sysmap_count); - if(!map_symb) map_symb = &fail; - - /* which result is closest? */ - good_symb = (mod_symb->addr > map_symb->addr) - ? mod_symb - : map_symb - ; - if(address > good_symb->addr + MAX_OFFSET) good_symb = &fail; - - /* good_symb->name has the data, but needs to be trimmed */ - ret = good_symb->name; - // lame ppc64 has a '.' in front of every name - if(*ret=='.') ret++; - switch(*ret){ - case 's': if(!strncmp(ret, "sys_", 4)) ret += 4; break; - case 'd': if(!strncmp(ret, "do_", 3)) ret += 3; break; - case '_': while(*ret=='_') ret++; break; - } - /* if(!*ret) ret = fail.name; */ /* not likely (name was "sys_", etc.) */ - - /* cache name after abbreviation */ - hashtable[hash].addr = address; - hashtable[hash].name = ret; - - return ret; -} diff --git a/proc/libprocps.sym b/proc/libprocps.sym index 9f1d61ca..b08d5714 100644 --- a/proc/libprocps.sym +++ b/proc/libprocps.sym @@ -40,8 +40,6 @@ global: look_up_our_self; lookup_wchan; meminfo; - open_psdb; - open_psdb_message; openproc; page_bytes; pretty_print_signals; diff --git a/proc/readproc.h b/proc/readproc.h index 14f2b74d..fb9b703a 100644 --- a/proc/readproc.h +++ b/proc/readproc.h @@ -287,7 +287,6 @@ extern proc_t * get_proc_stats(pid_t pid, proc_t *p); #define PROC_FILLGRP 0x0010 // resolve group id number -> group name #define PROC_FILLSTATUS 0x0020 // read status #define PROC_FILLSTAT 0x0040 // read stat -#define PROC_FILLWCHAN 0x0080 // look up WCHAN name #define PROC_FILLARG 0x0100 // alloc and fill in `cmdline' #define PROC_FILLCGROUP 0x0200 // alloc and fill in `cgroup` #define PROC_FILLSUPGRP 0x0400 // resolve supplementary group id -> group name diff --git a/proc/wchan.c b/proc/wchan.c new file mode 100644 index 00000000..b4e40fe1 --- /dev/null +++ b/proc/wchan.c @@ -0,0 +1,56 @@ +/* + * wchan.c - kernel symbol handling + * Copyright 1998-2003 by Albert Cahalan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +#include "wchan.h" // to verify prototype + + +const char * lookup_wchan (int pid) { + static char buf[64]; + const char *ret = buf; + ssize_t num; + int fd; + + snprintf(buf, sizeof buf, "/proc/%d/wchan", pid); + fd = open(buf, O_RDONLY); + if (fd==-1) return "?"; + + num = read(fd, buf, sizeof buf - 1); + close(fd); + + if (num<1) return "?"; // allow for "0" + buf[num] = '\0'; + + if (buf[0]=='0' && buf[1]=='\0') return "-"; + + // lame ppc64 has a '.' in front of every name + if (*ret=='.') ret++; + switch (*ret){ + case 's': if(!strncmp(ret, "sys_", 4)) ret += 4; break; + case 'd': if(!strncmp(ret, "do_", 3)) ret += 3; break; + case '_': while(*ret=='_') ret++; break; + default : break; + } + return ret; +} diff --git a/proc/wchan.h b/proc/wchan.h index e3d0b310..65f0a3cc 100644 --- a/proc/wchan.h +++ b/proc/wchan.h @@ -5,9 +5,7 @@ EXTERN_C_BEGIN -extern const char * lookup_wchan(unsigned KLONG address, unsigned pid); -extern int open_psdb(const char *__restrict override); -extern int open_psdb_message(const char *__restrict override, message_fn message); +extern const char * lookup_wchan (int pid); EXTERN_C_END