2 /*--------------------------------------------------------------------*/
3 /*--- A program that merges multiple cachegrind output files. ---*/
5 /*--------------------------------------------------------------------*/
8 This file is part of Cachegrind, a Valgrind tool for cache
11 Copyright (C) 2002-2013 Nicholas Nethercote
14 AVL tree code derived from
15 ANSI C Library for maintainance of AVL Balanced Trees
16 (C) 2000 Daniel Nagy, Budapest University of Technology and Economics
17 Released under GNU General Public License (GPL) version 2
19 This program is free software; you can redistribute it and/or
20 modify it under the terms of the GNU General Public License as
21 published by the Free Software Foundation; either version 2 of the
22 License, or (at your option) any later version.
24 This program is distributed in the hope that it will be useful, but
25 WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 General Public License for more details.
29 You should have received a copy of the GNU General Public License
30 along with this program; if not, write to the Free Software
31 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
34 The GNU General Public License is contained in the file COPYING.
43 typedef signed long Word
;
44 typedef unsigned long UWord
;
45 typedef unsigned char Bool
;
46 #define True ((Bool)1)
47 #define False ((Bool)0)
48 typedef signed int Int
;
49 typedef unsigned int UInt
;
50 typedef unsigned long long int ULong
;
51 typedef signed char Char
;
55 //------------------------------------------------------------------//
57 //--- Public interface ---//
58 //------------------------------------------------------------------//
60 typedef struct _WordFM WordFM
; /* opaque */
62 /* Initialise a WordFM */
63 void initFM ( WordFM
* t
,
64 void* (*alloc_nofail
)( SizeT
),
65 void (*dealloc
)(void*),
66 Word (*kCmp
)(Word
,Word
) );
68 /* Allocate and initialise a WordFM */
69 WordFM
* newFM( void* (*alloc_nofail
)( SizeT
),
70 void (*dealloc
)(void*),
71 Word (*kCmp
)(Word
,Word
) );
73 /* Free up the FM. If kFin is non-NULL, it is applied to keys
74 before the FM is deleted; ditto with vFin for vals. */
75 void deleteFM ( WordFM
*, void(*kFin
)(Word
), void(*vFin
)(Word
) );
77 /* Add (k,v) to fm. If a binding for k already exists, it is updated
78 to map to this new v. In that case we should really return the
79 previous v so that caller can finalise it. Oh well. */
80 void addToFM ( WordFM
* fm
, Word k
, Word v
);
82 // Delete key from fm, returning associated val if found
83 Bool
delFromFM ( WordFM
* fm
, /*OUT*/Word
* oldV
, Word key
);
85 // Look up in fm, assigning found val at spec'd address
86 Bool
lookupFM ( WordFM
* fm
, /*OUT*/Word
* valP
, Word key
);
88 Word
sizeFM ( WordFM
* fm
);
90 // set up FM for iteration
91 void initIterFM ( WordFM
* fm
);
93 // get next key/val pair. Will assert if fm has been modified
94 // or looked up in since initIterFM was called.
95 Bool
nextIterFM ( WordFM
* fm
, /*OUT*/Word
* pKey
, /*OUT*/Word
* pVal
);
97 // clear the I'm iterating flag
98 void doneIterFM ( WordFM
* fm
);
100 // Deep copy a FM. If dopyK is NULL, keys are copied verbatim.
101 // If non-null, dopyK is applied to each key to generate the
102 // version in the new copy. In that case, if the argument to dopyK
103 // is non-NULL but the result is NULL, it is assumed that dopyK
104 // could not allocate memory, in which case the copy is abandoned
105 // and NULL is returned. Ditto with dopyV for values.
106 WordFM
* dopyFM ( WordFM
* fm
, Word(*dopyK
)(Word
), Word(*dopyV
)(Word
) );
108 //------------------------------------------------------------------//
109 //--- end WordFM ---//
110 //--- Public interface ---//
111 //------------------------------------------------------------------//
114 static const char* argv0
= "cg_merge";
116 /* Keep track of source filename/line no so as to be able to
117 print decent error messages. */
126 static void printSrcLoc ( SOURCE
* s
)
128 fprintf(stderr
, "%s: near %s line %u\n", argv0
, s
->filename
, s
->lno
-1);
131 __attribute__((noreturn
))
132 static void mallocFail ( SOURCE
* s
, const char* who
)
134 fprintf(stderr
, "%s: out of memory in %s\n", argv0
, who
);
139 __attribute__((noreturn
))
140 static void parseError ( SOURCE
* s
, const char* msg
)
142 fprintf(stderr
, "%s: parse error: %s\n", argv0
, msg
);
147 __attribute__((noreturn
))
148 static void barf ( SOURCE
* s
, const char* msg
)
150 fprintf(stderr
, "%s: %s\n", argv0
, msg
);
155 // Read a line. Return the line read, or NULL if at EOF.
156 // The line is allocated dynamically but will be overwritten with
157 // every invocation. Caller must not free it.
158 static const char *readline ( SOURCE
* s
)
160 static char *line
= NULL
;
161 static size_t linesiz
= 0;
168 if (i
+ 1 >= linesiz
) {
170 line
= realloc(line
, linesiz
* sizeof *line
);
172 mallocFail(s
, "readline:");
184 barf(s
, "I/O error while reading input file");
191 return i
== 0 ? NULL
: line
;
194 static Bool
streqn ( const char* s1
, const char* s2
, size_t n
)
196 return 0 == strncmp(s1
, s2
, n
);
199 static Bool
streq ( const char* s1
, const char* s2
)
201 return 0 == strcmp(s1
, s2
);
205 ////////////////////////////////////////////////////////////////
223 // null-terminated vector of desc_lines
233 // Summary line (copied from input)
237 WordFM FileFn* innerMap
238 where innerMap is WordFM line-number=UWord Counts */
241 // Summary counts (computed whilst parsing)
242 // should match .summary_line
247 static FileFn
* new_FileFn ( char* file_name
, char* fn_name
)
249 FileFn
* ffn
= malloc(sizeof(FileFn
));
252 ffn
->fi_name
= file_name
;
253 ffn
->fn_name
= fn_name
;
257 static void ddel_FileFn ( FileFn
* ffn
)
263 memset(ffn
, 0, sizeof(FileFn
));
267 static FileFn
* dopy_FileFn ( FileFn
* ff
)
270 fi2
= strdup(ff
->fi_name
);
271 if (fi2
== NULL
) return NULL
;
272 fn2
= strdup(ff
->fn_name
);
277 return new_FileFn( fi2
, fn2
);
280 static Counts
* new_Counts ( Int n_counts
, /*COPIED*/ULong
* counts
)
283 Counts
* cts
= malloc(sizeof(Counts
));
287 assert(n_counts
>= 0);
288 cts
->counts
= malloc(n_counts
* sizeof(ULong
));
289 if (cts
->counts
== NULL
) {
294 cts
->n_counts
= n_counts
;
295 for (i
= 0; i
< n_counts
; i
++)
296 cts
->counts
[i
] = counts
[i
];
301 static Counts
* new_Counts_Zeroed ( Int n_counts
)
304 Counts
* cts
= malloc(sizeof(Counts
));
308 assert(n_counts
>= 0);
309 cts
->counts
= malloc(n_counts
* sizeof(ULong
));
310 if (cts
->counts
== NULL
) {
315 cts
->n_counts
= n_counts
;
316 for (i
= 0; i
< n_counts
; i
++)
322 static void sdel_Counts ( Counts
* cts
)
324 memset(cts
, 0, sizeof(Counts
));
328 static void ddel_Counts ( Counts
* cts
)
332 memset(cts
, 0, sizeof(Counts
));
336 static Counts
* dopy_Counts ( Counts
* cts
)
338 return new_Counts( cts
->n_counts
, cts
->counts
);
342 CacheProfFile
* new_CacheProfFile ( char** desc_lines
,
350 CacheProfFile
* cpf
= malloc(sizeof(CacheProfFile
));
353 cpf
->desc_lines
= desc_lines
;
354 cpf
->cmd_line
= cmd_line
;
355 cpf
->events_line
= events_line
;
356 cpf
->n_events
= n_events
;
357 cpf
->summary_line
= summary_line
;
358 cpf
->outerMap
= outerMap
;
359 cpf
->summary
= summary
;
363 static WordFM
* dopy_InnerMap ( WordFM
* innerMap
)
365 return dopyFM ( innerMap
, NULL
,
366 (Word(*)(Word
))dopy_Counts
);
369 static void ddel_InnerMap ( WordFM
* innerMap
)
371 deleteFM( innerMap
, NULL
, (void(*)(Word
))ddel_Counts
);
374 static void ddel_CacheProfFile ( CacheProfFile
* cpf
)
377 if (cpf
->desc_lines
) {
378 for (p
= cpf
->desc_lines
; *p
; p
++)
380 free(cpf
->desc_lines
);
384 if (cpf
->events_line
)
385 free(cpf
->events_line
);
386 if (cpf
->summary_line
)
387 free(cpf
->summary_line
);
389 deleteFM( cpf
->outerMap
, (void(*)(Word
))ddel_FileFn
,
390 (void(*)(Word
))ddel_InnerMap
);
392 ddel_Counts(cpf
->summary
);
394 memset(cpf
, 0, sizeof(CacheProfFile
));
398 static void showCounts ( FILE* f
, Counts
* c
)
401 for (i
= 0; i
< c
->n_counts
; i
++) {
402 fprintf(f
, "%lld ", c
->counts
[i
]);
406 static void show_CacheProfFile ( FILE* f
, CacheProfFile
* cpf
)
415 for (d
= cpf
->desc_lines
; *d
; d
++)
416 fprintf(f
, "%s\n", *d
);
417 fprintf(f
, "%s\n", cpf
->cmd_line
);
418 fprintf(f
, "%s\n", cpf
->events_line
);
420 initIterFM( cpf
->outerMap
);
421 while (nextIterFM( cpf
->outerMap
, (Word
*)(&topKey
), (Word
*)(&topVal
) )) {
422 fprintf(f
, "fl=%s\nfn=%s\n",
423 topKey
->fi_name
, topKey
->fn_name
);
424 initIterFM( topVal
);
425 while (nextIterFM( topVal
, (Word
*)(&subKey
), (Word
*)(&subVal
) )) {
426 fprintf(f
, "%ld ", subKey
);
427 showCounts( f
, subVal
);
430 doneIterFM( topVal
);
432 doneIterFM( cpf
->outerMap
);
434 //fprintf(f, "%s\n", cpf->summary_line);
435 fprintf(f
, "summary:");
436 for (i
= 0; i
< cpf
->summary
->n_counts
; i
++)
437 fprintf(f
, " %lld", cpf
->summary
->counts
[i
]);
441 ////////////////////////////////////////////////////////////////
443 static Word
cmp_FileFn ( Word s1
, Word s2
)
445 FileFn
* ff1
= (FileFn
*)s1
;
446 FileFn
* ff2
= (FileFn
*)s2
;
447 Word r
= strcmp(ff1
->fi_name
, ff2
->fi_name
);
449 r
= strcmp(ff1
->fn_name
, ff2
->fn_name
);
453 static Word
cmp_unboxed_UWord ( Word s1
, Word s2
)
455 UWord u1
= (UWord
)s1
;
456 UWord u2
= (UWord
)s2
;
457 if (u1
< u2
) return -1;
458 if (u1
> u2
) return 1;
462 ////////////////////////////////////////////////////////////////
464 static Bool
parse_ULong ( /*OUT*/ULong
* res
, /*INOUT*/const char** pptr
)
467 const char* ptr
= *pptr
;
468 while (isspace(*ptr
)) ptr
++;
469 if (!isdigit(*ptr
)) {
471 return False
; /* end of string, or junk */
474 while (isdigit(*ptr
)) {
475 u64
= (u64
* 10) + (ULong
)(*ptr
- '0');
483 // str is a line of integers, starting with a line number. Parse it,
484 // returning the first number in *lnno and the rest in a newly
485 // allocated Counts struct. If lnno is non-NULL, treat the first
486 // number as a line number and assign it to *lnno instead of
487 // incorporating it in the counts array.
489 Counts
* splitUpCountsLine ( SOURCE
* s
, /*OUT*/UWord
* lnno
, const char* str
)
494 UInt n_tmpC
= 0, tmpCsize
= 0;
496 if (n_tmpC
>= tmpCsize
) {
498 tmpC
= realloc(tmpC
, tmpCsize
* sizeof *tmpC
);
500 mallocFail(s
, "splitUpCountsLine:");
502 ok
= parse_ULong( &tmpC
[n_tmpC
], &str
);
508 parseError(s
, "garbage in counts line");
509 if (lnno
? (n_tmpC
< 2) : (n_tmpC
< 1))
510 parseError(s
, "too few counts in count line");
513 *lnno
= (UWord
)tmpC
[0];
514 counts
= new_Counts( n_tmpC
-1, /*COPIED*/&tmpC
[1] );
516 counts
= new_Counts( n_tmpC
, /*COPIED*/&tmpC
[0] );
523 static void addCounts ( SOURCE
* s
, /*OUT*/Counts
* counts1
, Counts
* counts2
)
526 if (counts1
->n_counts
!= counts2
->n_counts
)
527 parseError(s
, "addCounts: inconsistent number of counts");
528 for (i
= 0; i
< counts1
->n_counts
; i
++)
529 counts1
->counts
[i
] += counts2
->counts
[i
];
532 static Bool
addCountsToMap ( SOURCE
* s
,
534 UWord lnno
, Counts
* newCounts
)
537 // look up lnno in the map. If none present, add a binding
538 // lnno->counts. If present, add counts to the existing entry.
539 if (lookupFM( counts_map
, (Word
*)(&oldCounts
), (Word
)lnno
)) {
540 // merge with existing binding
541 addCounts( s
, oldCounts
, newCounts
);
544 // create new binding
545 addToFM( counts_map
, (Word
)lnno
, (Word
)newCounts
);
551 void handle_counts ( SOURCE
* s
,
553 const char* fi
, const char* fn
, const char* newCountsStr
)
561 if (0) printf("%s %s %s\n", fi
, fn
, newCountsStr
);
564 newCounts
= splitUpCountsLine( s
, &lnno
, newCountsStr
);
566 // Did we get the right number?
567 if (newCounts
->n_counts
!= cpf
->n_events
)
571 topKey
= malloc(sizeof(FileFn
));
573 topKey
->fi_name
= strdup(fi
);
574 topKey
->fn_name
= strdup(fn
);
576 if (! (topKey
&& topKey
->fi_name
&& topKey
->fn_name
))
577 mallocFail(s
, "handle_counts:");
580 if (lookupFM( cpf
->outerMap
, (Word
*)(&countsMap
), (Word
)topKey
)) {
581 // found it. Merge in new counts
582 freeNewCounts
= addCountsToMap( s
, countsMap
, lnno
, newCounts
);
585 // not found in the top map. Create new entry
586 countsMap
= newFM( malloc
, free
, cmp_unboxed_UWord
);
589 addToFM( cpf
->outerMap
, (Word
)topKey
, (Word
)countsMap
);
590 freeNewCounts
= addCountsToMap( s
, countsMap
, lnno
, newCounts
);
593 // also add to running summary total
594 addCounts( s
, cpf
->summary
, newCounts
);
596 // if safe to do so, free up the count vector
598 ddel_Counts(newCounts
);
603 parseError(s
, "# counts doesn't match # events");
607 /* Parse a complete file from the stream in 's'. If a parse error
608 happens, do not return; instead exit via parseError(). If an
609 out-of-memory condition happens, do not return; instead exit via
612 static CacheProfFile
* parse_CacheProfFile ( SOURCE
* s
)
615 char** tmp_desclines
= NULL
;
616 unsigned tmp_desclines_size
= 0;
618 int n_tmp_desclines
= 0;
621 char* curr_fn
= strdup("???");
622 char* curr_fl
= strdup("???");
625 cpf
= new_CacheProfFile( NULL
, NULL
, NULL
, 0, NULL
, NULL
, NULL
);
627 mallocFail(s
, "parse_CacheProfFile(1)");
629 // Parse "desc:" lines
634 if (!streqn(line
, "desc: ", 6))
636 if (n_tmp_desclines
>= tmp_desclines_size
) {
637 tmp_desclines_size
+= 100;
638 tmp_desclines
= realloc(tmp_desclines
,
639 tmp_desclines_size
* sizeof *tmp_desclines
);
640 if (tmp_desclines
== NULL
)
641 mallocFail(s
, "parse_CacheProfFile(1)");
643 tmp_desclines
[n_tmp_desclines
++] = strdup(line
);
646 if (n_tmp_desclines
== 0)
647 parseError(s
, "parse_CacheProfFile: no DESC lines present");
649 cpf
->desc_lines
= malloc( (1+n_tmp_desclines
) * sizeof(char*) );
650 if (cpf
->desc_lines
== NULL
)
651 mallocFail(s
, "parse_CacheProfFile(2)");
653 cpf
->desc_lines
[n_tmp_desclines
] = NULL
;
654 for (i
= 0; i
< n_tmp_desclines
; i
++)
655 cpf
->desc_lines
[i
] = tmp_desclines
[i
];
658 if (!streqn(line
, "cmd: ", 5))
659 parseError(s
, "parse_CacheProfFile: no CMD line present");
661 cpf
->cmd_line
= strdup(line
);
662 if (cpf
->cmd_line
== NULL
)
663 mallocFail(s
, "parse_CacheProfFile(3)");
665 // Parse "events:" line and figure out how many events there are
668 parseError(s
, "parse_CacheProfFile: eof before EVENTS line");
669 if (!streqn(line
, "events: ", 8))
670 parseError(s
, "parse_CacheProfFile: no EVENTS line present");
672 // figure out how many events there are by counting the number
673 // of space-alphanum transitions in the events_line
674 cpf
->events_line
= strdup(line
);
675 if (cpf
->events_line
== NULL
)
676 mallocFail(s
, "parse_CacheProfFile(3)");
679 assert(cpf
->events_line
[6] == ':');
680 for (p
= &cpf
->events_line
[6]; *p
; p
++) {
681 if (p
[0] == ' ' && isalpha(p
[1]))
685 // create the running cross-check summary
686 cpf
->summary
= new_Counts_Zeroed( cpf
->n_events
);
687 if (cpf
->summary
== NULL
)
688 mallocFail(s
, "parse_CacheProfFile(4)");
690 // create the outer map (file+fn name --> inner map)
691 cpf
->outerMap
= newFM ( malloc
, free
, cmp_FileFn
);
692 if (cpf
->outerMap
== NULL
)
693 mallocFail(s
, "parse_CacheProfFile(5)");
695 // process count lines
699 parseError(s
, "parse_CacheProfFile: eof before SUMMARY line");
701 if (isdigit(line
[0])) {
702 handle_counts(s
, cpf
, curr_fl
, curr_fn
, line
);
706 if (streqn(line
, "fn=", 3)) {
708 curr_fn
= strdup(line
+3);
712 if (streqn(line
, "fl=", 3)) {
714 curr_fl
= strdup(line
+3);
718 if (streqn(line
, "summary: ", 9)) {
722 parseError(s
, "parse_CacheProfFile: unexpected line in main data");
725 // finally, the "summary:" line
726 if (!streqn(line
, "summary: ", 9))
727 parseError(s
, "parse_CacheProfFile: missing SUMMARY line");
729 cpf
->summary_line
= strdup(line
);
730 if (cpf
->summary_line
== NULL
)
731 mallocFail(s
, "parse_CacheProfFile(6)");
733 // there should be nothing more
736 parseError(s
, "parse_CacheProfFile: "
737 "extraneous content after SUMMARY line");
739 // check the summary counts are as expected
740 summaryRead
= splitUpCountsLine( s
, NULL
, &cpf
->summary_line
[8] );
741 if (summaryRead
== NULL
)
742 mallocFail(s
, "parse_CacheProfFile(7)");
743 if (summaryRead
->n_counts
!= cpf
->n_events
)
744 parseError(s
, "parse_CacheProfFile: wrong # counts in SUMMARY line");
745 for (i
= 0; i
< summaryRead
->n_counts
; i
++) {
746 if (summaryRead
->counts
[i
] != cpf
->summary
->counts
[i
]) {
747 parseError(s
, "parse_CacheProfFile: "
748 "computed vs stated SUMMARY counts mismatch");
751 free(summaryRead
->counts
);
752 sdel_Counts(summaryRead
);
754 // since the summary counts are OK, free up the summary_line text
755 // which contains the same info.
756 free(cpf
->summary_line
);
757 cpf
->summary_line
= NULL
;
768 static void merge_CacheProfInfo ( SOURCE
* s
,
769 /*MOD*/CacheProfFile
* dst
,
772 /* For each (filefn, innerMap) in src
774 add binding dopy(filefn)->dopy(innerMap) in src
776 // merge src->innerMap with dst->innerMap
777 for each (lineno, counts) in src->innerMap
778 if lineno not in dst->innerMap
779 add binding lineno->dopy(counts) to dst->innerMap
781 add counts into dst->innerMap[lineno]
783 /* Outer iterator: FileFn* -> WordFM* (inner iterator)
784 Inner iterator: UWord -> Counts*
793 /* First check mundane things: that the events: lines are
795 if (!streq( dst
->events_line
, src
->events_line
))
796 barf(s
, "\"events:\" line of most recent file does "
797 "not match those previously processed");
799 initIterFM( src
->outerMap
);
801 // for (filefn, innerMap) in src
802 while (nextIterFM( src
->outerMap
, (Word
*)&soKey
, (Word
*)&soVal
)) {
805 if (! lookupFM( dst
->outerMap
, (Word
*)&doVal
, (Word
)soKey
)) {
807 // no .. add dopy(filefn) -> dopy(innerMap) to src
808 FileFn
* c_soKey
= dopy_FileFn(soKey
);
809 WordFM
* c_soVal
= dopy_InnerMap(soVal
);
810 if ((!c_soKey
) || (!c_soVal
)) goto oom
;
811 addToFM( dst
->outerMap
, (Word
)c_soKey
, (Word
)c_soVal
);
815 // yes .. merge the two innermaps
818 // for (lno, counts) in soVal (source inner map)
819 while (nextIterFM( soVal
, (Word
*)&siKey
, (Word
*)&siVal
)) {
821 // is lno in the corresponding dst inner map?
822 if (! lookupFM( doVal
, (Word
*)&diVal
, siKey
)) {
824 // no .. add lineno->dopy(counts) to dst inner map
825 Counts
* c_siVal
= dopy_Counts( siVal
);
826 if (!c_siVal
) goto oom
;
827 addToFM( doVal
, siKey
, (Word
)c_siVal
);
831 // yes .. merge counts into dst inner map val
832 addCounts( s
, diVal
, siVal
);
841 // add the summaries too
842 addCounts(s
, dst
->summary
, src
->summary
);
847 mallocFail(s
, "merge_CacheProfInfo");
850 static void usage ( void )
852 fprintf(stderr
, "%s: Merges multiple cachegrind output files into one\n",
854 fprintf(stderr
, "%s: usage: %s [-o outfile] [files-to-merge]\n",
859 int main ( int argc
, char** argv
)
863 CacheProfFile
*cpf
, *cpfTmp
;
865 FILE* outfile
= NULL
;
866 char* outfilename
= NULL
;
875 for (i
= 1; i
< argc
; i
++) {
876 if (streq(argv
[i
], "-h") || streq(argv
[i
], "--help"))
880 /* Scan args, looking for '-o outfilename'. */
881 for (i
= 1; i
< argc
; i
++) {
882 if (streq(argv
[i
], "-o")) {
884 outfilename
= argv
[i
+1];
895 for (i
= 1; i
< argc
; i
++) {
897 if (i
== outfileix
) {
898 /* Skip '-o' and whatever follows it */
903 fprintf(stderr
, "%s: parsing %s\n", argv0
, argv
[i
]);
905 src
.filename
= argv
[i
];
906 src
.fp
= fopen(src
.filename
, "r");
909 barf(&src
, "Cannot open input file");
912 cpfTmp
= parse_CacheProfFile( &src
);
915 /* If this isn't the first file, merge */
917 /* this is the first file */
920 /* not the first file; merge */
921 fprintf(stderr
, "%s: merging %s\n", argv0
, argv
[i
]);
922 merge_CacheProfInfo( &src
, cpf
, cpfTmp
);
923 ddel_CacheProfFile( cpfTmp
);
928 /* Now create the output file. */
932 fprintf(stderr
, "%s: writing %s\n",
933 argv0
, outfilename
? outfilename
: "(stdout)" );
935 /* Write the output. */
937 outfile
= fopen(outfilename
, "w");
939 fprintf(stderr
, "%s: can't create output file %s\n",
948 show_CacheProfFile( outfile
, cpf
);
949 if (ferror(outfile
)) {
950 fprintf(stderr
, "%s: error writing output file %s\n",
951 argv0
, outfilename
? outfilename
: "(stdout)" );
953 if (outfile
!= stdout
)
959 if (outfile
!= stdout
)
962 ddel_CacheProfFile( cpf
);
969 //------------------------------------------------------------------//
971 //--- Implementation ---//
972 //------------------------------------------------------------------//
974 /* ------------ Implementation ------------ */
976 /* One element of the AVL tree */
981 struct _AvlNode
* left
;
982 struct _AvlNode
* right
;
994 #define WFM_STKMAX 32 // At most 2**32 entries can be iterated over
998 void* (*alloc_nofail
)( SizeT
);
999 void (*dealloc
)(void*);
1000 Word (*kCmp
)(Word
,Word
);
1001 AvlNode
* nodeStack
[WFM_STKMAX
]; // Iterator node stack
1002 Int numStack
[WFM_STKMAX
]; // Iterator num stack
1003 Int stackTop
; // Iterator stack pointer, one past end
1007 static Bool
avl_removeroot_wrk(AvlNode
** t
, Word(*kCmp
)(Word
,Word
));
1009 /* Swing to the left. Warning: no balance maintainance. */
1010 static void avl_swl ( AvlNode
** root
)
1013 AvlNode
* b
= a
->right
;
1019 /* Swing to the right. Warning: no balance maintainance. */
1020 static void avl_swr ( AvlNode
** root
)
1023 AvlNode
* b
= a
->left
;
1029 /* Balance maintainance after especially nasty swings. */
1030 static void avl_nasty ( AvlNode
* root
)
1032 switch (root
->balance
) {
1034 root
->left
->balance
= 0;
1035 root
->right
->balance
= 1;
1038 root
->left
->balance
= -1;
1039 root
->right
->balance
= 0;
1042 root
->left
->balance
= 0;
1043 root
->right
->balance
= 0;
1051 /* Find size of a non-NULL tree. */
1052 static Word
size_avl_nonNull ( AvlNode
* nd
)
1054 return 1 + (nd
->left
? size_avl_nonNull(nd
->left
) : 0)
1055 + (nd
->right
? size_avl_nonNull(nd
->right
) : 0);
1058 /* Insert element a into the AVL tree t. Returns True if the depth of
1059 the tree has grown. If element with that key is already present,
1060 just copy a->val to existing node, first returning old ->val field
1061 of existing node in *oldV, so that the caller can finalize it
1065 Bool
avl_insert_wrk ( AvlNode
** rootp
,
1066 /*OUT*/MaybeWord
* oldV
,
1068 Word (*kCmp
)(Word
,Word
) )
1078 /* insert into an empty tree? */
1084 cmpres
= kCmp( (*rootp
)->key
, a
->key
);
1087 /* insert into the left subtree */
1088 if ((*rootp
)->left
) {
1089 AvlNode
* left_subtree
= (*rootp
)->left
;
1090 if (avl_insert_wrk(&left_subtree
, oldV
, a
, kCmp
)) {
1091 switch ((*rootp
)->balance
--) {
1092 case 1: return False
;
1093 case 0: return True
;
1097 if ((*rootp
)->left
->balance
< 0) {
1099 (*rootp
)->balance
= 0;
1100 (*rootp
)->right
->balance
= 0;
1102 avl_swl( &((*rootp
)->left
) );
1104 avl_nasty( *rootp
);
1107 (*rootp
)->left
= left_subtree
;
1112 if ((*rootp
)->balance
--)
1116 assert(0);/*NOTREACHED*/
1120 /* insert into the right subtree */
1121 if ((*rootp
)->right
) {
1122 AvlNode
* right_subtree
= (*rootp
)->right
;
1123 if (avl_insert_wrk(&right_subtree
, oldV
, a
, kCmp
)) {
1124 switch((*rootp
)->balance
++){
1125 case -1: return False
;
1126 case 0: return True
;
1130 if ((*rootp
)->right
->balance
> 0) {
1132 (*rootp
)->balance
= 0;
1133 (*rootp
)->left
->balance
= 0;
1135 avl_swr( &((*rootp
)->right
) );
1137 avl_nasty( *rootp
);
1140 (*rootp
)->right
= right_subtree
;
1144 (*rootp
)->right
= a
;
1145 if ((*rootp
)->balance
++)
1149 assert(0);/*NOTREACHED*/
1152 /* cmpres == 0, a duplicate - replace the val, but don't
1153 incorporate the node in the tree */
1155 oldV
->w
= (*rootp
)->val
;
1156 (*rootp
)->val
= a
->val
;
1161 /* Remove an element a from the AVL tree t. a must be part of
1162 the tree. Returns True if the depth of the tree has shrunk.
1165 Bool
avl_remove_wrk ( AvlNode
** rootp
,
1167 Word(*kCmp
)(Word
,Word
) )
1170 Word cmpres
= kCmp( (*rootp
)->key
, a
->key
);
1173 /* remove from the left subtree */
1174 AvlNode
* left_subtree
= (*rootp
)->left
;
1175 assert(left_subtree
);
1176 ch
= avl_remove_wrk(&left_subtree
, a
, kCmp
);
1177 (*rootp
)->left
=left_subtree
;
1179 switch ((*rootp
)->balance
++) {
1180 case -1: return True
;
1181 case 0: return False
;
1185 switch ((*rootp
)->right
->balance
) {
1188 (*rootp
)->balance
= -1;
1189 (*rootp
)->left
->balance
= 1;
1193 (*rootp
)->balance
= 0;
1194 (*rootp
)->left
->balance
= 0;
1201 avl_swr( &((*rootp
)->right
) );
1203 avl_nasty( *rootp
);
1209 /* remove from the right subtree */
1210 AvlNode
* right_subtree
= (*rootp
)->right
;
1211 assert(right_subtree
);
1212 ch
= avl_remove_wrk(&right_subtree
, a
, kCmp
);
1213 (*rootp
)->right
= right_subtree
;
1215 switch ((*rootp
)->balance
--) {
1216 case 1: return True
;
1217 case 0: return False
;
1221 switch ((*rootp
)->left
->balance
) {
1224 (*rootp
)->balance
= 1;
1225 (*rootp
)->right
->balance
= -1;
1229 (*rootp
)->balance
= 0;
1230 (*rootp
)->right
->balance
= 0;
1237 avl_swl( &((*rootp
)->left
) );
1239 avl_nasty( *rootp
);
1244 assert(cmpres
== 0);
1245 assert((*rootp
)==a
);
1246 return avl_removeroot_wrk(rootp
, kCmp
);
1251 /* Remove the root of the AVL tree *rootp.
1252 * Warning: dumps core if *rootp is empty
1255 Bool
avl_removeroot_wrk ( AvlNode
** rootp
,
1256 Word(*kCmp
)(Word
,Word
) )
1260 if (!(*rootp
)->left
) {
1261 if (!(*rootp
)->right
) {
1265 (*rootp
) = (*rootp
)->right
;
1268 if (!(*rootp
)->right
) {
1269 (*rootp
) = (*rootp
)->left
;
1272 if ((*rootp
)->balance
< 0) {
1273 /* remove from the left subtree */
1275 while (a
->right
) a
= a
->right
;
1277 /* remove from the right subtree */
1278 a
= (*rootp
)->right
;
1279 while (a
->left
) a
= a
->left
;
1281 ch
= avl_remove_wrk(rootp
, a
, kCmp
);
1282 a
->left
= (*rootp
)->left
;
1283 a
->right
= (*rootp
)->right
;
1284 a
->balance
= (*rootp
)->balance
;
1286 if(a
->balance
== 0) return ch
;
1291 AvlNode
* avl_find_node ( AvlNode
* t
, Word k
, Word(*kCmp
)(Word
,Word
) )
1295 if (t
== NULL
) return NULL
;
1296 cmpres
= kCmp(t
->key
, k
);
1297 if (cmpres
> 0) t
= t
->left
; else
1298 if (cmpres
< 0) t
= t
->right
; else
1303 // Clear the iterator stack.
1304 static void stackClear(WordFM
* fm
)
1308 for (i
= 0; i
< WFM_STKMAX
; i
++) {
1309 fm
->nodeStack
[i
] = NULL
;
1310 fm
->numStack
[i
] = 0;
1315 // Push onto the iterator stack.
1316 static inline void stackPush(WordFM
* fm
, AvlNode
* n
, Int i
)
1318 assert(fm
->stackTop
< WFM_STKMAX
);
1319 assert(1 <= i
&& i
<= 3);
1320 fm
->nodeStack
[fm
->stackTop
] = n
;
1321 fm
-> numStack
[fm
->stackTop
] = i
;
1325 // Pop from the iterator stack.
1326 static inline Bool
stackPop(WordFM
* fm
, AvlNode
** n
, Int
* i
)
1328 assert(fm
->stackTop
<= WFM_STKMAX
);
1330 if (fm
->stackTop
> 0) {
1332 *n
= fm
->nodeStack
[fm
->stackTop
];
1333 *i
= fm
-> numStack
[fm
->stackTop
];
1334 assert(1 <= *i
&& *i
<= 3);
1335 fm
->nodeStack
[fm
->stackTop
] = NULL
;
1336 fm
-> numStack
[fm
->stackTop
] = 0;
1344 AvlNode
* avl_dopy ( AvlNode
* nd
,
1347 void*(alloc_nofail
)(SizeT
) )
1352 nyu
= alloc_nofail(sizeof(AvlNode
));
1355 nyu
->left
= nd
->left
;
1356 nyu
->right
= nd
->right
;
1357 nyu
->balance
= nd
->balance
;
1361 nyu
->key
= dopyK( nd
->key
);
1362 if (nd
->key
!= 0 && nyu
->key
== 0)
1363 return NULL
; /* oom in key dcopy */
1365 /* copying assumedly unboxed keys */
1371 nyu
->val
= dopyV( nd
->val
);
1372 if (nd
->val
!= 0 && nyu
->val
== 0)
1373 return NULL
; /* oom in val dcopy */
1375 /* copying assumedly unboxed vals */
1381 nyu
->left
= avl_dopy( nyu
->left
, dopyK
, dopyV
, alloc_nofail
);
1386 nyu
->right
= avl_dopy( nyu
->right
, dopyK
, dopyV
, alloc_nofail
);
1394 /* --- Public interface functions --- */
1396 /* Initialise a WordFM. */
1397 void initFM ( WordFM
* fm
,
1398 void* (*alloc_nofail
)( SizeT
),
1399 void (*dealloc
)(void*),
1400 Word (*kCmp
)(Word
,Word
) )
1404 fm
->alloc_nofail
= alloc_nofail
;
1405 fm
->dealloc
= dealloc
;
1409 /* Allocate and Initialise a WordFM. */
1410 WordFM
* newFM( void* (*alloc_nofail
)( SizeT
),
1411 void (*dealloc
)(void*),
1412 Word (*kCmp
)(Word
,Word
) )
1414 WordFM
* fm
= alloc_nofail(sizeof(WordFM
));
1416 initFM(fm
, alloc_nofail
, dealloc
, kCmp
);
1420 static void avl_free ( AvlNode
* nd
,
1423 void(*dealloc
)(void*) )
1428 avl_free(nd
->left
, kFin
, vFin
, dealloc
);
1430 avl_free(nd
->right
, kFin
, vFin
, dealloc
);
1435 memset(nd
, 0, sizeof(AvlNode
));
1439 /* Free up the FM. If kFin is non-NULL, it is applied to keys
1440 before the FM is deleted; ditto with vFin for vals. */
1441 void deleteFM ( WordFM
* fm
, void(*kFin
)(Word
), void(*vFin
)(Word
) )
1443 void(*dealloc
)(void*) = fm
->dealloc
;
1444 avl_free( fm
->root
, kFin
, vFin
, dealloc
);
1445 memset(fm
, 0, sizeof(WordFM
) );
1449 /* Add (k,v) to fm. */
1450 void addToFM ( WordFM
* fm
, Word k
, Word v
)
1454 node
= fm
->alloc_nofail( sizeof(struct _AvlNode
) );
1459 avl_insert_wrk( &fm
->root
, &oldV
, node
, fm
->kCmp
);
1460 //if (oldV.b && fm->vFin)
1461 // fm->vFin( oldV.w );
1466 // Delete key from fm, returning associated val if found
1467 Bool
delFromFM ( WordFM
* fm
, /*OUT*/Word
* oldV
, Word key
)
1469 AvlNode
* node
= avl_find_node( fm
->root
, key
, fm
->kCmp
);
1471 avl_remove_wrk( &fm
->root
, node
, fm
->kCmp
);
1481 // Look up in fm, assigning found val at spec'd address
1482 Bool
lookupFM ( WordFM
* fm
, /*OUT*/Word
* valP
, Word key
)
1484 AvlNode
* node
= avl_find_node( fm
->root
, key
, fm
->kCmp
);
1494 Word
sizeFM ( WordFM
* fm
)
1496 // Hmm, this is a bad way to do this
1497 return fm
->root
? size_avl_nonNull( fm
->root
) : 0;
1500 // set up FM for iteration
1501 void initIterFM ( WordFM
* fm
)
1506 stackPush(fm
, fm
->root
, 1);
1509 // get next key/val pair. Will assert if fm has been modified
1510 // or looked up in since initIterFM was called.
1511 Bool
nextIterFM ( WordFM
* fm
, /*OUT*/Word
* pKey
, /*OUT*/Word
* pVal
)
1518 // This in-order traversal requires each node to be pushed and popped
1519 // three times. These could be avoided by updating nodes in-situ on the
1520 // top of the stack, but the push/pop cost is so small that it's worth
1521 // keeping this loop in this simpler form.
1522 while (stackPop(fm
, &n
, &i
)) {
1525 stackPush(fm
, n
, 2);
1526 if (n
->left
) stackPush(fm
, n
->left
, 1);
1529 stackPush(fm
, n
, 3);
1530 if (pKey
) *pKey
= n
->key
;
1531 if (pVal
) *pVal
= n
->val
;
1534 if (n
->right
) stackPush(fm
, n
->right
, 1);
1541 // Stack empty, iterator is exhausted, return NULL
1545 // clear the I'm iterating flag
1546 void doneIterFM ( WordFM
* fm
)
1550 WordFM
* dopyFM ( WordFM
* fm
, Word(*dopyK
)(Word
), Word(*dopyV
)(Word
) )
1554 /* can't clone the fm whilst iterating on it */
1555 assert(fm
->stackTop
== 0);
1557 nyu
= fm
->alloc_nofail( sizeof(WordFM
) );
1563 memset(fm
->nodeStack
, 0, sizeof(fm
->nodeStack
));
1564 memset(fm
->numStack
, 0, sizeof(fm
->numStack
));
1567 nyu
->root
= avl_dopy( nyu
->root
, dopyK
, dopyV
, fm
->alloc_nofail
);
1575 //------------------------------------------------------------------//
1576 //--- end WordFM ---//
1577 //--- Implementation ---//
1578 //------------------------------------------------------------------//
1580 /*--------------------------------------------------------------------*/
1581 /*--- end cg_merge.c ---*/
1582 /*--------------------------------------------------------------------*/