xref: /freebsd/usr.bin/gprof/printgprof.c (revision a03411e84728e9b267056fd31c7d1d9d1dc1b01e)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1983, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #include <err.h>
34 #include <string.h>
35 
36 #include "gprof.h"
37 #include "pathnames.h"
38 
39 int namecmp(const void *, const void *);
40 int timecmp(const void *, const void *);
41 
42 void
43 printprof(void)
44 {
45     register nltype	*np;
46     nltype		**sortednlp;
47     int			idx;
48 
49     actime = 0.0;
50     printf( "\f\n" );
51     flatprofheader();
52 	/*
53 	 *	Sort the symbol table in by time
54 	 */
55     sortednlp = (nltype **) calloc( nname , sizeof(nltype *) );
56     if ( sortednlp == (nltype **) 0 )
57 	errx( 1 , "[printprof] ran out of memory for time sorting" );
58     for ( idx = 0 ; idx < nname ; idx += 1 ) {
59 	sortednlp[ idx ] = &nl[ idx ];
60     }
61     qsort( sortednlp , nname , sizeof(nltype *) , timecmp );
62     for ( idx = 0 ; idx < nname ; idx += 1 ) {
63 	np = sortednlp[ idx ];
64 	flatprofline( np );
65     }
66     actime = 0.0;
67     free( sortednlp );
68 }
69 
70 int
71 timecmp(const void *v1, const void *v2)
72 {
73     const nltype **npp1 = (const nltype **)v1;
74     const nltype **npp2 = (const nltype **)v2;
75     double	timediff;
76     long	calldiff;
77 
78     timediff = (*npp2) -> time - (*npp1) -> time;
79     if ( timediff > 0.0 )
80 	return 1 ;
81     if ( timediff < 0.0 )
82 	return -1;
83     calldiff = (*npp2) -> ncall - (*npp1) -> ncall;
84     if ( calldiff > 0 )
85 	return 1;
86     if ( calldiff < 0 )
87 	return -1;
88     return( strcmp( (*npp1) -> name , (*npp2) -> name ) );
89 }
90 
91     /*
92      *	header for flatprofline
93      */
94 void
95 flatprofheader(void)
96 {
97 
98     if ( bflag ) {
99 	printblurb( _PATH_FLAT_BLURB );
100     }
101     printf( "\ngranularity: each sample hit covers %g byte(s)" ,
102 	    scale * HISTORICAL_SCALE_2 );
103     if ( totime > 0.0 ) {
104 	printf( " for %.2f%% of %.2f seconds\n\n" ,
105 		100.0/totime , totime / hz );
106     } else {
107 	printf( " no time accumulated\n\n" );
108 	    /*
109 	     *	this doesn't hurt since all the numerators will be zero.
110 	     */
111 	totime = 1.0;
112     }
113     printf( "%5.5s %10.10s %8.8s %8.8s %8.8s %8.8s  %-8.8s\n" ,
114 	    "%  " , "cumulative" , "self  " , "" , "self  " , "total " , "" );
115     printf( "%5.5s %10.10s %8.8s %8.8s %8.8s %8.8s  %-8.8s\n" ,
116 	    "time" , "seconds " , "seconds" , "calls" ,
117 	    hz >= 10000000 ? "ns/call" : hz >= 10000 ? "us/call" : "ms/call" ,
118 	    hz >= 10000000 ? "ns/call" : hz >= 10000 ? "us/call" : "ms/call" ,
119 	    "name" );
120 }
121 
122 void
123 flatprofline(register nltype *np)
124 {
125 
126     if ( zflag == 0 && np -> ncall == 0 && np -> time == 0 &&
127 	 np -> childtime == 0 ) {
128 	return;
129     }
130     actime += np -> time;
131     if (hz >= 10000)
132 	printf( "%5.1f %10.3f %8.3f" ,
133 	    100 * np -> time / totime , actime / hz , np -> time / hz );
134     else
135 	printf( "%5.1f %10.2f %8.2f" ,
136 	    100 * np -> time / totime , actime / hz , np -> time / hz );
137     if ( np -> ncall != 0 ) {
138 	if (hz >= 10000000)
139 	    printf( " %8ld %8.0f %8.0f  " , np -> ncall ,
140 		1e9 * np -> time / hz / np -> ncall ,
141 		1e9 * ( np -> time + np -> childtime ) / hz / np -> ncall );
142 	else if (hz >= 10000)
143 	    printf( " %8ld %8.0f %8.0f  " , np -> ncall ,
144 		1e6 * np -> time / hz / np -> ncall ,
145 		1e6 * ( np -> time + np -> childtime ) / hz / np -> ncall );
146 	else
147 	    printf( " %8ld %8.2f %8.2f  " , np -> ncall ,
148 		1000 * np -> time / hz / np -> ncall ,
149 		1000 * ( np -> time + np -> childtime ) / hz / np -> ncall );
150     } else if ( np -> time != 0 || np -> childtime != 0 ) {
151 	printf( " %8ld %7.2f%% %8.8s  " , np -> ncall ,
152 	    100 * np -> time / ( np -> time + np -> childtime ) , "" );
153     } else {
154 	printf( " %8.8s %8.8s %8.8s  " , "" , "" , "" );
155     }
156     printname( np );
157     printf( "\n" );
158 }
159 
160 void
161 gprofheader(void)
162 {
163 
164     if ( bflag ) {
165 	printblurb( _PATH_CALLG_BLURB );
166     }
167     printf( "\ngranularity: each sample hit covers %g byte(s)" ,
168 	    scale * HISTORICAL_SCALE_2 );
169     if ( printtime > 0.0 ) {
170 	printf( " for %.2f%% of %.2f seconds\n\n" ,
171 		100.0/printtime , printtime / hz );
172     } else {
173 	printf( " no time propagated\n\n" );
174 	    /*
175 	     *	this doesn't hurt, since all the numerators will be 0.0
176 	     */
177 	printtime = 1.0;
178     }
179     printf( "%6.6s %5.5s %7.7s %11.11s %7.7s/%-7.7s     %-8.8s\n" ,
180 	"" , "" , "" , "" , "called" , "total" , "parents");
181     printf( "%-6.6s %5.5s %7.7s %11.11s %7.7s+%-7.7s %-8.8s\t%5.5s\n" ,
182 	"index" , "%time" , "self" , "descendents" ,
183 	"called" , "self" , "name" , "index" );
184     printf( "%6.6s %5.5s %7.7s %11.11s %7.7s/%-7.7s     %-8.8s\n" ,
185 	"" , "" , "" , "" , "called" , "total" , "children");
186     printf( "\n" );
187 }
188 
189 void
190 gprofline(register nltype *np)
191 {
192     char	kirkbuffer[ BUFSIZ ];
193 
194     sprintf( kirkbuffer , "[%d]" , np -> index );
195     printf( "%-6.6s %5.1f %7.2f %11.2f" ,
196 	    kirkbuffer ,
197 	    100 * ( np -> propself + np -> propchild ) / printtime ,
198 	    np -> propself / hz ,
199 	    np -> propchild / hz );
200     if ( ( np -> ncall + np -> selfcalls ) != 0 ) {
201 	printf( " %7ld" , np -> npropcall );
202 	if ( np -> selfcalls != 0 ) {
203 	    printf( "+%-7ld " , np -> selfcalls );
204 	} else {
205 	    printf( " %7.7s " , "" );
206 	}
207     } else {
208 	printf( " %7.7s %7.7s " , "" , "" );
209     }
210     printname( np );
211     printf( "\n" );
212 }
213 
214 void
215 printgprof(nltype **timesortnlp)
216 {
217     int		idx;
218     nltype	*parentp;
219 
220 	/*
221 	 *	Print out the structured profiling list
222 	 */
223     gprofheader();
224     for ( idx = 0 ; idx < nname + ncycle ; idx ++ ) {
225 	parentp = timesortnlp[ idx ];
226 	if ( zflag == 0 &&
227 	     parentp -> ncall == 0 &&
228 	     parentp -> selfcalls == 0 &&
229 	     parentp -> propself == 0 &&
230 	     parentp -> propchild == 0 ) {
231 	    continue;
232 	}
233 	if ( ! parentp -> printflag ) {
234 	    continue;
235 	}
236 	if ( parentp -> name == 0 && parentp -> cycleno != 0 ) {
237 		/*
238 		 *	cycle header
239 		 */
240 	    printcycle( parentp );
241 	    printmembers( parentp );
242 	} else {
243 	    printparents( parentp );
244 	    gprofline( parentp );
245 	    printchildren( parentp );
246 	}
247 	printf( "\n" );
248 	printf( "-----------------------------------------------\n" );
249 	printf( "\n" );
250     }
251     free( timesortnlp );
252 }
253 
254     /*
255      *	sort by decreasing propagated time
256      *	if times are equal, but one is a cycle header,
257      *		say that's first (e.g. less, i.e. -1).
258      *	if one's name doesn't have an underscore and the other does,
259      *		say the one is first.
260      *	all else being equal, sort by names.
261      */
262 int
263 totalcmp(const void *v1, const void *v2)
264 {
265     const nltype **npp1 = (const nltype **)v1;
266     const nltype **npp2 = (const nltype **)v2;
267     register const nltype *np1 = *npp1;
268     register const nltype *np2 = *npp2;
269     double		diff;
270 
271     diff =    ( np1 -> propself + np1 -> propchild )
272 	    - ( np2 -> propself + np2 -> propchild );
273     if ( diff < 0.0 )
274 	    return 1;
275     if ( diff > 0.0 )
276 	    return -1;
277     if ( np1 -> name == 0 && np1 -> cycleno != 0 )
278 	return -1;
279     if ( np2 -> name == 0 && np2 -> cycleno != 0 )
280 	return 1;
281     if ( np1 -> name == 0 )
282 	return -1;
283     if ( np2 -> name == 0 )
284 	return 1;
285     if ( *(np1 -> name) != '_' && *(np2 -> name) == '_' )
286 	return -1;
287     if ( *(np1 -> name) == '_' && *(np2 -> name) != '_' )
288 	return 1;
289     if ( np1 -> ncall > np2 -> ncall )
290 	return -1;
291     if ( np1 -> ncall < np2 -> ncall )
292 	return 1;
293     return strcmp( np1 -> name , np2 -> name );
294 }
295 
296 void
297 printparents(nltype *childp)
298 {
299     nltype	*parentp;
300     arctype	*arcp;
301     nltype	*cycleheadp;
302 
303     if ( childp -> cyclehead != 0 ) {
304 	cycleheadp = childp -> cyclehead;
305     } else {
306 	cycleheadp = childp;
307     }
308     if ( childp -> parents == 0 ) {
309 	printf( "%6.6s %5.5s %7.7s %11.11s %7.7s %7.7s     <spontaneous>\n" ,
310 		"" , "" , "" , "" , "" , "" );
311 	return;
312     }
313     sortparents( childp );
314     for ( arcp = childp -> parents ; arcp ; arcp = arcp -> arc_parentlist ) {
315 	parentp = arcp -> arc_parentp;
316 	if ( childp == parentp || ( arcp -> arc_flags & DEADARC ) ||
317 	     ( childp->cycleno != 0 && parentp->cycleno == childp->cycleno ) ) {
318 		/*
319 		 *	selfcall or call among siblings
320 		 */
321 	    printf( "%6.6s %5.5s %7.7s %11.11s %7ld %7.7s     " ,
322 		    "" , "" , "" , "" ,
323 		    arcp -> arc_count , "" );
324 	    printname( parentp );
325 	    printf( "\n" );
326 	} else {
327 		/*
328 		 *	regular parent of child
329 		 */
330 	    printf( "%6.6s %5.5s %7.2f %11.2f %7ld/%-7ld     " ,
331 		    "" , "" ,
332 		    arcp -> arc_time / hz , arcp -> arc_childtime / hz ,
333 		    arcp -> arc_count , cycleheadp -> npropcall );
334 	    printname( parentp );
335 	    printf( "\n" );
336 	}
337     }
338 }
339 
340 void
341 printchildren(nltype *parentp)
342 {
343     nltype	*childp;
344     arctype	*arcp;
345 
346     sortchildren( parentp );
347     arcp = parentp -> children;
348     for ( arcp = parentp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
349 	childp = arcp -> arc_childp;
350 	if ( childp == parentp || ( arcp -> arc_flags & DEADARC ) ||
351 	    ( childp->cycleno != 0 && childp->cycleno == parentp->cycleno ) ) {
352 		/*
353 		 *	self call or call to sibling
354 		 */
355 	    printf( "%6.6s %5.5s %7.7s %11.11s %7ld %7.7s     " ,
356 		    "" , "" , "" , "" , arcp -> arc_count , "" );
357 	    printname( childp );
358 	    printf( "\n" );
359 	} else {
360 		/*
361 		 *	regular child of parent
362 		 */
363 	    printf( "%6.6s %5.5s %7.2f %11.2f %7ld/%-7ld     " ,
364 		    "" , "" ,
365 		    arcp -> arc_time / hz , arcp -> arc_childtime / hz ,
366 		    arcp -> arc_count , childp -> cyclehead -> npropcall );
367 	    printname( childp );
368 	    printf( "\n" );
369 	}
370     }
371 }
372 
373 void
374 printname(nltype *selfp)
375 {
376 
377     if ( selfp -> name != 0 ) {
378 	printf( "%s" , selfp -> name );
379 #	ifdef DEBUG
380 	    if ( debug & DFNDEBUG ) {
381 		printf( "{%d} " , selfp -> toporder );
382 	    }
383 	    if ( debug & PROPDEBUG ) {
384 		printf( "%5.2f%% " , selfp -> propfraction );
385 	    }
386 #	endif /* DEBUG */
387     }
388     if ( selfp -> cycleno != 0 ) {
389 	printf( " <cycle %d>" , selfp -> cycleno );
390     }
391     if ( selfp -> index != 0 ) {
392 	if ( selfp -> printflag ) {
393 	    printf( " [%d]" , selfp -> index );
394 	} else {
395 	    printf( " (%d)" , selfp -> index );
396 	}
397     }
398 }
399 
400 void
401 sortchildren(nltype *parentp)
402 {
403     arctype	*arcp;
404     arctype	*detachedp;
405     arctype	sorted;
406     arctype	*prevp;
407 
408 	/*
409 	 *	unlink children from parent,
410 	 *	then insertion sort back on to sorted's children.
411 	 *	    *arcp	the arc you have detached and are inserting.
412 	 *	    *detachedp	the rest of the arcs to be sorted.
413 	 *	    sorted	arc list onto which you insertion sort.
414 	 *	    *prevp	arc before the arc you are comparing.
415 	 */
416     sorted.arc_childlist = 0;
417     for (  (arcp = parentp -> children)&&(detachedp = arcp -> arc_childlist);
418 	    arcp ;
419 	   (arcp = detachedp)&&(detachedp = detachedp -> arc_childlist)) {
420 	    /*
421 	     *	consider *arcp as disconnected
422 	     *	insert it into sorted
423 	     */
424 	for (   prevp = &sorted ;
425 		prevp -> arc_childlist ;
426 		prevp = prevp -> arc_childlist ) {
427 	    if ( arccmp( arcp , prevp -> arc_childlist ) != LESSTHAN ) {
428 		break;
429 	    }
430 	}
431 	arcp -> arc_childlist = prevp -> arc_childlist;
432 	prevp -> arc_childlist = arcp;
433     }
434 	/*
435 	 *	reattach sorted children to parent
436 	 */
437     parentp -> children = sorted.arc_childlist;
438 }
439 
440 void
441 sortparents(nltype *childp)
442 {
443     arctype	*arcp;
444     arctype	*detachedp;
445     arctype	sorted;
446     arctype	*prevp;
447 
448 	/*
449 	 *	unlink parents from child,
450 	 *	then insertion sort back on to sorted's parents.
451 	 *	    *arcp	the arc you have detached and are inserting.
452 	 *	    *detachedp	the rest of the arcs to be sorted.
453 	 *	    sorted	arc list onto which you insertion sort.
454 	 *	    *prevp	arc before the arc you are comparing.
455 	 */
456     sorted.arc_parentlist = 0;
457     for (  (arcp = childp -> parents)&&(detachedp = arcp -> arc_parentlist);
458 	    arcp ;
459 	   (arcp = detachedp)&&(detachedp = detachedp -> arc_parentlist)) {
460 	    /*
461 	     *	consider *arcp as disconnected
462 	     *	insert it into sorted
463 	     */
464 	for (   prevp = &sorted ;
465 		prevp -> arc_parentlist ;
466 		prevp = prevp -> arc_parentlist ) {
467 	    if ( arccmp( arcp , prevp -> arc_parentlist ) != GREATERTHAN ) {
468 		break;
469 	    }
470 	}
471 	arcp -> arc_parentlist = prevp -> arc_parentlist;
472 	prevp -> arc_parentlist = arcp;
473     }
474 	/*
475 	 *	reattach sorted arcs to child
476 	 */
477     childp -> parents = sorted.arc_parentlist;
478 }
479 
480     /*
481      *	print a cycle header
482      */
483 void
484 printcycle(nltype *cyclep)
485 {
486     char	kirkbuffer[ BUFSIZ ];
487 
488     sprintf( kirkbuffer , "[%d]" , cyclep -> index );
489     printf( "%-6.6s %5.1f %7.2f %11.2f %7ld" ,
490 	    kirkbuffer ,
491 	    100 * ( cyclep -> propself + cyclep -> propchild ) / printtime ,
492 	    cyclep -> propself / hz ,
493 	    cyclep -> propchild / hz ,
494 	    cyclep -> npropcall );
495     if ( cyclep -> selfcalls != 0 ) {
496 	printf( "+%-7ld" , cyclep -> selfcalls );
497     } else {
498 	printf( " %7.7s" , "" );
499     }
500     printf( " <cycle %d as a whole>\t[%d]\n" ,
501 	    cyclep -> cycleno , cyclep -> index );
502 }
503 
504     /*
505      *	print the members of a cycle
506      */
507 void
508 printmembers(nltype *cyclep)
509 {
510     nltype	*memberp;
511 
512     sortmembers( cyclep );
513     for ( memberp = cyclep -> cnext ; memberp ; memberp = memberp -> cnext ) {
514 	printf( "%6.6s %5.5s %7.2f %11.2f %7ld" ,
515 		"" , "" , memberp -> propself / hz , memberp -> propchild / hz ,
516 		memberp -> npropcall );
517 	if ( memberp -> selfcalls != 0 ) {
518 	    printf( "+%-7ld" , memberp -> selfcalls );
519 	} else {
520 	    printf( " %7.7s" , "" );
521 	}
522 	printf( "     " );
523 	printname( memberp );
524 	printf( "\n" );
525     }
526 }
527 
528     /*
529      *	sort members of a cycle
530      */
531 void
532 sortmembers(nltype *cyclep)
533 {
534     nltype	*todo;
535     nltype	*doing;
536     nltype	*prev;
537 
538 	/*
539 	 *	detach cycle members from cyclehead,
540 	 *	and insertion sort them back on.
541 	 */
542     todo = cyclep -> cnext;
543     cyclep -> cnext = 0;
544     for (  (doing = todo)&&(todo = doing -> cnext);
545 	    doing ;
546 	   (doing = todo )&&(todo = doing -> cnext )){
547 	for ( prev = cyclep ; prev -> cnext ; prev = prev -> cnext ) {
548 	    if ( membercmp( doing , prev -> cnext ) == GREATERTHAN ) {
549 		break;
550 	    }
551 	}
552 	doing -> cnext = prev -> cnext;
553 	prev -> cnext = doing;
554     }
555 }
556 
557     /*
558      *	major sort is on propself + propchild,
559      *	next is sort on ncalls + selfcalls.
560      */
561 int
562 membercmp(nltype *this, nltype *that)
563 {
564     double	thistime = this -> propself + this -> propchild;
565     double	thattime = that -> propself + that -> propchild;
566     long	thiscalls = this -> ncall + this -> selfcalls;
567     long	thatcalls = that -> ncall + that -> selfcalls;
568 
569     if ( thistime > thattime ) {
570 	return GREATERTHAN;
571     }
572     if ( thistime < thattime ) {
573 	return LESSTHAN;
574     }
575     if ( thiscalls > thatcalls ) {
576 	return GREATERTHAN;
577     }
578     if ( thiscalls < thatcalls ) {
579 	return LESSTHAN;
580     }
581     return EQUALTO;
582 }
583     /*
584      *	compare two arcs to/from the same child/parent.
585      *	- if one arc is a self arc, it's least.
586      *	- if one arc is within a cycle, it's less than.
587      *	- if both arcs are within a cycle, compare arc counts.
588      *	- if neither arc is within a cycle, compare with
589      *		arc_time + arc_childtime as major key
590      *		arc count as minor key
591      */
592 int
593 arccmp(arctype *thisp, arctype *thatp)
594 {
595     nltype	*thisparentp = thisp -> arc_parentp;
596     nltype	*thischildp = thisp -> arc_childp;
597     nltype	*thatparentp = thatp -> arc_parentp;
598     nltype	*thatchildp = thatp -> arc_childp;
599     double	thistime;
600     double	thattime;
601 
602 #   ifdef DEBUG
603 	if ( debug & TIMEDEBUG ) {
604 	    printf( "[arccmp] " );
605 	    printname( thisparentp );
606 	    printf( " calls " );
607 	    printname ( thischildp );
608 	    printf( " %f + %f %ld/%ld\n" ,
609 		    thisp -> arc_time , thisp -> arc_childtime ,
610 		    thisp -> arc_count , thischildp -> ncall );
611 	    printf( "[arccmp] " );
612 	    printname( thatparentp );
613 	    printf( " calls " );
614 	    printname( thatchildp );
615 	    printf( " %f + %f %ld/%ld\n" ,
616 		    thatp -> arc_time , thatp -> arc_childtime ,
617 		    thatp -> arc_count , thatchildp -> ncall );
618 	    printf( "\n" );
619 	}
620 #   endif /* DEBUG */
621     if ( thisparentp == thischildp ) {
622 	    /* this is a self call */
623 	return LESSTHAN;
624     }
625     if ( thatparentp == thatchildp ) {
626 	    /* that is a self call */
627 	return GREATERTHAN;
628     }
629     if ( thisparentp -> cycleno != 0 && thischildp -> cycleno != 0 &&
630 	thisparentp -> cycleno == thischildp -> cycleno ) {
631 	    /* this is a call within a cycle */
632 	if ( thatparentp -> cycleno != 0 && thatchildp -> cycleno != 0 &&
633 	    thatparentp -> cycleno == thatchildp -> cycleno ) {
634 		/* that is a call within the cycle, too */
635 	    if ( thisp -> arc_count < thatp -> arc_count ) {
636 		return LESSTHAN;
637 	    }
638 	    if ( thisp -> arc_count > thatp -> arc_count ) {
639 		return GREATERTHAN;
640 	    }
641 	    return EQUALTO;
642 	} else {
643 		/* that isn't a call within the cycle */
644 	    return LESSTHAN;
645 	}
646     } else {
647 	    /* this isn't a call within a cycle */
648 	if ( thatparentp -> cycleno != 0 && thatchildp -> cycleno != 0 &&
649 	    thatparentp -> cycleno == thatchildp -> cycleno ) {
650 		/* that is a call within a cycle */
651 	    return GREATERTHAN;
652 	} else {
653 		/* neither is a call within a cycle */
654 	    thistime = thisp -> arc_time + thisp -> arc_childtime;
655 	    thattime = thatp -> arc_time + thatp -> arc_childtime;
656 	    if ( thistime < thattime )
657 		return LESSTHAN;
658 	    if ( thistime > thattime )
659 		return GREATERTHAN;
660 	    if ( thisp -> arc_count < thatp -> arc_count )
661 		return LESSTHAN;
662 	    if ( thisp -> arc_count > thatp -> arc_count )
663 		return GREATERTHAN;
664 	    return EQUALTO;
665 	}
666     }
667 }
668 
669 void
670 printblurb(const char *blurbname)
671 {
672     FILE	*blurbfile;
673     int		input;
674 
675     blurbfile = fopen( blurbname , "r" );
676     if ( blurbfile == NULL ) {
677 	warn( "%s" , blurbname );
678 	return;
679     }
680     while ( ( input = getc( blurbfile ) ) != EOF ) {
681 	putchar( input );
682     }
683     fclose( blurbfile );
684 }
685 
686 int
687 namecmp(const void *v1, const void *v2)
688 {
689     const nltype **npp1 = (const nltype **)v1;
690     const nltype **npp2 = (const nltype **)v2;
691 
692     return( strcmp( (*npp1) -> name , (*npp2) -> name ) );
693 }
694 
695 void
696 printindex(void)
697 {
698     nltype		**namesortnlp;
699     register nltype	*nlp;
700     int			idx, nnames, todo, i, j;
701     char		peterbuffer[ BUFSIZ ];
702 
703 	/*
704 	 *	Now, sort regular function name alphabetically
705 	 *	to create an index.
706 	 */
707     namesortnlp = (nltype **) calloc( nname + ncycle , sizeof(nltype *) );
708     if ( namesortnlp == (nltype **) 0 )
709 	errx( 1 , "ran out of memory for sorting");
710     for ( idx = 0 , nnames = 0 ; idx < nname ; idx++ ) {
711 	if ( zflag == 0 && nl[idx].ncall == 0 && nl[idx].time == 0 )
712 		continue;
713 	namesortnlp[nnames++] = &nl[idx];
714     }
715     qsort( namesortnlp , nnames , sizeof(nltype *) , namecmp );
716     for ( idx = 1 , todo = nnames ; idx <= ncycle ; idx++ ) {
717 	namesortnlp[todo++] = &cyclenl[idx];
718     }
719     printf( "\f\nIndex by function name\n\n" );
720     idx = ( todo + 2 ) / 3;
721     for ( i = 0; i < idx ; i++ ) {
722 	for ( j = i; j < todo ; j += idx ) {
723 	    nlp = namesortnlp[ j ];
724 	    if ( nlp -> printflag ) {
725 		sprintf( peterbuffer , "[%d]" , nlp -> index );
726 	    } else {
727 		sprintf( peterbuffer , "(%d)" , nlp -> index );
728 	    }
729 	    if ( j < nnames ) {
730 		printf( "%6.6s %-19.19s" , peterbuffer , nlp -> name );
731 	    } else {
732 		printf( "%6.6s " , peterbuffer );
733 		sprintf( peterbuffer , "<cycle %d>" , nlp -> cycleno );
734 		printf( "%-19.19s" , peterbuffer );
735 	    }
736 	}
737 	printf( "\n" );
738     }
739     free( namesortnlp );
740 }
741