112892Ssam #ifndef lint 2*18502Smckusick static char *sccsid = "@(#)prof.c 4.4 (Berkeley) 03/24/85"; 312892Ssam #endif 412892Ssam /* 512892Ssam * prof 612892Ssam */ 712892Ssam #include <stdio.h> 812892Ssam #include <sys/types.h> 912892Ssam #include <sys/stat.h> 1012892Ssam #include <a.out.h> 11*18502Smckusick #include <sys/time.h> 1212892Ssam 1312892Ssam typedef short UNIT; /* unit of profiling */ 1413721Ssam #define PCFUDGE 11 1512892Ssam #define A_OUTNAME "a.out" 1612892Ssam #define MON_OUTNAME "mon.out" 1712892Ssam #define MON_SUMNAME "mon.sum" 1812892Ssam 1912892Ssam /* 2012892Ssam * The symbol table; 2112892Ssam * for each external in the specified file we gather 2212892Ssam * its address, the number of calls and compute its share of cpu time. 2312892Ssam */ 2412892Ssam struct nl { 2512892Ssam char *name; 2612892Ssam unsigned value; 2712892Ssam float time; 2812892Ssam long ncall; 2912892Ssam } *nl; 3012892Ssam int nname; 3112892Ssam struct nl *np; 3212892Ssam struct nl *npe; 3312892Ssam 3412892Ssam /* 3512892Ssam * The header on the mon.out file. 3612892Ssam * Mon.out consists of one of these headers, an array of ncount 3712892Ssam * cnt structures (as below) and then an array of samples 3812892Ssam * representing the discretized program counter values. 3912892Ssam */ 4012892Ssam struct hdr { 4112892Ssam UNIT *lowpc, *highpc; 4212892Ssam int ncount; 4312892Ssam } h; 4412892Ssam 4512892Ssam /* 4612892Ssam * Each counter has an address and a number of calls. 4712892Ssam */ 4812892Ssam struct cnt { 4912892Ssam unsigned cvalue; 5012892Ssam long cncall; 5112892Ssam } *cbuf; 5212892Ssam 5312892Ssam /* 5412892Ssam * Each discretized pc sample has 5512892Ssam * a count of the number of samples in its range 5612892Ssam */ 5712892Ssam unsigned UNIT *samples; 5812892Ssam 5912892Ssam FILE *pfile, *nfile; 6012892Ssam 6112892Ssam unsigned lowpc, highpc; /* range profiled */ 6212892Ssam double ransca, ranoff; /* scaling for blowing up plots */ 6312892Ssam unsigned sampbytes; /* number of bytes of samples */ 6412892Ssam int nsamples; /* number of samples */ 6512892Ssam double totime; /* total time for all routines */ 6612892Ssam double maxtime; /* maximum time of any routine (for plot) */ 6712892Ssam double scale; /* scale factor converting samples to pc 6812892Ssam values: each sample covers scale bytes */ 6912892Ssam char *strtab; /* string table in core */ 7012892Ssam off_t ssiz; /* size of the string table */ 7112892Ssam struct exec xbuf; /* exec header of a.out */ 7212892Ssam 7312892Ssam int aflg; 7412892Ssam int nflg; 7512892Ssam int vflg; 7612892Ssam int lflg; 7712892Ssam int zflg; 7812892Ssam int sflag; 7912892Ssam 8012892Ssam char *namfil; 8112892Ssam 8212892Ssam int timcmp(), valcmp(), cntcmp(); 8312892Ssam 8412892Ssam main(argc, argv) 8512892Ssam char **argv; 8612892Ssam { 8712892Ssam int lowpct, highpct; 8812892Ssam 8912892Ssam /* 9012892Ssam * Use highpct and lowpc as percentages, temporarily 9112892Ssam * for graphing options involving blow-up 9212892Ssam */ 9312892Ssam lowpct = -1; 9412892Ssam highpct = -1; 9512892Ssam argv++; 9612892Ssam while ( *argv != 0 && **argv == '-' ) { 9712892Ssam *argv += 1; 9812892Ssam if (**argv == 'l') 9912892Ssam lflg++; 10012892Ssam else if (**argv == 'a') 10112892Ssam aflg++; 10212892Ssam else if (**argv == 'n') 10312892Ssam nflg++; 10412892Ssam else if (**argv == 'z') 10512892Ssam zflg++; 10612892Ssam else if (**argv == 'v') 10712892Ssam vflg++; 10812892Ssam else if ( **argv == 's' ) 10912892Ssam sflag++; 11012892Ssam else if (**argv >= '0' && **argv <= '9') { 11112892Ssam int i = atoi(*argv); 11212892Ssam if (lowpct == -1) 11312892Ssam lowpct = i; 11412892Ssam else 11512892Ssam highpct = i; 11612892Ssam } 11712892Ssam argv++; 11812892Ssam } 11912892Ssam if ( *argv != 0 ) { 12012892Ssam namfil = *argv; 12112892Ssam argv++; 12212892Ssam } else { 12312892Ssam namfil = A_OUTNAME; 12412892Ssam } 12512892Ssam if (lowpct >= 100) 12612892Ssam lowpct = 0; 12712892Ssam if (highpct <= lowpct || highpct > 100) 12812892Ssam highpct = 100; 12912892Ssam ransca = 100./(highpct-lowpct); 13012892Ssam ranoff = 2040. + 40.8*lowpc*ransca; 13112892Ssam /* 13212892Ssam * get information about a.out file. 13312892Ssam */ 13412892Ssam getnfile(); 13512892Ssam /* 13612892Ssam * get information about mon.out file(s). 13712892Ssam */ 13812892Ssam if ( *argv == 0 ) { 13912892Ssam getpfile( MON_OUTNAME ); 14012892Ssam } else { 14112892Ssam do { 14212892Ssam getpfile( *argv ); 14312892Ssam argv++; 14412892Ssam } while ( *argv != 0 ); 14512892Ssam } 14612892Ssam asgnsamples(); /* assign samples to procedures */ 14712892Ssam #ifdef plot 14812892Ssam if (vflg) 14912892Ssam plotprof(); /* a plotted or ... */ 15012892Ssam else 15112892Ssam #endif 15212892Ssam printprof(); /* a printed profile */ 15312892Ssam if ( sflag != 0 ) { 15412892Ssam putprof(); 15512892Ssam } 15612892Ssam done(); 15712892Ssam } 15812892Ssam 15912892Ssam printprof() 16012892Ssam { 161*18502Smckusick double time, actime, hz; 16212892Ssam 16312892Ssam actime = 0; 164*18502Smckusick hz = hertz(); 16512892Ssam printf(" %%time cumsecs #call ms/call name\n"); 16612892Ssam if (!lflg) 16712892Ssam qsort(nl, nname, sizeof(struct nl), timcmp); 16812892Ssam for (np = nl; np<npe-1; np++) { 16912892Ssam if (zflg == 0 && np->time == 0 && np->ncall == 0) 17012892Ssam continue; 17112892Ssam time = np->time/totime; 17212892Ssam actime += np->time; 173*18502Smckusick printf("%6.1f%9.2f", 100*time, actime/hz); 17412892Ssam if (np->ncall != 0) 17512892Ssam printf("%7ld %8.2f", 176*18502Smckusick np->ncall, (np->time*1000/hz)/np->ncall); 17712892Ssam else 17812892Ssam printf("%7.7s %8.8s", "", ""); 17912892Ssam printf(" %s\n", np->name); 18012892Ssam } 18112892Ssam } 18212892Ssam 18312892Ssam /* 18412892Ssam * Set up string and symbol tables from a.out. 18512892Ssam * On return symbol table is sorted by value. 18612892Ssam */ 18712892Ssam getnfile() 18812892Ssam { 18912892Ssam 19012892Ssam nfile = fopen(namfil,"r"); 19112892Ssam if (nfile == NULL) { 19212892Ssam perror(namfil); 19312892Ssam done(); 19412892Ssam } 19512892Ssam fread(&xbuf, 1, sizeof(xbuf), nfile); 19612892Ssam if (N_BADMAG(xbuf)) { 19712892Ssam fprintf(stderr, "%s: bad format\n", namfil); 19812892Ssam done(); 19912892Ssam } 20012892Ssam getstrtab(); 20112892Ssam getsymtab(); 20212892Ssam qsort(nl, nname, sizeof(struct nl), valcmp); 20312892Ssam } 20412892Ssam 20512892Ssam getstrtab() 20612892Ssam { 20712892Ssam 20812892Ssam fseek(nfile, N_SYMOFF(xbuf) + xbuf.a_syms, 0); 20912892Ssam if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) { 21012892Ssam fprintf(stderr, "%s: no string table (old format?)\n", namfil); 21112892Ssam done(); 21212892Ssam } 21312892Ssam strtab = (char *)calloc(ssiz, 1); 21412892Ssam if (strtab == NULL) { 21512892Ssam fprintf(stderr, "%s: no room for %d bytes of string table", 21612892Ssam namfil, ssiz); 21712892Ssam done(); 21812892Ssam } 21912892Ssam if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) { 22012892Ssam fprintf(stderr, "%s: error reading string table\n", namfil); 22112892Ssam done(); 22212892Ssam } 22312892Ssam } 22412892Ssam 22512892Ssam /* 22612892Ssam * Read in symbol table 22712892Ssam */ 22812892Ssam getsymtab() 22912892Ssam { 23012892Ssam register int i; 23112892Ssam 23212892Ssam /* pass1 - count symbols */ 23312892Ssam fseek(nfile, N_SYMOFF(xbuf), 0); 23412892Ssam nname = 0; 23512892Ssam for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) { 23612892Ssam struct nlist nbuf; 23712892Ssam fread(&nbuf, sizeof(nbuf), 1, nfile); 23812892Ssam if (nbuf.n_type!=N_TEXT && nbuf.n_type!=N_TEXT+N_EXT) 23912892Ssam continue; 24012892Ssam if (aflg==0 && nbuf.n_type!=N_TEXT+N_EXT) 24112892Ssam continue; 24212892Ssam nname++; 24312892Ssam } 24412892Ssam if (nname == 0) { 24512892Ssam fprintf(stderr, "%s: no symbols\n", namfil); 24612892Ssam done(); 24712892Ssam } 24812892Ssam nl = (struct nl *)calloc((nname+1), sizeof (struct nl)); 24912892Ssam if (nl == 0) { 25012892Ssam fprintf(stderr, "prof: No room for %d bytes of symbol table\n", 25112892Ssam (nname+1) * sizeof (struct nlist)); 25212892Ssam done(); 25312892Ssam } 25412892Ssam 25512892Ssam /* pass2 - read symbols */ 25612892Ssam fseek(nfile, N_SYMOFF(xbuf), 0); 25712892Ssam npe = nl; 25812892Ssam nname = 0; 25912892Ssam for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) { 26012892Ssam struct nlist nbuf; 26112892Ssam fread(&nbuf, sizeof(nbuf), 1, nfile); 26212892Ssam if (nbuf.n_type!=N_TEXT && nbuf.n_type!=N_TEXT+N_EXT) 26312892Ssam continue; 26412892Ssam if (aflg==0 && nbuf.n_type!=N_TEXT+N_EXT) 26512892Ssam continue; 26612892Ssam npe->value = nbuf.n_value/sizeof(UNIT); 26712892Ssam npe->name = strtab+nbuf.n_un.n_strx; 26812892Ssam npe++; 26912892Ssam nname++; 27012892Ssam } 27112892Ssam npe->value = -1; 27212892Ssam npe++; 27312892Ssam } 27412892Ssam 27512892Ssam /* 27612892Ssam * information from a mon.out file is in two parts: 27712892Ssam * the counters of how many times each procedure was called, 27812892Ssam * if it was called at all; 27912892Ssam * and an array of sampling hits within pc ranges. 28012892Ssam * the counters must be dealt with on a file-by-file basis, 28112892Ssam * since which procedures are represented may vary. 28212892Ssam * the samples ranges are fixed, but must be summed across 28312892Ssam * files, and then distributed among procedures, because 28412892Ssam * of the wierd way the plotting is done. 28512892Ssam */ 28612892Ssam getpfile(filename) 28712892Ssam char *filename; 28812892Ssam { 28912892Ssam 29012892Ssam openpfile(filename); 29112892Ssam readcntrs(); 29212892Ssam asgncntrs(); /* assign counts to procedures */ 29312892Ssam readsamples(); 29412892Ssam closepfile(); 29512892Ssam } 29612892Ssam 29712892Ssam openpfile(filename) 29812892Ssam char *filename; 29912892Ssam { 30012892Ssam struct stat stb; 30112892Ssam 30212892Ssam if((pfile = fopen(filename, "r")) == NULL) { 30312892Ssam perror(filename); 30412892Ssam done(); 30512892Ssam } 30612892Ssam fstat(fileno(pfile), &stb); 30712892Ssam fread(&h, sizeof(struct hdr), 1, pfile); 30812892Ssam lowpc = h.lowpc - (UNIT *)0; 30912892Ssam highpc = h.highpc - (UNIT *)0; 31012892Ssam sampbytes = 31112892Ssam stb.st_size - sizeof(struct hdr) - h.ncount*sizeof(struct cnt); 31212892Ssam nsamples = sampbytes / sizeof (unsigned UNIT); 31312892Ssam } 31412892Ssam 31512892Ssam closepfile() 31612892Ssam { 31712892Ssam 31812892Ssam fclose(pfile); 31912892Ssam free(cbuf); 32012892Ssam } 32112892Ssam 32212892Ssam readcntrs() 32312892Ssam { 32412892Ssam struct cnt *kp; 32512892Ssam 32612892Ssam cbuf = (struct cnt *)calloc((h.ncount+1), sizeof (struct cnt)); 32712892Ssam if (cbuf == 0) { 32812892Ssam fprintf(stderr, "prof: No room for %d bytes of count buffer\n", 32912892Ssam (h.ncount+1) * sizeof (struct cnt)); 33012892Ssam exit(1); 33112892Ssam } 33212892Ssam fread(cbuf, sizeof(struct cnt), h.ncount, pfile); 33312892Ssam /* eliminate zero counters and scale counter pc values */ 33412892Ssam if (h.ncount) { 33512892Ssam kp = &cbuf[h.ncount - 1]; 33612892Ssam for (;;) { 33712892Ssam if (kp->cvalue==0) { 33812892Ssam h.ncount=kp-cbuf; 33912892Ssam ++kp; 34012892Ssam break; 34112892Ssam } 34212892Ssam if (kp == cbuf) { 34312892Ssam h.ncount = 0; 34412892Ssam break; 34512892Ssam } 34612892Ssam --kp; 34712892Ssam } 34812892Ssam for (; --kp>=cbuf; ) 34912892Ssam kp->cvalue /= sizeof(UNIT); 35012892Ssam } 35112892Ssam /* sort counters */ 35212892Ssam qsort(cbuf, h.ncount, sizeof(struct cnt), cntcmp); 35312892Ssam } 35412892Ssam 35512892Ssam /* 35612892Ssam * Assign counters to the procedures to which they belong 35712892Ssam */ 35812892Ssam asgncntrs() 35912892Ssam { 36012892Ssam register int i; 36112892Ssam struct cnt *kp; 36212892Ssam 36312892Ssam kp = &cbuf[h.ncount-1]; 36412892Ssam np = npe; 36512892Ssam while (--np>=nl) { 36612892Ssam if (kp<cbuf || np->value > kp->cvalue) 36712892Ssam continue; 36812892Ssam /* skip ``static'' functions */ 36913721Ssam while (kp >= cbuf && kp->cvalue > np->value + PCFUDGE) 37012892Ssam --kp; 37112892Ssam if (kp->cvalue >= np->value) { 37212892Ssam np->ncall += kp->cncall; 37312892Ssam --kp; 37412892Ssam } 37512892Ssam } 37612892Ssam } 37712892Ssam 37812892Ssam readsamples() 37912892Ssam { 38012892Ssam register i; 38112892Ssam unsigned UNIT sample; 38212892Ssam int totalt; 38312892Ssam 38412892Ssam if (samples == 0) { 38512892Ssam samples = (unsigned UNIT *) 38612892Ssam calloc(sampbytes, sizeof (unsigned UNIT)); 38712892Ssam if (samples == 0) { 38812892Ssam printf("prof: No room for %d sample pc's\n", 38912892Ssam sampbytes / sizeof (unsigned UNIT)); 39012892Ssam done(); 39112892Ssam } 39212892Ssam } 39312892Ssam for (i = 0; ; i++) { 39412892Ssam fread(&sample, sizeof (unsigned UNIT), 1, pfile); 39512892Ssam if (feof(pfile)) 39612892Ssam break; 39712892Ssam samples[i] += sample; 39812892Ssam totalt += sample; 39912892Ssam } 40012892Ssam if (i != nsamples) { 40112892Ssam fprintf(stderr, 40212892Ssam "prof: unexpected EOF after reading %d/%d samples\n", 40312892Ssam --i, nsamples); 40412892Ssam done(); 40512892Ssam } 40612892Ssam } 40712892Ssam 40812892Ssam /* 40912892Ssam * Assign samples to the procedures to which they belong. 41012892Ssam */ 41112892Ssam asgnsamples() 41212892Ssam { 41312892Ssam register j; 41412892Ssam unsigned UNIT ccnt; 41512892Ssam double time; 41612892Ssam unsigned pcl, pch; 41712892Ssam register int i; 41812892Ssam int overlap; 41912892Ssam 42012892Ssam /* read samples and assign to namelist symbols */ 42112892Ssam scale = highpc - lowpc; 42212892Ssam scale /= nsamples; 42312892Ssam for (i=0; i < nsamples; i++) { 42412892Ssam ccnt = samples[i]; 42512892Ssam if (ccnt == 0) 42612892Ssam continue; 42712892Ssam pcl = lowpc + scale*i; 42812892Ssam pch = lowpc + scale*(i+1); 42912892Ssam time = ccnt; 43012892Ssam totime += time; 43112892Ssam if(time > maxtime) 43212892Ssam maxtime = time; 43312892Ssam for (j=0; j<nname; j++) { 43412892Ssam if (pch < nl[j].value) 43512892Ssam break; 43612892Ssam if (pcl >= nl[j+1].value) 43712892Ssam continue; 43812892Ssam overlap=(min(pch,nl[j+1].value)-max(pcl,nl[j].value)); 43912892Ssam if (overlap>0) 44012892Ssam nl[j].time += overlap*time/scale; 44112892Ssam } 44212892Ssam } 44312892Ssam if (totime==0.0) { 44412892Ssam fprintf(stderr, "No time accumulated\n"); 44512892Ssam /* 44612892Ssam done(); 44712892Ssam */ 44812892Ssam totime=1.0; 44912892Ssam } 45012892Ssam } 45112892Ssam 45212892Ssam /* 45312892Ssam * dump what you have out to a mon.out style file. 45412892Ssam */ 45512892Ssam putprof() 45612892Ssam { 45712892Ssam FILE *sfile; 45812892Ssam struct nl *np; 45912892Ssam struct cnt kp; 46012892Ssam int i; 46112892Ssam 46212892Ssam sfile = fopen(MON_SUMNAME, "w"); 46312892Ssam if (sfile == NULL) { 46412892Ssam perror(MON_SUMNAME); 46512892Ssam done(); 46612892Ssam } 46712892Ssam /* 46812892Ssam * build a new header. 46912892Ssam * h.lowpc and h.highpc are already fine. 47012892Ssam * fix h.ncount to count non-zero calls, 47112892Ssam * and the one zero call which marks the end. 47212892Ssam */ 47312892Ssam h.ncount = 0; 47412892Ssam for (np = nl; np < npe-1 ; np++) 47512892Ssam if (np->ncall > 0) 47612892Ssam h.ncount++; 47712892Ssam h.ncount++; 47812892Ssam fwrite(&h, sizeof (struct hdr), 1, sfile); 47912892Ssam for (np = nl; np < npe-1; np++) { 48012892Ssam if (np->ncall > 0) { 48112892Ssam kp.cvalue = np->value * sizeof (unsigned UNIT); 48212892Ssam kp.cncall = np->ncall; 48312892Ssam fwrite(&kp, sizeof (struct cnt), 1, sfile); 48412892Ssam } 48512892Ssam } 48612892Ssam kp.cvalue = 0; 48712892Ssam kp.cncall = 0; 48812892Ssam fwrite(&kp, sizeof (struct cnt), 1, sfile); 48912892Ssam fwrite(samples, sizeof (unsigned UNIT), nsamples, sfile); 49012892Ssam fclose(sfile); 49112892Ssam } 49212892Ssam 493*18502Smckusick /* 494*18502Smckusick * discover the tick frequency of the machine 495*18502Smckusick * if something goes wrong, we return 1. 496*18502Smckusick */ 497*18502Smckusick hertz() 498*18502Smckusick { 499*18502Smckusick struct itimerval tim; 500*18502Smckusick 501*18502Smckusick tim.it_interval.tv_sec = 0; 502*18502Smckusick tim.it_interval.tv_usec = 1; 503*18502Smckusick tim.it_value.tv_sec = 0; 504*18502Smckusick tim.it_value.tv_usec = 0; 505*18502Smckusick setitimer(ITIMER_REAL, &tim, 0); 506*18502Smckusick setitimer(ITIMER_REAL, 0, &tim); 507*18502Smckusick if (tim.it_interval.tv_usec < 1) 508*18502Smckusick return (1); 509*18502Smckusick return (1000000 / tim.it_interval.tv_usec); 510*18502Smckusick } 511*18502Smckusick 51212892Ssam min(a, b) 51312892Ssam { 51412892Ssam if (a<b) 51512892Ssam return(a); 51612892Ssam return(b); 51712892Ssam } 51812892Ssam 51912892Ssam max(a, b) 52012892Ssam { 52112892Ssam if (a>b) 52212892Ssam return(a); 52312892Ssam return(b); 52412892Ssam } 52512892Ssam 52612892Ssam valcmp(p1, p2) 52712892Ssam struct nl *p1, *p2; 52812892Ssam { 52912892Ssam 53012892Ssam return(p1->value - p2->value); 53112892Ssam } 53212892Ssam 53312892Ssam timcmp(p1, p2) 53412892Ssam struct nl *p1, *p2; 53512892Ssam { 53612892Ssam float d; 53712892Ssam 53812892Ssam if (nflg && p2->ncall != p1->ncall) 53912892Ssam return (p2->ncall - p1->ncall); 54012892Ssam d = p2->time - p1->time; 54112892Ssam if (d > 0.0) 54212892Ssam return(1); 54312892Ssam if (d < 0.0) 54412892Ssam return(-1); 54512892Ssam return(strcmp(p1->name,p2->name)); 54612892Ssam } 54712892Ssam 54812892Ssam cntcmp(p1, p2) 54912892Ssam struct cnt *p1, *p2; 55012892Ssam { 55112892Ssam 55212892Ssam return(p1->cvalue - p2->cvalue); 55312892Ssam } 55412892Ssam 55512892Ssam done() 55612892Ssam { 55712892Ssam 55812892Ssam #ifdef plot 55912892Ssam if(vflg) { 56012892Ssam point(0, -2040); 56112892Ssam closepl(); 56212892Ssam } 56312892Ssam #endif 56412892Ssam exit(0); 56512892Ssam } 56612892Ssam 56712892Ssam #ifdef plot 56812892Ssam plotprof() 56912892Ssam { 57012892Ssam double time, lastx, lasty, lastsx; 57112892Ssam register i; 57212892Ssam 57312892Ssam openpl(); 57412892Ssam erase(); 57512892Ssam space(-2048, -2048, 2048, 2048); 57612892Ssam line(-2040, -2040, -2040, 2040); 57712892Ssam line(0, 2040, 0, -2040); 57812892Ssam for(i=0; i<11; i++) 57912892Ssam line(-2040, 2040-i*408, 0, 2040-i*408); 58012892Ssam lastx = 0.; 58112892Ssam lasty = ranoff; 58212892Ssam scale = (4080.*ransca)/(sampbytes/sizeof(UNIT)); 58312892Ssam lastsx = 0.0; 58412892Ssam for(i = 0; i < nsamples; i++) { 58512892Ssam unsigned UNIT ccnt; 58612892Ssam double tx, ty; 58712892Ssam ccnt = samples[i]; 58812892Ssam time = ccnt; 58912892Ssam tx = lastsx; 59012892Ssam ty = lasty; 59112892Ssam lastsx -= 2000.*time/totime; 59212892Ssam lasty -= scale; 59312892Ssam if(lasty >= -2040. && ty <= 2040.) { 59412892Ssam line((int)tx, (int)ty, (int)lastsx, (int)lasty); 59512892Ssam if (ccnt!=0 || lastx!=0.0) { 59612892Ssam tx = lastx; 59712892Ssam lastx = -time*2000./maxtime; 59812892Ssam ty += scale/2; 59912892Ssam line(0, (int)ty, (int)tx, (int)ty); 60012892Ssam } 60112892Ssam } 60212892Ssam } 60312892Ssam scale = (4080.*ransca)/(highpc-lowpc); 60412892Ssam lastx = 50.; 60512892Ssam for(np = nl; np<npe; np++) { 60612892Ssam if(np->value < lowpc) 60712892Ssam continue; 60812892Ssam if(np->value >= highpc) 60912892Ssam continue; 61013174Sroot if(zflg == 0 && np->time == 0 && np->ncall == 0) 61113174Sroot continue; 61212892Ssam time = np->time/totime; 61312892Ssam lasty = ranoff - (np->value - lowpc)*scale; 61412892Ssam if(lasty >= -2040. && lasty <= 2040.) { 61512892Ssam char bufl[BUFSIZ], *namp; 61612892Ssam register j; 61712892Ssam line(0, (int)lasty, 50, (int)lasty); 61812892Ssam line((int)(lastx-50),(int)lasty,(int)lastx,(int)lasty); 61912892Ssam move((int)(lastx+30), (int)(lasty+10)); 62012892Ssam sprintf(bufl, "%s", np->name + (np->name[0] == '_')); 62112892Ssam label(bufl); 62212892Ssam } 62312892Ssam lastx += 500.; 62412892Ssam if(lastx > 2000.) 62512892Ssam lastx = 50.; 62612892Ssam } 62712892Ssam } 62812892Ssam #endif 629