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-2017 Nicholas Nethercote
14 AVL tree code derived from
15 ANSI C Library for maintenance 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, see <http://www.gnu.org/licenses/>.
32 The GNU General Public License is contained in the file COPYING.
41 typedef signed long Word
;
42 typedef unsigned long UWord
;
43 typedef unsigned char Bool
;
44 #define True ((Bool)1)
45 #define False ((Bool)0)
46 typedef signed int Int
;
47 typedef unsigned int UInt
;
48 typedef unsigned long long int ULong
;
49 typedef signed char Char
;
53 //------------------------------------------------------------------//
55 //--- Public interface ---//
56 //------------------------------------------------------------------//
58 typedef struct _WordFM WordFM
; /* opaque */
60 /* Initialise a WordFM */
61 void initFM ( WordFM
* t
,
62 void* (*alloc_nofail
)( SizeT
),
63 void (*dealloc
)(void*),
64 Word (*kCmp
)(Word
,Word
) );
66 /* Allocate and initialise a WordFM */
67 WordFM
* newFM( void* (*alloc_nofail
)( SizeT
),
68 void (*dealloc
)(void*),
69 Word (*kCmp
)(Word
,Word
) );
71 /* Free up the FM. If kFin is non-NULL, it is applied to keys
72 before the FM is deleted; ditto with vFin for vals. */
73 void deleteFM ( WordFM
*, void(*kFin
)(Word
), void(*vFin
)(Word
) );
75 /* Add (k,v) to fm. If a binding for k already exists, it is updated
76 to map to this new v. In that case we should really return the
77 previous v so that caller can finalise it. Oh well. */
78 void addToFM ( WordFM
* fm
, Word k
, Word v
);
80 // Delete key from fm, returning associated val if found
81 Bool
delFromFM ( WordFM
* fm
, /*OUT*/Word
* oldV
, Word key
);
83 // Look up in fm, assigning found val at spec'd address
84 Bool
lookupFM ( WordFM
* fm
, /*OUT*/Word
* valP
, Word key
);
86 Word
sizeFM ( WordFM
* fm
);
88 // set up FM for iteration
89 void initIterFM ( WordFM
* fm
);
91 // get next key/val pair. Will assert if fm has been modified
92 // or looked up in since initIterFM was called.
93 Bool
nextIterFM ( WordFM
* fm
, /*OUT*/Word
* pKey
, /*OUT*/Word
* pVal
);
95 // clear the I'm iterating flag
96 void doneIterFM ( WordFM
* fm
);
98 // Deep copy a FM. If dopyK is NULL, keys are copied verbatim.
99 // If non-null, dopyK is applied to each key to generate the
100 // version in the new copy. In that case, if the argument to dopyK
101 // is non-NULL but the result is NULL, it is assumed that dopyK
102 // could not allocate memory, in which case the copy is abandoned
103 // and NULL is returned. Ditto with dopyV for values.
104 WordFM
* dopyFM ( WordFM
* fm
, Word(*dopyK
)(Word
), Word(*dopyV
)(Word
) );
106 //------------------------------------------------------------------//
107 //--- end WordFM ---//
108 //--- Public interface ---//
109 //------------------------------------------------------------------//
112 static const char* argv0
= "cg_merge";
114 /* Keep track of source filename/line no so as to be able to
115 print decent error messages. */
124 static void printSrcLoc ( SOURCE
* s
)
126 fprintf(stderr
, "%s: near %s line %u\n", argv0
, s
->filename
, s
->lno
-1);
129 __attribute__((noreturn
))
130 static void mallocFail ( SOURCE
* s
, const char* who
)
132 fprintf(stderr
, "%s: out of memory in %s\n", argv0
, who
);
137 __attribute__((noreturn
))
138 static void parseError ( SOURCE
* s
, const char* msg
)
140 fprintf(stderr
, "%s: parse error: %s\n", argv0
, msg
);
145 __attribute__((noreturn
))
146 static void barf ( SOURCE
* s
, const char* msg
)
148 fprintf(stderr
, "%s: %s\n", argv0
, msg
);
153 // Read a line. Return the line read, or NULL if at EOF.
154 // The line is allocated dynamically but will be overwritten with
155 // every invocation. Caller must not free it.
156 static const char *readline ( SOURCE
* s
)
158 static char *line
= NULL
;
159 static size_t linesiz
= 0;
166 if (i
+ 1 >= linesiz
) {
168 line
= realloc(line
, linesiz
* sizeof *line
);
170 mallocFail(s
, "readline:");
182 barf(s
, "I/O error while reading input file");
189 return i
== 0 ? NULL
: line
;
192 static Bool
streqn ( const char* s1
, const char* s2
, size_t n
)
194 return 0 == strncmp(s1
, s2
, n
);
197 static Bool
streq ( const char* s1
, const char* s2
)
199 return 0 == strcmp(s1
, s2
);
203 ////////////////////////////////////////////////////////////////
221 // null-terminated vector of desc_lines
231 // Summary line (copied from input)
235 WordFM FileFn* innerMap
236 where innerMap is WordFM line-number=UWord Counts */
239 // Summary counts (computed whilst parsing)
240 // should match .summary_line
245 static FileFn
* new_FileFn ( char* file_name
, char* fn_name
)
247 FileFn
* ffn
= malloc(sizeof(FileFn
));
250 ffn
->fi_name
= file_name
;
251 ffn
->fn_name
= fn_name
;
255 static void ddel_FileFn ( FileFn
* ffn
)
261 memset(ffn
, 0, sizeof(FileFn
));
265 static FileFn
* dopy_FileFn ( FileFn
* ff
)
268 fi2
= strdup(ff
->fi_name
);
269 if (fi2
== NULL
) return NULL
;
270 fn2
= strdup(ff
->fn_name
);
275 return new_FileFn( fi2
, fn2
);
278 static Counts
* new_Counts ( Int n_counts
, /*COPIED*/ULong
* counts
)
281 Counts
* cts
= malloc(sizeof(Counts
));
285 assert(n_counts
>= 0);
286 cts
->counts
= malloc(n_counts
* sizeof(ULong
));
287 if (cts
->counts
== NULL
) {
292 cts
->n_counts
= n_counts
;
293 for (i
= 0; i
< n_counts
; i
++)
294 cts
->counts
[i
] = counts
[i
];
299 static Counts
* new_Counts_Zeroed ( Int n_counts
)
302 Counts
* cts
= malloc(sizeof(Counts
));
306 assert(n_counts
>= 0);
307 cts
->counts
= malloc(n_counts
* sizeof(ULong
));
308 if (cts
->counts
== NULL
) {
313 cts
->n_counts
= n_counts
;
314 for (i
= 0; i
< n_counts
; i
++)
320 static void sdel_Counts ( Counts
* cts
)
322 memset(cts
, 0, sizeof(Counts
));
326 static void ddel_Counts ( Counts
* cts
)
330 memset(cts
, 0, sizeof(Counts
));
334 static Counts
* dopy_Counts ( Counts
* cts
)
336 return new_Counts( cts
->n_counts
, cts
->counts
);
340 CacheProfFile
* new_CacheProfFile ( char** desc_lines
,
348 CacheProfFile
* cpf
= malloc(sizeof(CacheProfFile
));
351 cpf
->desc_lines
= desc_lines
;
352 cpf
->cmd_line
= cmd_line
;
353 cpf
->events_line
= events_line
;
354 cpf
->n_events
= n_events
;
355 cpf
->summary_line
= summary_line
;
356 cpf
->outerMap
= outerMap
;
357 cpf
->summary
= summary
;
361 static WordFM
* dopy_InnerMap ( WordFM
* innerMap
)
363 return dopyFM ( innerMap
, NULL
,
364 (Word(*)(Word
))dopy_Counts
);
367 static void ddel_InnerMap ( WordFM
* innerMap
)
369 deleteFM( innerMap
, NULL
, (void(*)(Word
))ddel_Counts
);
372 static void ddel_CacheProfFile ( CacheProfFile
* cpf
)
375 if (cpf
->desc_lines
) {
376 for (p
= cpf
->desc_lines
; *p
; p
++)
378 free(cpf
->desc_lines
);
382 if (cpf
->events_line
)
383 free(cpf
->events_line
);
384 if (cpf
->summary_line
)
385 free(cpf
->summary_line
);
387 deleteFM( cpf
->outerMap
, (void(*)(Word
))ddel_FileFn
,
388 (void(*)(Word
))ddel_InnerMap
);
390 ddel_Counts(cpf
->summary
);
392 memset(cpf
, 0, sizeof(CacheProfFile
));
396 static void showCounts ( FILE* f
, Counts
* c
)
399 for (i
= 0; i
< c
->n_counts
; i
++) {
400 fprintf(f
, "%lld ", c
->counts
[i
]);
404 static void show_CacheProfFile ( FILE* f
, CacheProfFile
* cpf
)
413 for (d
= cpf
->desc_lines
; *d
; d
++)
414 fprintf(f
, "%s\n", *d
);
415 fprintf(f
, "%s\n", cpf
->cmd_line
);
416 fprintf(f
, "%s\n", cpf
->events_line
);
418 initIterFM( cpf
->outerMap
);
419 while (nextIterFM( cpf
->outerMap
, (Word
*)(&topKey
), (Word
*)(&topVal
) )) {
420 fprintf(f
, "fl=%s\nfn=%s\n",
421 topKey
->fi_name
, topKey
->fn_name
);
422 initIterFM( topVal
);
423 while (nextIterFM( topVal
, (Word
*)(&subKey
), (Word
*)(&subVal
) )) {
424 fprintf(f
, "%ld ", subKey
);
425 showCounts( f
, subVal
);
428 doneIterFM( topVal
);
430 doneIterFM( cpf
->outerMap
);
432 //fprintf(f, "%s\n", cpf->summary_line);
433 fprintf(f
, "summary:");
434 for (i
= 0; i
< cpf
->summary
->n_counts
; i
++)
435 fprintf(f
, " %lld", cpf
->summary
->counts
[i
]);
439 ////////////////////////////////////////////////////////////////
441 static Word
cmp_FileFn ( Word s1
, Word s2
)
443 FileFn
* ff1
= (FileFn
*)s1
;
444 FileFn
* ff2
= (FileFn
*)s2
;
445 Word r
= strcmp(ff1
->fi_name
, ff2
->fi_name
);
447 r
= strcmp(ff1
->fn_name
, ff2
->fn_name
);
451 static Word
cmp_unboxed_UWord ( Word s1
, Word s2
)
453 UWord u1
= (UWord
)s1
;
454 UWord u2
= (UWord
)s2
;
455 if (u1
< u2
) return -1;
456 if (u1
> u2
) return 1;
460 ////////////////////////////////////////////////////////////////
462 static Bool
parse_ULong ( /*OUT*/ULong
* res
, /*INOUT*/const char** pptr
)
465 const char* ptr
= *pptr
;
466 while (isspace(*ptr
)) ptr
++;
467 if (!isdigit(*ptr
)) {
469 return False
; /* end of string, or junk */
472 while (isdigit(*ptr
)) {
473 u64
= (u64
* 10) + (ULong
)(*ptr
- '0');
481 // str is a line of integers, starting with a line number. Parse it,
482 // returning the first number in *lnno and the rest in a newly
483 // allocated Counts struct. If lnno is non-NULL, treat the first
484 // number as a line number and assign it to *lnno instead of
485 // incorporating it in the counts array.
487 Counts
* splitUpCountsLine ( SOURCE
* s
, /*OUT*/UWord
* lnno
, const char* str
)
492 UInt n_tmpC
= 0, tmpCsize
= 0;
494 if (n_tmpC
>= tmpCsize
) {
496 tmpC
= realloc(tmpC
, tmpCsize
* sizeof *tmpC
);
498 mallocFail(s
, "splitUpCountsLine:");
500 ok
= parse_ULong( &tmpC
[n_tmpC
], &str
);
506 parseError(s
, "garbage in counts line");
507 if (lnno
? (n_tmpC
< 2) : (n_tmpC
< 1))
508 parseError(s
, "too few counts in count line");
511 *lnno
= (UWord
)tmpC
[0];
512 counts
= new_Counts( n_tmpC
-1, /*COPIED*/&tmpC
[1] );
514 counts
= new_Counts( n_tmpC
, /*COPIED*/&tmpC
[0] );
521 static void addCounts ( SOURCE
* s
, /*OUT*/Counts
* counts1
, Counts
* counts2
)
524 if (counts1
->n_counts
!= counts2
->n_counts
)
525 parseError(s
, "addCounts: inconsistent number of counts");
526 for (i
= 0; i
< counts1
->n_counts
; i
++)
527 counts1
->counts
[i
] += counts2
->counts
[i
];
530 static Bool
addCountsToMap ( SOURCE
* s
,
532 UWord lnno
, Counts
* newCounts
)
535 // look up lnno in the map. If none present, add a binding
536 // lnno->counts. If present, add counts to the existing entry.
537 if (lookupFM( counts_map
, (Word
*)(&oldCounts
), (Word
)lnno
)) {
538 // merge with existing binding
539 addCounts( s
, oldCounts
, newCounts
);
542 // create new binding
543 addToFM( counts_map
, (Word
)lnno
, (Word
)newCounts
);
549 void handle_counts ( SOURCE
* s
,
551 const char* fi
, const char* fn
, const char* newCountsStr
)
559 if (0) printf("%s %s %s\n", fi
, fn
, newCountsStr
);
562 newCounts
= splitUpCountsLine( s
, &lnno
, newCountsStr
);
564 // Did we get the right number?
565 if (newCounts
->n_counts
!= cpf
->n_events
)
569 topKey
= malloc(sizeof(FileFn
));
571 topKey
->fi_name
= strdup(fi
);
572 topKey
->fn_name
= strdup(fn
);
574 if (! (topKey
&& topKey
->fi_name
&& topKey
->fn_name
))
575 mallocFail(s
, "handle_counts:");
578 if (lookupFM( cpf
->outerMap
, (Word
*)(&countsMap
), (Word
)topKey
)) {
579 // found it. Merge in new counts
580 freeNewCounts
= addCountsToMap( s
, countsMap
, lnno
, newCounts
);
583 // not found in the top map. Create new entry
584 countsMap
= newFM( malloc
, free
, cmp_unboxed_UWord
);
587 addToFM( cpf
->outerMap
, (Word
)topKey
, (Word
)countsMap
);
588 freeNewCounts
= addCountsToMap( s
, countsMap
, lnno
, newCounts
);
591 // also add to running summary total
592 addCounts( s
, cpf
->summary
, newCounts
);
594 // if safe to do so, free up the count vector
596 ddel_Counts(newCounts
);
601 parseError(s
, "# counts doesn't match # events");
605 /* Parse a complete file from the stream in 's'. If a parse error
606 happens, do not return; instead exit via parseError(). If an
607 out-of-memory condition happens, do not return; instead exit via
610 static CacheProfFile
* parse_CacheProfFile ( SOURCE
* s
)
613 char** tmp_desclines
= NULL
;
614 unsigned tmp_desclines_size
= 0;
616 int n_tmp_desclines
= 0;
619 char* curr_fn
= strdup("???");
620 char* curr_fl
= strdup("???");
623 cpf
= new_CacheProfFile( NULL
, NULL
, NULL
, 0, NULL
, NULL
, NULL
);
625 mallocFail(s
, "parse_CacheProfFile(1)");
627 // Parse "desc:" lines
632 if (!streqn(line
, "desc: ", 6))
634 if (n_tmp_desclines
>= tmp_desclines_size
) {
635 tmp_desclines_size
+= 100;
636 tmp_desclines
= realloc(tmp_desclines
,
637 tmp_desclines_size
* sizeof *tmp_desclines
);
638 if (tmp_desclines
== NULL
)
639 mallocFail(s
, "parse_CacheProfFile(1)");
641 tmp_desclines
[n_tmp_desclines
++] = strdup(line
);
644 if (n_tmp_desclines
== 0)
645 parseError(s
, "parse_CacheProfFile: no DESC lines present");
647 cpf
->desc_lines
= malloc( (1+n_tmp_desclines
) * sizeof(char*) );
648 if (cpf
->desc_lines
== NULL
)
649 mallocFail(s
, "parse_CacheProfFile(2)");
651 cpf
->desc_lines
[n_tmp_desclines
] = NULL
;
652 for (i
= 0; i
< n_tmp_desclines
; i
++)
653 cpf
->desc_lines
[i
] = tmp_desclines
[i
];
656 if (!streqn(line
, "cmd: ", 5))
657 parseError(s
, "parse_CacheProfFile: no CMD line present");
659 cpf
->cmd_line
= strdup(line
);
660 if (cpf
->cmd_line
== NULL
)
661 mallocFail(s
, "parse_CacheProfFile(3)");
663 // Parse "events:" line and figure out how many events there are
666 parseError(s
, "parse_CacheProfFile: eof before EVENTS line");
667 if (!streqn(line
, "events: ", 8))
668 parseError(s
, "parse_CacheProfFile: no EVENTS line present");
670 // figure out how many events there are by counting the number
671 // of space-alphanum transitions in the events_line
672 cpf
->events_line
= strdup(line
);
673 if (cpf
->events_line
== NULL
)
674 mallocFail(s
, "parse_CacheProfFile(3)");
677 assert(cpf
->events_line
[6] == ':');
678 for (p
= &cpf
->events_line
[6]; *p
; p
++) {
679 if (p
[0] == ' ' && isalpha(p
[1]))
683 // create the running cross-check summary
684 cpf
->summary
= new_Counts_Zeroed( cpf
->n_events
);
685 if (cpf
->summary
== NULL
)
686 mallocFail(s
, "parse_CacheProfFile(4)");
688 // create the outer map (file+fn name --> inner map)
689 cpf
->outerMap
= newFM ( malloc
, free
, cmp_FileFn
);
690 if (cpf
->outerMap
== NULL
)
691 mallocFail(s
, "parse_CacheProfFile(5)");
693 // process count lines
697 parseError(s
, "parse_CacheProfFile: eof before SUMMARY line");
699 if (isdigit(line
[0])) {
700 handle_counts(s
, cpf
, curr_fl
, curr_fn
, line
);
704 if (streqn(line
, "fn=", 3)) {
706 curr_fn
= strdup(line
+3);
710 if (streqn(line
, "fl=", 3)) {
712 curr_fl
= strdup(line
+3);
716 if (streqn(line
, "summary: ", 9)) {
720 parseError(s
, "parse_CacheProfFile: unexpected line in main data");
723 // finally, the "summary:" line
724 if (!streqn(line
, "summary: ", 9))
725 parseError(s
, "parse_CacheProfFile: missing SUMMARY line");
727 cpf
->summary_line
= strdup(line
);
728 if (cpf
->summary_line
== NULL
)
729 mallocFail(s
, "parse_CacheProfFile(6)");
731 // there should be nothing more
734 parseError(s
, "parse_CacheProfFile: "
735 "extraneous content after SUMMARY line");
737 // check the summary counts are as expected
738 summaryRead
= splitUpCountsLine( s
, NULL
, &cpf
->summary_line
[8] );
739 if (summaryRead
== NULL
)
740 mallocFail(s
, "parse_CacheProfFile(7)");
741 if (summaryRead
->n_counts
!= cpf
->n_events
)
742 parseError(s
, "parse_CacheProfFile: wrong # counts in SUMMARY line");
743 for (i
= 0; i
< summaryRead
->n_counts
; i
++) {
744 if (summaryRead
->counts
[i
] != cpf
->summary
->counts
[i
]) {
745 parseError(s
, "parse_CacheProfFile: "
746 "computed vs stated SUMMARY counts mismatch");
749 free(summaryRead
->counts
);
750 sdel_Counts(summaryRead
);
752 // since the summary counts are OK, free up the summary_line text
753 // which contains the same info.
754 free(cpf
->summary_line
);
755 cpf
->summary_line
= NULL
;
766 static void merge_CacheProfInfo ( SOURCE
* s
,
767 /*MOD*/CacheProfFile
* dst
,
770 /* For each (filefn, innerMap) in src
772 add binding dopy(filefn)->dopy(innerMap) in src
774 // merge src->innerMap with dst->innerMap
775 for each (lineno, counts) in src->innerMap
776 if lineno not in dst->innerMap
777 add binding lineno->dopy(counts) to dst->innerMap
779 add counts into dst->innerMap[lineno]
781 /* Outer iterator: FileFn* -> WordFM* (inner iterator)
782 Inner iterator: UWord -> Counts*
791 /* First check mundane things: that the events: lines are
793 if (!streq( dst
->events_line
, src
->events_line
))
794 barf(s
, "\"events:\" line of most recent file does "
795 "not match those previously processed");
797 initIterFM( src
->outerMap
);
799 // for (filefn, innerMap) in src
800 while (nextIterFM( src
->outerMap
, (Word
*)&soKey
, (Word
*)&soVal
)) {
803 if (! lookupFM( dst
->outerMap
, (Word
*)&doVal
, (Word
)soKey
)) {
805 // no .. add dopy(filefn) -> dopy(innerMap) to src
806 FileFn
* c_soKey
= dopy_FileFn(soKey
);
807 WordFM
* c_soVal
= dopy_InnerMap(soVal
);
808 if ((!c_soKey
) || (!c_soVal
)) goto oom
;
809 addToFM( dst
->outerMap
, (Word
)c_soKey
, (Word
)c_soVal
);
813 // yes .. merge the two innermaps
816 // for (lno, counts) in soVal (source inner map)
817 while (nextIterFM( soVal
, (Word
*)&siKey
, (Word
*)&siVal
)) {
819 // is lno in the corresponding dst inner map?
820 if (! lookupFM( doVal
, (Word
*)&diVal
, siKey
)) {
822 // no .. add lineno->dopy(counts) to dst inner map
823 Counts
* c_siVal
= dopy_Counts( siVal
);
824 if (!c_siVal
) goto oom
;
825 addToFM( doVal
, siKey
, (Word
)c_siVal
);
829 // yes .. merge counts into dst inner map val
830 addCounts( s
, diVal
, siVal
);
839 // add the summaries too
840 addCounts(s
, dst
->summary
, src
->summary
);
845 mallocFail(s
, "merge_CacheProfInfo");
848 static void usage ( void )
850 fprintf(stderr
, "%s: Merges multiple cachegrind output files into one\n",
852 fprintf(stderr
, "%s: usage: %s [-o outfile] [files-to-merge]\n",
857 int main ( int argc
, char** argv
)
861 CacheProfFile
*cpf
, *cpfTmp
;
863 FILE* outfile
= NULL
;
864 char* outfilename
= NULL
;
873 for (i
= 1; i
< argc
; i
++) {
874 if (streq(argv
[i
], "-h") || streq(argv
[i
], "--help"))
878 /* Scan args, looking for '-o outfilename'. */
879 for (i
= 1; i
< argc
; i
++) {
880 if (streq(argv
[i
], "-o")) {
882 outfilename
= argv
[i
+1];
893 for (i
= 1; i
< argc
; i
++) {
895 if (i
== outfileix
) {
896 /* Skip '-o' and whatever follows it */
901 fprintf(stderr
, "%s: parsing %s\n", argv0
, argv
[i
]);
903 src
.filename
= argv
[i
];
904 src
.fp
= fopen(src
.filename
, "r");
907 barf(&src
, "Cannot open input file");
910 cpfTmp
= parse_CacheProfFile( &src
);
913 /* If this isn't the first file, merge */
915 /* this is the first file */
918 /* not the first file; merge */
919 fprintf(stderr
, "%s: merging %s\n", argv0
, argv
[i
]);
920 merge_CacheProfInfo( &src
, cpf
, cpfTmp
);
921 ddel_CacheProfFile( cpfTmp
);
926 /* Now create the output file. */
930 fprintf(stderr
, "%s: writing %s\n",
931 argv0
, outfilename
? outfilename
: "(stdout)" );
933 /* Write the output. */
935 outfile
= fopen(outfilename
, "w");
937 fprintf(stderr
, "%s: can't create output file %s\n",
946 show_CacheProfFile( outfile
, cpf
);
947 if (ferror(outfile
)) {
948 fprintf(stderr
, "%s: error writing output file %s\n",
949 argv0
, outfilename
? outfilename
: "(stdout)" );
951 if (outfile
!= stdout
)
957 if (outfile
!= stdout
)
960 ddel_CacheProfFile( cpf
);
967 //------------------------------------------------------------------//
969 //--- Implementation ---//
970 //------------------------------------------------------------------//
972 /* ------------ Implementation ------------ */
974 /* One element of the AVL tree */
979 struct _AvlNode
* left
;
980 struct _AvlNode
* right
;
992 #define WFM_STKMAX 32 // At most 2**32 entries can be iterated over
996 void* (*alloc_nofail
)( SizeT
);
997 void (*dealloc
)(void*);
998 Word (*kCmp
)(Word
,Word
);
999 AvlNode
* nodeStack
[WFM_STKMAX
]; // Iterator node stack
1000 Int numStack
[WFM_STKMAX
]; // Iterator num stack
1001 Int stackTop
; // Iterator stack pointer, one past end
1005 static Bool
avl_removeroot_wrk(AvlNode
** t
, Word(*kCmp
)(Word
,Word
));
1007 /* Swing to the left. Warning: no balance maintenance. */
1008 static void avl_swl ( AvlNode
** root
)
1011 AvlNode
* b
= a
->right
;
1017 /* Swing to the right. Warning: no balance maintenance. */
1018 static void avl_swr ( AvlNode
** root
)
1021 AvlNode
* b
= a
->left
;
1027 /* Balance maintenance after especially nasty swings. */
1028 static void avl_nasty ( AvlNode
* root
)
1030 switch (root
->balance
) {
1032 root
->left
->balance
= 0;
1033 root
->right
->balance
= 1;
1036 root
->left
->balance
= -1;
1037 root
->right
->balance
= 0;
1040 root
->left
->balance
= 0;
1041 root
->right
->balance
= 0;
1049 /* Find size of a non-NULL tree. */
1050 static Word
size_avl_nonNull ( AvlNode
* nd
)
1052 return 1 + (nd
->left
? size_avl_nonNull(nd
->left
) : 0)
1053 + (nd
->right
? size_avl_nonNull(nd
->right
) : 0);
1056 /* Insert element a into the AVL tree t. Returns True if the depth of
1057 the tree has grown. If element with that key is already present,
1058 just copy a->val to existing node, first returning old ->val field
1059 of existing node in *oldV, so that the caller can finalize it
1063 Bool
avl_insert_wrk ( AvlNode
** rootp
,
1064 /*OUT*/MaybeWord
* oldV
,
1066 Word (*kCmp
)(Word
,Word
) )
1076 /* insert into an empty tree? */
1082 cmpres
= kCmp( (*rootp
)->key
, a
->key
);
1085 /* insert into the left subtree */
1086 if ((*rootp
)->left
) {
1087 AvlNode
* left_subtree
= (*rootp
)->left
;
1088 if (avl_insert_wrk(&left_subtree
, oldV
, a
, kCmp
)) {
1089 switch ((*rootp
)->balance
--) {
1090 case 1: return False
;
1091 case 0: return True
;
1095 if ((*rootp
)->left
->balance
< 0) {
1097 (*rootp
)->balance
= 0;
1098 (*rootp
)->right
->balance
= 0;
1100 avl_swl( &((*rootp
)->left
) );
1102 avl_nasty( *rootp
);
1105 (*rootp
)->left
= left_subtree
;
1110 if ((*rootp
)->balance
--)
1114 assert(0);/*NOTREACHED*/
1118 /* insert into the right subtree */
1119 if ((*rootp
)->right
) {
1120 AvlNode
* right_subtree
= (*rootp
)->right
;
1121 if (avl_insert_wrk(&right_subtree
, oldV
, a
, kCmp
)) {
1122 switch((*rootp
)->balance
++){
1123 case -1: return False
;
1124 case 0: return True
;
1128 if ((*rootp
)->right
->balance
> 0) {
1130 (*rootp
)->balance
= 0;
1131 (*rootp
)->left
->balance
= 0;
1133 avl_swr( &((*rootp
)->right
) );
1135 avl_nasty( *rootp
);
1138 (*rootp
)->right
= right_subtree
;
1142 (*rootp
)->right
= a
;
1143 if ((*rootp
)->balance
++)
1147 assert(0);/*NOTREACHED*/
1150 /* cmpres == 0, a duplicate - replace the val, but don't
1151 incorporate the node in the tree */
1153 oldV
->w
= (*rootp
)->val
;
1154 (*rootp
)->val
= a
->val
;
1159 /* Remove an element a from the AVL tree t. a must be part of
1160 the tree. Returns True if the depth of the tree has shrunk.
1163 Bool
avl_remove_wrk ( AvlNode
** rootp
,
1165 Word(*kCmp
)(Word
,Word
) )
1168 Word cmpres
= kCmp( (*rootp
)->key
, a
->key
);
1171 /* remove from the left subtree */
1172 AvlNode
* left_subtree
= (*rootp
)->left
;
1173 assert(left_subtree
);
1174 ch
= avl_remove_wrk(&left_subtree
, a
, kCmp
);
1175 (*rootp
)->left
=left_subtree
;
1177 switch ((*rootp
)->balance
++) {
1178 case -1: return True
;
1179 case 0: return False
;
1183 switch ((*rootp
)->right
->balance
) {
1186 (*rootp
)->balance
= -1;
1187 (*rootp
)->left
->balance
= 1;
1191 (*rootp
)->balance
= 0;
1192 (*rootp
)->left
->balance
= 0;
1199 avl_swr( &((*rootp
)->right
) );
1201 avl_nasty( *rootp
);
1207 /* remove from the right subtree */
1208 AvlNode
* right_subtree
= (*rootp
)->right
;
1209 assert(right_subtree
);
1210 ch
= avl_remove_wrk(&right_subtree
, a
, kCmp
);
1211 (*rootp
)->right
= right_subtree
;
1213 switch ((*rootp
)->balance
--) {
1214 case 1: return True
;
1215 case 0: return False
;
1219 switch ((*rootp
)->left
->balance
) {
1222 (*rootp
)->balance
= 1;
1223 (*rootp
)->right
->balance
= -1;
1227 (*rootp
)->balance
= 0;
1228 (*rootp
)->right
->balance
= 0;
1235 avl_swl( &((*rootp
)->left
) );
1237 avl_nasty( *rootp
);
1242 assert(cmpres
== 0);
1243 assert((*rootp
)==a
);
1244 return avl_removeroot_wrk(rootp
, kCmp
);
1249 /* Remove the root of the AVL tree *rootp.
1250 * Warning: dumps core if *rootp is empty
1253 Bool
avl_removeroot_wrk ( AvlNode
** rootp
,
1254 Word(*kCmp
)(Word
,Word
) )
1258 if (!(*rootp
)->left
) {
1259 if (!(*rootp
)->right
) {
1263 (*rootp
) = (*rootp
)->right
;
1266 if (!(*rootp
)->right
) {
1267 (*rootp
) = (*rootp
)->left
;
1270 if ((*rootp
)->balance
< 0) {
1271 /* remove from the left subtree */
1273 while (a
->right
) a
= a
->right
;
1275 /* remove from the right subtree */
1276 a
= (*rootp
)->right
;
1277 while (a
->left
) a
= a
->left
;
1279 ch
= avl_remove_wrk(rootp
, a
, kCmp
);
1280 a
->left
= (*rootp
)->left
;
1281 a
->right
= (*rootp
)->right
;
1282 a
->balance
= (*rootp
)->balance
;
1284 if(a
->balance
== 0) return ch
;
1289 AvlNode
* avl_find_node ( AvlNode
* t
, Word k
, Word(*kCmp
)(Word
,Word
) )
1293 if (t
== NULL
) return NULL
;
1294 cmpres
= kCmp(t
->key
, k
);
1295 if (cmpres
> 0) t
= t
->left
; else
1296 if (cmpres
< 0) t
= t
->right
; else
1301 // Clear the iterator stack.
1302 static void stackClear(WordFM
* fm
)
1306 for (i
= 0; i
< WFM_STKMAX
; i
++) {
1307 fm
->nodeStack
[i
] = NULL
;
1308 fm
->numStack
[i
] = 0;
1313 // Push onto the iterator stack.
1314 static inline void stackPush(WordFM
* fm
, AvlNode
* n
, Int i
)
1316 assert(fm
->stackTop
< WFM_STKMAX
);
1317 assert(1 <= i
&& i
<= 3);
1318 fm
->nodeStack
[fm
->stackTop
] = n
;
1319 fm
-> numStack
[fm
->stackTop
] = i
;
1323 // Pop from the iterator stack.
1324 static inline Bool
stackPop(WordFM
* fm
, AvlNode
** n
, Int
* i
)
1326 assert(fm
->stackTop
<= WFM_STKMAX
);
1328 if (fm
->stackTop
> 0) {
1330 *n
= fm
->nodeStack
[fm
->stackTop
];
1331 *i
= fm
-> numStack
[fm
->stackTop
];
1332 assert(1 <= *i
&& *i
<= 3);
1333 fm
->nodeStack
[fm
->stackTop
] = NULL
;
1334 fm
-> numStack
[fm
->stackTop
] = 0;
1342 AvlNode
* avl_dopy ( AvlNode
* nd
,
1345 void*(alloc_nofail
)(SizeT
) )
1350 nyu
= alloc_nofail(sizeof(AvlNode
));
1353 nyu
->left
= nd
->left
;
1354 nyu
->right
= nd
->right
;
1355 nyu
->balance
= nd
->balance
;
1359 nyu
->key
= dopyK( nd
->key
);
1360 if (nd
->key
!= 0 && nyu
->key
== 0)
1361 return NULL
; /* oom in key dcopy */
1363 /* copying assumedly unboxed keys */
1369 nyu
->val
= dopyV( nd
->val
);
1370 if (nd
->val
!= 0 && nyu
->val
== 0)
1371 return NULL
; /* oom in val dcopy */
1373 /* copying assumedly unboxed vals */
1379 nyu
->left
= avl_dopy( nyu
->left
, dopyK
, dopyV
, alloc_nofail
);
1384 nyu
->right
= avl_dopy( nyu
->right
, dopyK
, dopyV
, alloc_nofail
);
1392 /* --- Public interface functions --- */
1394 /* Initialise a WordFM. */
1395 void initFM ( WordFM
* fm
,
1396 void* (*alloc_nofail
)( SizeT
),
1397 void (*dealloc
)(void*),
1398 Word (*kCmp
)(Word
,Word
) )
1402 fm
->alloc_nofail
= alloc_nofail
;
1403 fm
->dealloc
= dealloc
;
1407 /* Allocate and Initialise a WordFM. */
1408 WordFM
* newFM( void* (*alloc_nofail
)( SizeT
),
1409 void (*dealloc
)(void*),
1410 Word (*kCmp
)(Word
,Word
) )
1412 WordFM
* fm
= alloc_nofail(sizeof(WordFM
));
1414 initFM(fm
, alloc_nofail
, dealloc
, kCmp
);
1418 static void avl_free ( AvlNode
* nd
,
1421 void(*dealloc
)(void*) )
1426 avl_free(nd
->left
, kFin
, vFin
, dealloc
);
1428 avl_free(nd
->right
, kFin
, vFin
, dealloc
);
1433 memset(nd
, 0, sizeof(AvlNode
));
1437 /* Free up the FM. If kFin is non-NULL, it is applied to keys
1438 before the FM is deleted; ditto with vFin for vals. */
1439 void deleteFM ( WordFM
* fm
, void(*kFin
)(Word
), void(*vFin
)(Word
) )
1441 void(*dealloc
)(void*) = fm
->dealloc
;
1442 avl_free( fm
->root
, kFin
, vFin
, dealloc
);
1443 memset(fm
, 0, sizeof(WordFM
) );
1447 /* Add (k,v) to fm. */
1448 void addToFM ( WordFM
* fm
, Word k
, Word v
)
1452 node
= fm
->alloc_nofail( sizeof(struct _AvlNode
) );
1457 avl_insert_wrk( &fm
->root
, &oldV
, node
, fm
->kCmp
);
1458 //if (oldV.b && fm->vFin)
1459 // fm->vFin( oldV.w );
1464 // Delete key from fm, returning associated val if found
1465 Bool
delFromFM ( WordFM
* fm
, /*OUT*/Word
* oldV
, Word key
)
1467 AvlNode
* node
= avl_find_node( fm
->root
, key
, fm
->kCmp
);
1469 avl_remove_wrk( &fm
->root
, node
, fm
->kCmp
);
1479 // Look up in fm, assigning found val at spec'd address
1480 Bool
lookupFM ( WordFM
* fm
, /*OUT*/Word
* valP
, Word key
)
1482 AvlNode
* node
= avl_find_node( fm
->root
, key
, fm
->kCmp
);
1492 Word
sizeFM ( WordFM
* fm
)
1494 // Hmm, this is a bad way to do this
1495 return fm
->root
? size_avl_nonNull( fm
->root
) : 0;
1498 // set up FM for iteration
1499 void initIterFM ( WordFM
* fm
)
1504 stackPush(fm
, fm
->root
, 1);
1507 // get next key/val pair. Will assert if fm has been modified
1508 // or looked up in since initIterFM was called.
1509 Bool
nextIterFM ( WordFM
* fm
, /*OUT*/Word
* pKey
, /*OUT*/Word
* pVal
)
1516 // This in-order traversal requires each node to be pushed and popped
1517 // three times. These could be avoided by updating nodes in-situ on the
1518 // top of the stack, but the push/pop cost is so small that it's worth
1519 // keeping this loop in this simpler form.
1520 while (stackPop(fm
, &n
, &i
)) {
1523 stackPush(fm
, n
, 2);
1524 if (n
->left
) stackPush(fm
, n
->left
, 1);
1527 stackPush(fm
, n
, 3);
1528 if (pKey
) *pKey
= n
->key
;
1529 if (pVal
) *pVal
= n
->val
;
1532 if (n
->right
) stackPush(fm
, n
->right
, 1);
1539 // Stack empty, iterator is exhausted, return NULL
1543 // clear the I'm iterating flag
1544 void doneIterFM ( WordFM
* fm
)
1548 WordFM
* dopyFM ( WordFM
* fm
, Word(*dopyK
)(Word
), Word(*dopyV
)(Word
) )
1552 /* can't clone the fm whilst iterating on it */
1553 assert(fm
->stackTop
== 0);
1555 nyu
= fm
->alloc_nofail( sizeof(WordFM
) );
1561 memset(fm
->nodeStack
, 0, sizeof(fm
->nodeStack
));
1562 memset(fm
->numStack
, 0, sizeof(fm
->numStack
));
1565 nyu
->root
= avl_dopy( nyu
->root
, dopyK
, dopyV
, fm
->alloc_nofail
);
1573 //------------------------------------------------------------------//
1574 //--- end WordFM ---//
1575 //--- Implementation ---//
1576 //------------------------------------------------------------------//
1578 /*--------------------------------------------------------------------*/
1579 /*--- end cg_merge.c ---*/
1580 /*--------------------------------------------------------------------*/