| /* |
| * readprofile.c - used to read /proc/profile |
| * |
| * Copyright (C) 1994,1996 Alessandro Rubini (rubini@ipvvis.unipv.it) |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program 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 General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| */ |
| |
| #include <errno.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| |
| #define RELEASE "2.0, May 1996" |
| |
| #define S_LEN 128 |
| |
| static char *prgname; |
| |
| /* These are the defaults */ |
| static char defaultmap[]="/usr/src/linux/System.map"; |
| static char defaultpro[]="/proc/profile"; |
| static char optstring[]="m:p:itvarV"; |
| |
| void usage() |
| { |
| fprintf(stderr, |
| "%s: Usage: \"%s [options]\n" |
| "\t -m <mapfile> (default = \"%s\")\n" |
| "\t -p <pro-file> (default = \"%s\")\n" |
| "\t -i print only info about the sampling step\n" |
| "\t -v print verbose data\n" |
| "\t -a print all symbols, even if count is 0\n" |
| "\t -r reset all the counters (root only)\n" |
| "\t -V print version and exit\n" |
| ,prgname,prgname,defaultmap,defaultpro); |
| exit(1); |
| } |
| |
| FILE *myopen(char *name, char *mode, int *flag) |
| { |
| static char cmdline[S_LEN]; |
| |
| if (!strcmp(name+strlen(name)-3,".gz")) |
| { |
| *flag=1; |
| sprintf(cmdline,"zcat %s", name); |
| return popen(cmdline,mode); |
| } |
| *flag=0; |
| return fopen(name,mode); |
| } |
| |
| int main (int argc, char **argv) |
| { |
| FILE *pro; |
| FILE *map; |
| int proFd; |
| char *mapFile, *proFile; |
| unsigned int len, add0=0, step, index=0; |
| unsigned int *buf, total, fn_len; |
| unsigned int fn_add, next_add; /* current and next address */ |
| char fn_name[S_LEN], next_name[S_LEN]; /* current and next name */ |
| char mode[8]; |
| int c; |
| int optAll=0, optInfo=0, optReset=0, optVerbose=0; |
| char mapline[S_LEN]; |
| int maplineno=1; |
| int popenMap; /* flag to tell if popen() has been used */ |
| |
| #define next (current^1) |
| |
| prgname=argv[0]; |
| proFile=defaultpro; |
| mapFile=defaultmap; |
| |
| while ((c=getopt(argc,argv,optstring))!=-1) |
| { |
| switch(c) |
| { |
| case 'm': mapFile=optarg; break; |
| case 'p': proFile=optarg; break; |
| case 'a': optAll++; break; |
| case 'i': optInfo++; break; |
| case 'r': optReset++; break; |
| case 'v': optVerbose++; break; |
| case 'V': printf("%s Version %s\n",prgname,RELEASE); exit(0); |
| default: usage(); |
| } |
| } |
| |
| if (optReset) |
| { |
| /* try to become root, just in case */ |
| setuid(0); |
| pro=fopen(defaultpro,"w"); |
| if (!pro) |
| {perror(proFile); exit(1);} |
| fprintf(pro,"anything\n"); |
| fclose(pro); |
| exit(0); |
| } |
| |
| /* |
| * Use an fd for the profiling buffer, to skip stdio overhead |
| */ |
| if ( ((proFd=open(proFile,O_RDONLY)) < 0) |
| || ((len=lseek(proFd,0,SEEK_END)) < 0) |
| || (lseek(proFd,0,SEEK_SET)<0) ) |
| { |
| fprintf(stderr,"%s: %s: %s\n",prgname,proFile,strerror(errno)); |
| exit(1); |
| } |
| |
| if ( !(buf=malloc(len)) ) |
| { fprintf(stderr,"%s: malloc(): %s\n",prgname, strerror(errno)); exit(1); } |
| |
| if (read(proFd,buf,len) != len) |
| { |
| fprintf(stderr,"%s: %s: %s\n",prgname,proFile,strerror(errno)); |
| exit(1); |
| } |
| close(proFd); |
| |
| step=buf[0]; |
| if (optInfo) |
| { |
| printf("Sampling_step: %i\n",step); |
| exit(0); |
| } |
| |
| total=0; |
| |
| if (!(map=myopen(mapFile,"r",&popenMap))) |
| {fprintf(stderr,"%s: ",prgname);perror(mapFile);exit(1);} |
| |
| while(fgets(mapline,S_LEN,map)) |
| { |
| if (sscanf(mapline,"%x %s %s",&fn_add,mode,fn_name)!=3) |
| { |
| fprintf(stderr,"%s: %s(%i): wrong map line\n", |
| prgname,mapFile, maplineno); |
| exit(1); |
| } |
| if (strcmp(fn_name,"_stext")) /* only elf works like this */ |
| { |
| add0=fn_add; |
| break; |
| } |
| } |
| |
| if (!add0) |
| { |
| fprintf(stderr,"%s: can't find \"_stext\" in %s\n",prgname, mapFile); |
| exit(1); |
| } |
| |
| /* |
| * Main loop. |
| */ |
| while(fgets(mapline,S_LEN,map)) |
| { |
| unsigned int this=0; |
| |
| if (sscanf(mapline,"%x %s %s",&next_add,mode,next_name)!=3) |
| { |
| fprintf(stderr,"%s: %s(%i): wrong map line\n", |
| prgname,mapFile, maplineno); |
| exit(1); |
| } |
| if (*mode!='T' && *mode!='t') break; /* only text is profiled */ |
| |
| while (index < (next_add-add0)/step) |
| this += buf[index++]; |
| total += this; |
| |
| fn_len = next_add-fn_add; |
| if (fn_len && (this || optAll)) |
| { |
| if (optVerbose) |
| printf("%08x %-40s %6i %8.4f\n", |
| fn_add,fn_name,this,this/(double)fn_len); |
| else |
| printf("%6i %-40s %8.4f\n", |
| this,fn_name,this/(double)fn_len); |
| } |
| fn_add=next_add; strcpy(fn_name,next_name); |
| } |
| /* trailer */ |
| if (optVerbose) |
| printf("%08x %-40s %6i %8.4f\n", |
| 0,"total",total,total/(double)(fn_add-add0)); |
| else |
| printf("%6i %-40s %8.4f\n", |
| total,"total",total/(double)(fn_add-add0)); |
| |
| popenMap ? pclose(map) : fclose(map); |
| exit(0); |
| } |
| |
| |
| |