cg_merge.c revision fdae2316e234e31b87c876376f60d927da7a7ccf
1
2/*--------------------------------------------------------------------*/
3/*--- A program that merges multiple cachegrind output files.      ---*/
4/*---                                                   cg_merge.c ---*/
5/*--------------------------------------------------------------------*/
6
7/*
8  This file is part of Cachegrind, a Valgrind tool for cache
9  profiling programs.
10
11  Copyright (C) 2002-2013 Nicholas Nethercote
12     njn@valgrind.org
13
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
18
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.
23
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.
28
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
32  02111-1307, USA.
33
34  The GNU General Public License is contained in the file COPYING.
35*/
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <assert.h>
40#include <string.h>
41#include <ctype.h>
42
43typedef  signed long   Word;
44typedef  unsigned long UWord;
45typedef  unsigned char Bool;
46#define True ((Bool)1)
47#define False ((Bool)0)
48typedef  signed int    Int;
49typedef  unsigned int  UInt;
50typedef  unsigned long long int ULong;
51typedef  signed char   Char;
52typedef  size_t        SizeT;
53
54
55//------------------------------------------------------------------//
56//---                           WordFM                           ---//
57//---                      Public interface                      ---//
58//------------------------------------------------------------------//
59
60typedef  struct _WordFM  WordFM; /* opaque */
61
62/* Initialise a WordFM */
63void initFM ( WordFM* t,
64              void*   (*alloc_nofail)( SizeT ),
65              void    (*dealloc)(void*),
66              Word    (*kCmp)(Word,Word) );
67
68/* Allocate and initialise a WordFM */
69WordFM* newFM( void* (*alloc_nofail)( SizeT ),
70               void  (*dealloc)(void*),
71               Word  (*kCmp)(Word,Word) );
72
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. */
75void deleteFM ( WordFM*, void(*kFin)(Word), void(*vFin)(Word) );
76
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. */
80void addToFM ( WordFM* fm, Word k, Word v );
81
82// Delete key from fm, returning associated val if found
83Bool delFromFM ( WordFM* fm, /*OUT*/Word* oldV, Word key );
84
85// Look up in fm, assigning found val at spec'd address
86Bool lookupFM ( WordFM* fm, /*OUT*/Word* valP, Word key );
87
88Word sizeFM ( WordFM* fm );
89
90// set up FM for iteration
91void initIterFM ( WordFM* fm );
92
93// get next key/val pair.  Will assert if fm has been modified
94// or looked up in since initIterFM was called.
95Bool nextIterFM ( WordFM* fm, /*OUT*/Word* pKey, /*OUT*/Word* pVal );
96
97// clear the I'm iterating flag
98void doneIterFM ( WordFM* fm );
99
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.
106WordFM* dopyFM ( WordFM* fm, Word(*dopyK)(Word), Word(*dopyV)(Word) );
107
108//------------------------------------------------------------------//
109//---                         end WordFM                         ---//
110//---                      Public interface                      ---//
111//------------------------------------------------------------------//
112
113
114static const char* argv0 = "cg_merge";
115
116/* Keep track of source filename/line no so as to be able to
117   print decent error messages. */
118typedef
119   struct {
120      FILE* fp;
121      UInt  lno;
122      char* filename;
123   }
124   SOURCE;
125
126static void printSrcLoc ( SOURCE* s )
127{
128   fprintf(stderr, "%s: near %s line %u\n", argv0, s->filename, s->lno-1);
129}
130
131__attribute__((noreturn))
132static void mallocFail ( SOURCE* s, const char* who )
133{
134   fprintf(stderr, "%s: out of memory in %s\n", argv0, who );
135   printSrcLoc( s );
136   exit(2);
137}
138
139__attribute__((noreturn))
140static void parseError ( SOURCE* s, const char* msg )
141{
142   fprintf(stderr, "%s: parse error: %s\n", argv0, msg );
143   printSrcLoc( s );
144   exit(1);
145}
146
147__attribute__((noreturn))
148static void barf ( SOURCE* s, const char* msg )
149{
150   fprintf(stderr, "%s: %s\n", argv0, msg );
151   printSrcLoc( s );
152   exit(1);
153}
154
155// Read a line
156#define M_LINEBUF 40960
157static char line[M_LINEBUF];
158
159// True if anything read, False if at EOF
160static Bool readline ( SOURCE* s )
161{
162   int ch, i = 0;
163   line[0] = 0;
164   while (1) {
165      if (i >= M_LINEBUF-10)
166         parseError(s, "Unexpected long line in input file");
167      ch = getc(s->fp);
168      if (ch != EOF) {
169          line[i++] = ch;
170          line[i] = 0;
171          if (ch == '\n') {
172             line[i-1] = 0;
173             s->lno++;
174             break;
175          }
176      } else {
177         if (ferror(s->fp)) {
178            perror(argv0);
179            barf(s, "I/O error while reading input file");
180         } else {
181            // hit EOF
182            break;
183         }
184      }
185   }
186   return line[0] != 0;
187}
188
189static Bool streqn ( const char* s1, const char* s2, size_t n )
190{
191   return 0 == strncmp(s1, s2, n);
192}
193
194static Bool streq ( const char* s1, const char* s2 )
195{
196   return 0 == strcmp(s1, s2 );
197}
198
199
200////////////////////////////////////////////////////////////////
201
202typedef
203   struct {
204      char* fi_name;
205      char* fn_name;
206   }
207   FileFn;
208
209typedef
210   struct {
211      Int n_counts;
212      ULong* counts;
213   }
214   Counts;
215
216typedef
217   struct {
218      // null-terminated vector of desc_lines
219      char** desc_lines;
220
221      // Cmd line
222      char* cmd_line;
223
224      // Events line
225      char* events_line;
226      Int   n_events;
227
228      // Summary line (copied from input)
229      char* summary_line;
230
231      /* Outermost map is
232            WordFM FileFn* innerMap
233         where innerMap is   WordFM line-number=UWord Counts */
234      WordFM* outerMap;
235
236      // Summary counts (computed whilst parsing)
237      // should match .summary_line
238      Counts* summary;
239   }
240   CacheProfFile;
241
242static FileFn* new_FileFn ( char* file_name, char* fn_name )
243{
244   FileFn* ffn = malloc(sizeof(FileFn));
245   if (ffn == NULL)
246      return NULL;
247   ffn->fi_name = file_name;
248   ffn->fn_name = fn_name;
249   return ffn;
250}
251
252static void ddel_FileFn ( FileFn* ffn )
253{
254   if (ffn->fi_name)
255      free(ffn->fi_name);
256   if (ffn->fn_name)
257      free(ffn->fn_name);
258   memset(ffn, 0, sizeof(FileFn));
259   free(ffn);
260}
261
262static FileFn* dopy_FileFn ( FileFn* ff )
263{
264   char *fi2, *fn2;
265   fi2 = strdup(ff->fi_name);
266   if (fi2 == NULL) return NULL;
267   fn2 = strdup(ff->fn_name);
268   if (fn2 == NULL) {
269      free(fi2);
270      return NULL;
271   }
272   return new_FileFn( fi2, fn2 );
273}
274
275static Counts* new_Counts ( Int n_counts, /*COPIED*/ULong* counts )
276{
277   Int i;
278   Counts* cts = malloc(sizeof(Counts));
279   if (cts == NULL)
280      return NULL;
281
282   assert(n_counts >= 0);
283   cts->counts = malloc(n_counts * sizeof(ULong));
284   if (cts->counts == NULL) {
285      free(cts);
286      return NULL;
287   }
288
289   cts->n_counts = n_counts;
290   for (i = 0; i < n_counts; i++)
291      cts->counts[i] = counts[i];
292
293   return cts;
294}
295
296static Counts* new_Counts_Zeroed ( Int n_counts )
297{
298   Int i;
299   Counts* cts = malloc(sizeof(Counts));
300   if (cts == NULL)
301      return NULL;
302
303   assert(n_counts >= 0);
304   cts->counts = malloc(n_counts * sizeof(ULong));
305   if (cts->counts == NULL) {
306      free(cts);
307      return NULL;
308   }
309
310   cts->n_counts = n_counts;
311   for (i = 0; i < n_counts; i++)
312      cts->counts[i] = 0;
313
314   return cts;
315}
316
317static void sdel_Counts ( Counts* cts )
318{
319   memset(cts, 0, sizeof(Counts));
320   free(cts);
321}
322
323static void ddel_Counts ( Counts* cts )
324{
325   if (cts->counts)
326      free(cts->counts);
327   memset(cts, 0, sizeof(Counts));
328   free(cts);
329}
330
331static Counts* dopy_Counts ( Counts* cts )
332{
333   return new_Counts( cts->n_counts, cts->counts );
334}
335
336static
337CacheProfFile* new_CacheProfFile ( char**  desc_lines,
338                                   char*   cmd_line,
339                                   char*   events_line,
340                                   Int     n_events,
341                                   char*   summary_line,
342                                   WordFM* outerMap,
343                                   Counts* summary )
344{
345   CacheProfFile* cpf = malloc(sizeof(CacheProfFile));
346   if (cpf == NULL)
347      return NULL;
348   cpf->desc_lines   = desc_lines;
349   cpf->cmd_line     = cmd_line;
350   cpf->events_line  = events_line;
351   cpf->n_events     = n_events;
352   cpf->summary_line = summary_line;
353   cpf->outerMap     = outerMap;
354   cpf->summary      = summary;
355   return cpf;
356}
357
358static WordFM* dopy_InnerMap ( WordFM* innerMap )
359{
360   return dopyFM ( innerMap, NULL,
361                             (Word(*)(Word))dopy_Counts );
362}
363
364static void ddel_InnerMap ( WordFM* innerMap )
365{
366   deleteFM( innerMap, NULL, (void(*)(Word))ddel_Counts );
367}
368
369static void ddel_CacheProfFile ( CacheProfFile* cpf )
370{
371   char** p;
372   if (cpf->desc_lines) {
373      for (p = cpf->desc_lines; *p; p++)
374         free(*p);
375      free(cpf->desc_lines);
376   }
377   if (cpf->cmd_line)
378      free(cpf->cmd_line);
379   if (cpf->events_line)
380      free(cpf->events_line);
381   if (cpf->summary_line)
382      free(cpf->summary_line);
383   if (cpf->outerMap)
384      deleteFM( cpf->outerMap, (void(*)(Word))ddel_FileFn,
385                               (void(*)(Word))ddel_InnerMap );
386   if (cpf->summary)
387      ddel_Counts(cpf->summary);
388
389   memset(cpf, 0, sizeof(CacheProfFile));
390   free(cpf);
391}
392
393static void showCounts ( FILE* f, Counts* c )
394{
395   Int i;
396   for (i = 0; i < c->n_counts; i++) {
397      fprintf(f, "%lld ", c->counts[i]);
398   }
399}
400
401static void show_CacheProfFile ( FILE* f, CacheProfFile* cpf )
402{
403   Int     i;
404   char**  d;
405   FileFn* topKey;
406   WordFM* topVal;
407   UWord   subKey;
408   Counts* subVal;
409
410   for (d = cpf->desc_lines; *d; d++)
411      fprintf(f, "%s\n", *d);
412   fprintf(f, "%s\n", cpf->cmd_line);
413   fprintf(f, "%s\n", cpf->events_line);
414
415   initIterFM( cpf->outerMap );
416   while (nextIterFM( cpf->outerMap, (Word*)(&topKey), (Word*)(&topVal) )) {
417      fprintf(f, "fl=%s\nfn=%s\n",
418                 topKey->fi_name, topKey->fn_name );
419      initIterFM( topVal );
420      while (nextIterFM( topVal, (Word*)(&subKey), (Word*)(&subVal) )) {
421         fprintf(f, "%ld   ", subKey );
422         showCounts( f, subVal );
423         fprintf(f, "\n");
424      }
425      doneIterFM( topVal );
426   }
427   doneIterFM( cpf->outerMap );
428
429   //fprintf(f, "%s\n", cpf->summary_line);
430   fprintf(f, "summary:");
431   for (i = 0; i < cpf->summary->n_counts; i++)
432      fprintf(f, " %lld", cpf->summary->counts[i]);
433   fprintf(f, "\n");
434}
435
436////////////////////////////////////////////////////////////////
437
438static Word cmp_FileFn ( Word s1, Word s2 )
439{
440   FileFn* ff1 = (FileFn*)s1;
441   FileFn* ff2 = (FileFn*)s2;
442   Word r = strcmp(ff1->fi_name, ff2->fi_name);
443   if (r == 0)
444      r = strcmp(ff1->fn_name, ff2->fn_name);
445   return r;
446}
447
448static Word cmp_unboxed_UWord ( Word s1, Word s2 )
449{
450   UWord u1 = (UWord)s1;
451   UWord u2 = (UWord)s2;
452   if (u1 < u2) return -1;
453   if (u1 > u2) return 1;
454   return 0;
455}
456
457////////////////////////////////////////////////////////////////
458
459static Bool parse_ULong ( /*OUT*/ULong* res, /*INOUT*/char** pptr)
460{
461   ULong u64;
462   char* ptr = *pptr;
463   while (isspace(*ptr)) ptr++;
464   if (!isdigit(*ptr)) {
465      *pptr = ptr;
466      return False; /* end of string, or junk */
467   }
468   u64 = 0;
469   while (isdigit(*ptr)) {
470      u64 = (u64 * 10) + (ULong)(*ptr - '0');
471      ptr++;
472   }
473   *res = u64;
474   *pptr = ptr;
475   return True;
476}
477
478// str is a line of digits, starting with a line number.  Parse it,
479// returning the first number in *lnno and the rest in a newly
480// allocated Counts struct.  If lnno is non-NULL, treat the first
481// number as a line number and assign it to *lnno instead of
482// incorporating it in the counts array.
483static
484Counts* splitUpCountsLine ( SOURCE* s, /*OUT*/UWord* lnno, char* str )
485{
486#define N_TMPC 50
487   Bool    ok;
488   Counts* counts;
489   ULong   tmpC[N_TMPC];
490   Int     n_tmpC = 0;
491   while (1) {
492      ok = parse_ULong( &tmpC[n_tmpC], &str );
493      if (!ok)
494         break;
495      n_tmpC++;
496      if (n_tmpC >= N_TMPC)
497         barf(s, "N_TMPC too low.  Increase and recompile.");
498   }
499   if (*str != 0)
500      parseError(s, "garbage in counts line");
501   if (lnno ? (n_tmpC < 2) : (n_tmpC < 1))
502      parseError(s, "too few counts in count line");
503
504   if (lnno) {
505      *lnno = (UWord)tmpC[0];
506      counts = new_Counts( n_tmpC-1, /*COPIED*/&tmpC[1] );
507   } else {
508      counts = new_Counts( n_tmpC, /*COPIED*/&tmpC[0] );
509   }
510
511   return counts;
512#undef N_TMPC
513}
514
515static void addCounts ( SOURCE* s, /*OUT*/Counts* counts1, Counts* counts2 )
516{
517   Int i;
518   if (counts1->n_counts != counts2->n_counts)
519      parseError(s, "addCounts: inconsistent number of counts");
520   for (i = 0; i < counts1->n_counts; i++)
521      counts1->counts[i] += counts2->counts[i];
522}
523
524static Bool addCountsToMap ( SOURCE* s,
525                             WordFM* counts_map,
526                             UWord lnno, Counts* newCounts )
527{
528   Counts* oldCounts;
529   // look up lnno in the map.  If none present, add a binding
530   // lnno->counts.  If present, add counts to the existing entry.
531   if (lookupFM( counts_map, (Word*)(&oldCounts), (Word)lnno )) {
532      // merge with existing binding
533      addCounts( s, oldCounts, newCounts );
534      return True;
535   } else {
536      // create new binding
537      addToFM( counts_map, (Word)lnno, (Word)newCounts );
538      return False;
539   }
540}
541
542static
543void handle_counts ( SOURCE* s,
544                     CacheProfFile* cpf,
545                     const char* fi, const char* fn, char* newCountsStr )
546{
547   WordFM* countsMap;
548   Bool    freeNewCounts;
549   UWord   lnno;
550   Counts* newCounts;
551   FileFn* topKey;
552
553   if (0)  printf("%s %s %s\n", fi, fn, newCountsStr );
554
555   // parse the numbers
556   newCounts = splitUpCountsLine( s, &lnno, newCountsStr );
557
558   // Did we get the right number?
559   if (newCounts->n_counts != cpf->n_events)
560      goto oom;
561
562   // allocate the key
563   topKey = malloc(sizeof(FileFn));
564   if (topKey) {
565      topKey->fi_name = strdup(fi);
566      topKey->fn_name = strdup(fn);
567   }
568   if (! (topKey && topKey->fi_name && topKey->fn_name))
569      mallocFail(s, "handle_counts:");
570
571   // search for it
572   if (lookupFM( cpf->outerMap, (Word*)(&countsMap), (Word)topKey )) {
573      // found it.  Merge in new counts
574      freeNewCounts = addCountsToMap( s, countsMap, lnno, newCounts );
575      ddel_FileFn(topKey);
576   } else {
577      // not found in the top map.  Create new entry
578      countsMap = newFM( malloc, free, cmp_unboxed_UWord );
579      if (!countsMap)
580         goto oom;
581      addToFM( cpf->outerMap, (Word)topKey, (Word)countsMap );
582      freeNewCounts = addCountsToMap( s, countsMap, lnno, newCounts );
583   }
584
585   // also add to running summary total
586   addCounts( s, cpf->summary, newCounts );
587
588   // if safe to do so, free up the count vector
589   if (freeNewCounts)
590      ddel_Counts(newCounts);
591
592   return;
593
594  oom:
595   parseError(s, "# counts doesn't match # events");
596}
597
598
599/* Parse a complete file from the stream in 's'.  If a parse error
600   happens, do not return; instead exit via parseError().  If an
601   out-of-memory condition happens, do not return; instead exit via
602   mallocError().
603*/
604static CacheProfFile* parse_CacheProfFile ( SOURCE* s )
605{
606#define M_TMP_DESCLINES 10
607
608   Int            i;
609   Bool           b;
610   char*          tmp_desclines[M_TMP_DESCLINES];
611   char*          p;
612   int            n_tmp_desclines = 0;
613   CacheProfFile* cpf;
614   Counts*        summaryRead;
615   char*          curr_fn = strdup("???");
616   char*          curr_fl = strdup("???");
617
618   cpf = new_CacheProfFile( NULL, NULL, NULL, 0, NULL, NULL, NULL );
619   if (cpf == NULL)
620      mallocFail(s, "parse_CacheProfFile(1)");
621
622   // Parse "desc:" lines
623   while (1) {
624      b = readline(s);
625      if (!b)
626         break;
627      if (!streqn(line, "desc: ", 6))
628         break;
629      if (n_tmp_desclines >= M_TMP_DESCLINES)
630         barf(s, "M_TMP_DESCLINES too low; increase and recompile");
631      tmp_desclines[n_tmp_desclines++] = strdup(line);
632   }
633
634   if (n_tmp_desclines == 0)
635      parseError(s, "parse_CacheProfFile: no DESC lines present");
636
637   cpf->desc_lines = malloc( (1+n_tmp_desclines) * sizeof(char*) );
638   if (cpf->desc_lines == NULL)
639      mallocFail(s, "parse_CacheProfFile(2)");
640
641   cpf->desc_lines[n_tmp_desclines] = NULL;
642   for (i = 0; i < n_tmp_desclines; i++)
643      cpf->desc_lines[i] = tmp_desclines[i];
644
645   // Parse "cmd:" line
646   if (!streqn(line, "cmd: ", 5))
647      parseError(s, "parse_CacheProfFile: no CMD line present");
648
649   cpf->cmd_line = strdup(line);
650   if (cpf->cmd_line == NULL)
651      mallocFail(s, "parse_CacheProfFile(3)");
652
653   // Parse "events:" line and figure out how many events there are
654   b = readline(s);
655   if (!b)
656      parseError(s, "parse_CacheProfFile: eof before EVENTS line");
657   if (!streqn(line, "events: ", 8))
658      parseError(s, "parse_CacheProfFile: no EVENTS line present");
659
660   // figure out how many events there are by counting the number
661   // of space-alphanum transitions in the events_line
662   cpf->events_line = strdup(line);
663   if (cpf->events_line == NULL)
664      mallocFail(s, "parse_CacheProfFile(3)");
665
666   cpf->n_events = 0;
667   assert(cpf->events_line[6] == ':');
668   for (p = &cpf->events_line[6]; *p; p++) {
669      if (p[0] == ' ' && isalpha(p[1]))
670         cpf->n_events++;
671   }
672
673   // create the running cross-check summary
674   cpf->summary = new_Counts_Zeroed( cpf->n_events );
675   if (cpf->summary == NULL)
676      mallocFail(s, "parse_CacheProfFile(4)");
677
678   // create the outer map (file+fn name --> inner map)
679   cpf->outerMap = newFM ( malloc, free, cmp_FileFn );
680   if (cpf->outerMap == NULL)
681      mallocFail(s, "parse_CacheProfFile(5)");
682
683   // process count lines
684   while (1) {
685      b = readline(s);
686      if (!b)
687         parseError(s, "parse_CacheProfFile: eof before SUMMARY line");
688
689      if (isdigit(line[0])) {
690         handle_counts(s, cpf, curr_fl, curr_fn, line);
691         continue;
692      }
693      else
694      if (streqn(line, "fn=", 3)) {
695         free(curr_fn);
696         curr_fn = strdup(line+3);
697         continue;
698      }
699      else
700      if (streqn(line, "fl=", 3)) {
701         free(curr_fl);
702         curr_fl = strdup(line+3);
703         continue;
704      }
705      else
706      if (streqn(line, "summary: ", 9)) {
707         break;
708      }
709      else
710         parseError(s, "parse_CacheProfFile: unexpected line in main data");
711   }
712
713   // finally, the "summary:" line
714   if (!streqn(line, "summary: ", 9))
715      parseError(s, "parse_CacheProfFile: missing SUMMARY line");
716
717   cpf->summary_line = strdup(line);
718   if (cpf->summary_line == NULL)
719      mallocFail(s, "parse_CacheProfFile(6)");
720
721   // there should be nothing more
722   b = readline(s);
723   if (b)
724      parseError(s, "parse_CacheProfFile: "
725                    "extraneous content after SUMMARY line");
726
727   // check the summary counts are as expected
728   summaryRead = splitUpCountsLine( s, NULL, &cpf->summary_line[8] );
729   if (summaryRead == NULL)
730      mallocFail(s, "parse_CacheProfFile(7)");
731   if (summaryRead->n_counts != cpf->n_events)
732      parseError(s, "parse_CacheProfFile: wrong # counts in SUMMARY line");
733   for (i = 0; i < summaryRead->n_counts; i++) {
734      if (summaryRead->counts[i] != cpf->summary->counts[i]) {
735         parseError(s, "parse_CacheProfFile: "
736                       "computed vs stated SUMMARY counts mismatch");
737      }
738   }
739   free(summaryRead->counts);
740   sdel_Counts(summaryRead);
741
742   // since the summary counts are OK, free up the summary_line text
743   // which contains the same info.
744   if (cpf->summary_line) {
745      free(cpf->summary_line);
746      cpf->summary_line = NULL;
747   }
748
749   free(curr_fn);
750   free(curr_fl);
751
752   // All looks OK
753   return cpf;
754
755#undef N_TMP_DESCLINES
756}
757
758
759static void merge_CacheProfInfo ( SOURCE* s,
760                                  /*MOD*/CacheProfFile* dst,
761                                  CacheProfFile* src )
762{
763   /* For each (filefn, innerMap) in src
764      if filefn not in dst
765         add binding dopy(filefn)->dopy(innerMap) in src
766      else
767         // merge src->innerMap with dst->innerMap
768         for each (lineno, counts) in src->innerMap
769         if lineno not in dst->innerMap
770            add binding lineno->dopy(counts) to dst->innerMap
771         else
772            add counts into dst->innerMap[lineno]
773   */
774   /* Outer iterator:  FileFn* -> WordFM* (inner iterator)
775      Inner iterator:  UWord   -> Counts*
776   */
777   FileFn* soKey;
778   WordFM* soVal;
779   WordFM* doVal;
780   UWord   siKey;
781   Counts* siVal;
782   Counts* diVal;
783
784   /* First check mundane things: that the events: lines are
785      identical. */
786   if (!streq( dst->events_line, src->events_line ))
787     barf(s, "\"events:\" line of most recent file does "
788             "not match those previously processed");
789
790   initIterFM( src->outerMap );
791
792   // for (filefn, innerMap) in src
793   while (nextIterFM( src->outerMap, (Word*)&soKey, (Word*)&soVal )) {
794
795      // is filefn in dst?
796      if (! lookupFM( dst->outerMap, (Word*)&doVal, (Word)soKey )) {
797
798         // no .. add dopy(filefn) -> dopy(innerMap) to src
799         FileFn* c_soKey = dopy_FileFn(soKey);
800         WordFM* c_soVal = dopy_InnerMap(soVal);
801         if ((!c_soKey) || (!c_soVal)) goto oom;
802         addToFM( dst->outerMap, (Word)c_soKey, (Word)c_soVal );
803
804      } else {
805
806         // yes .. merge the two innermaps
807         initIterFM( soVal );
808
809         // for (lno, counts) in soVal (source inner map)
810         while (nextIterFM( soVal, (Word*)&siKey, (Word*)&siVal )) {
811
812            // is lno in the corresponding dst inner map?
813            if (! lookupFM( doVal, (Word*)&diVal, siKey )) {
814
815               // no .. add lineno->dopy(counts) to dst inner map
816               Counts* c_siVal = dopy_Counts( siVal );
817               if (!c_siVal) goto oom;
818               addToFM( doVal, siKey, (Word)c_siVal );
819
820            } else {
821
822               // yes .. merge counts into dst inner map val
823               addCounts( s, diVal, siVal );
824
825            }
826         }
827
828      }
829
830   }
831
832   // add the summaries too
833   addCounts(s, dst->summary, src->summary );
834
835   return;
836
837  oom:
838   mallocFail(s, "merge_CacheProfInfo");
839}
840
841static void usage ( void )
842{
843   fprintf(stderr, "%s: Merges multiple cachegrind output files into one\n",
844                   argv0);
845   fprintf(stderr, "%s: usage: %s [-o outfile] [files-to-merge]\n",
846                   argv0, argv0);
847   exit(1);
848}
849
850int main ( int argc, char** argv )
851{
852   Int            i;
853   SOURCE         src;
854   CacheProfFile  *cpf, *cpfTmp;
855
856   FILE*          outfile = NULL;
857   char*          outfilename = NULL;
858   Int            outfileix = 0;
859
860   if (argv[0])
861      argv0 = argv[0];
862
863   if (argc < 2)
864      usage();
865
866   for (i = 1; i < argc; i++) {
867      if (streq(argv[i], "-h") || streq(argv[i], "--help"))
868         usage();
869   }
870
871   /* Scan args, looking for '-o outfilename'. */
872   for (i = 1; i < argc; i++) {
873      if (streq(argv[i], "-o")) {
874         if (i+1 < argc) {
875            outfilename = argv[i+1];
876            outfileix   = i;
877            break;
878         } else {
879            usage();
880         }
881      }
882   }
883
884   cpf = NULL;
885
886   for (i = 1; i < argc; i++) {
887
888      if (i == outfileix) {
889         /* Skip '-o' and whatever follows it */
890         i += 1;
891         continue;
892      }
893
894      fprintf(stderr, "%s: parsing %s\n", argv0, argv[i]);
895      src.lno      = 1;
896      src.filename = argv[i];
897      src.fp       = fopen(src.filename, "r");
898      if (!src.fp) {
899         perror(argv0);
900         barf(&src, "Cannot open input file");
901      }
902      assert(src.fp);
903      cpfTmp = parse_CacheProfFile( &src );
904      fclose(src.fp);
905
906      /* If this isn't the first file, merge */
907      if (cpf == NULL) {
908         /* this is the first file */
909         cpf = cpfTmp;
910      } else {
911         /* not the first file; merge */
912         fprintf(stderr, "%s: merging %s\n", argv0, argv[i]);
913         merge_CacheProfInfo( &src, cpf, cpfTmp );
914         ddel_CacheProfFile( cpfTmp );
915      }
916
917   }
918
919   /* Now create the output file. */
920
921   if (cpf) {
922
923      fprintf(stderr, "%s: writing %s\n",
924                       argv0, outfilename ? outfilename : "(stdout)" );
925
926      /* Write the output. */
927      if (outfilename) {
928         outfile = fopen(outfilename, "w");
929         if (!outfile) {
930            fprintf(stderr, "%s: can't create output file %s\n",
931                            argv0, outfilename);
932            perror(argv0);
933            exit(1);
934         }
935      } else {
936         outfile = stdout;
937      }
938
939      show_CacheProfFile( outfile, cpf );
940      if (ferror(outfile)) {
941         fprintf(stderr, "%s: error writing output file %s\n",
942                         argv0, outfilename ? outfilename : "(stdout)" );
943         perror(argv0);
944         if (outfile != stdout)
945            fclose(outfile);
946         exit(1);
947      }
948
949      fflush(outfile);
950      if (outfile != stdout)
951         fclose( outfile );
952
953      ddel_CacheProfFile( cpf );
954   }
955
956   return 0;
957}
958
959
960//------------------------------------------------------------------//
961//---                           WordFM                           ---//
962//---                       Implementation                       ---//
963//------------------------------------------------------------------//
964
965/* ------------ Implementation ------------ */
966
967/* One element of the AVL tree */
968typedef
969   struct _AvlNode {
970      Word key;
971      Word val;
972      struct _AvlNode* left;
973      struct _AvlNode* right;
974      Char balance;
975   }
976   AvlNode;
977
978typedef
979   struct {
980      Word w;
981      Bool b;
982   }
983   MaybeWord;
984
985#define WFM_STKMAX    32    // At most 2**32 entries can be iterated over
986
987struct _WordFM {
988   AvlNode* root;
989   void*    (*alloc_nofail)( SizeT );
990   void     (*dealloc)(void*);
991   Word     (*kCmp)(Word,Word);
992   AvlNode* nodeStack[WFM_STKMAX]; // Iterator node stack
993   Int      numStack[WFM_STKMAX];  // Iterator num stack
994   Int      stackTop;              // Iterator stack pointer, one past end
995};
996
997/* forward */
998static Bool avl_removeroot_wrk(AvlNode** t, Word(*kCmp)(Word,Word));
999
1000/* Swing to the left.  Warning: no balance maintainance. */
1001static void avl_swl ( AvlNode** root )
1002{
1003   AvlNode* a = *root;
1004   AvlNode* b = a->right;
1005   *root    = b;
1006   a->right = b->left;
1007   b->left  = a;
1008}
1009
1010/* Swing to the right.  Warning: no balance maintainance. */
1011static void avl_swr ( AvlNode** root )
1012{
1013   AvlNode* a = *root;
1014   AvlNode* b = a->left;
1015   *root    = b;
1016   a->left  = b->right;
1017   b->right = a;
1018}
1019
1020/* Balance maintainance after especially nasty swings. */
1021static void avl_nasty ( AvlNode* root )
1022{
1023   switch (root->balance) {
1024      case -1:
1025         root->left->balance  = 0;
1026         root->right->balance = 1;
1027         break;
1028      case 1:
1029         root->left->balance  = -1;
1030         root->right->balance = 0;
1031         break;
1032      case 0:
1033         root->left->balance  = 0;
1034         root->right->balance = 0;
1035         break;
1036      default:
1037         assert(0);
1038   }
1039   root->balance=0;
1040}
1041
1042/* Find size of a non-NULL tree. */
1043static Word size_avl_nonNull ( AvlNode* nd )
1044{
1045   return 1 + (nd->left  ? size_avl_nonNull(nd->left)  : 0)
1046            + (nd->right ? size_avl_nonNull(nd->right) : 0);
1047}
1048
1049/* Insert element a into the AVL tree t.  Returns True if the depth of
1050   the tree has grown.  If element with that key is already present,
1051   just copy a->val to existing node, first returning old ->val field
1052   of existing node in *oldV, so that the caller can finalize it
1053   however it wants.
1054*/
1055static
1056Bool avl_insert_wrk ( AvlNode**         rootp,
1057                      /*OUT*/MaybeWord* oldV,
1058                      AvlNode*          a,
1059                      Word              (*kCmp)(Word,Word) )
1060{
1061   Word cmpres;
1062
1063   /* initialize */
1064   a->left    = 0;
1065   a->right   = 0;
1066   a->balance = 0;
1067   oldV->b    = False;
1068
1069   /* insert into an empty tree? */
1070   if (!(*rootp)) {
1071      (*rootp) = a;
1072      return True;
1073   }
1074
1075   cmpres = kCmp( (*rootp)->key, a->key );
1076
1077   if (cmpres > 0) {
1078      /* insert into the left subtree */
1079      if ((*rootp)->left) {
1080         AvlNode* left_subtree = (*rootp)->left;
1081         if (avl_insert_wrk(&left_subtree, oldV, a, kCmp)) {
1082            switch ((*rootp)->balance--) {
1083               case  1: return False;
1084               case  0: return True;
1085               case -1: break;
1086               default: assert(0);
1087            }
1088            if ((*rootp)->left->balance < 0) {
1089               avl_swr( rootp );
1090               (*rootp)->balance = 0;
1091               (*rootp)->right->balance = 0;
1092            } else {
1093               avl_swl( &((*rootp)->left) );
1094               avl_swr( rootp );
1095               avl_nasty( *rootp );
1096            }
1097         } else {
1098            (*rootp)->left = left_subtree;
1099         }
1100         return False;
1101      } else {
1102         (*rootp)->left = a;
1103         if ((*rootp)->balance--)
1104            return False;
1105         return True;
1106      }
1107      assert(0);/*NOTREACHED*/
1108   }
1109   else
1110   if (cmpres < 0) {
1111      /* insert into the right subtree */
1112      if ((*rootp)->right) {
1113         AvlNode* right_subtree = (*rootp)->right;
1114         if (avl_insert_wrk(&right_subtree, oldV, a, kCmp)) {
1115            switch((*rootp)->balance++){
1116               case -1: return False;
1117               case  0: return True;
1118               case  1: break;
1119               default: assert(0);
1120            }
1121            if ((*rootp)->right->balance > 0) {
1122               avl_swl( rootp );
1123               (*rootp)->balance = 0;
1124               (*rootp)->left->balance = 0;
1125            } else {
1126               avl_swr( &((*rootp)->right) );
1127               avl_swl( rootp );
1128               avl_nasty( *rootp );
1129            }
1130         } else {
1131            (*rootp)->right = right_subtree;
1132         }
1133         return False;
1134      } else {
1135         (*rootp)->right = a;
1136         if ((*rootp)->balance++)
1137            return False;
1138         return True;
1139      }
1140      assert(0);/*NOTREACHED*/
1141   }
1142   else {
1143      /* cmpres == 0, a duplicate - replace the val, but don't
1144         incorporate the node in the tree */
1145      oldV->b = True;
1146      oldV->w = (*rootp)->val;
1147      (*rootp)->val = a->val;
1148      return False;
1149   }
1150}
1151
1152/* Remove an element a from the AVL tree t.  a must be part of
1153   the tree.  Returns True if the depth of the tree has shrunk.
1154*/
1155static
1156Bool avl_remove_wrk ( AvlNode** rootp,
1157                      AvlNode*  a,
1158                      Word(*kCmp)(Word,Word) )
1159{
1160   Bool ch;
1161   Word cmpres = kCmp( (*rootp)->key, a->key );
1162
1163   if (cmpres > 0){
1164      /* remove from the left subtree */
1165      AvlNode* left_subtree = (*rootp)->left;
1166      assert(left_subtree);
1167      ch = avl_remove_wrk(&left_subtree, a, kCmp);
1168      (*rootp)->left=left_subtree;
1169      if (ch) {
1170         switch ((*rootp)->balance++) {
1171            case -1: return True;
1172            case  0: return False;
1173            case  1: break;
1174            default: assert(0);
1175         }
1176         switch ((*rootp)->right->balance) {
1177            case 0:
1178               avl_swl( rootp );
1179               (*rootp)->balance = -1;
1180               (*rootp)->left->balance = 1;
1181               return False;
1182            case 1:
1183               avl_swl( rootp );
1184               (*rootp)->balance = 0;
1185               (*rootp)->left->balance = 0;
1186               return -1;
1187            case -1:
1188               break;
1189            default:
1190               assert(0);
1191         }
1192         avl_swr( &((*rootp)->right) );
1193         avl_swl( rootp );
1194         avl_nasty( *rootp );
1195         return True;
1196      }
1197   }
1198   else
1199   if (cmpres < 0) {
1200      /* remove from the right subtree */
1201      AvlNode* right_subtree = (*rootp)->right;
1202      assert(right_subtree);
1203      ch = avl_remove_wrk(&right_subtree, a, kCmp);
1204      (*rootp)->right = right_subtree;
1205      if (ch) {
1206         switch ((*rootp)->balance--) {
1207            case  1: return True;
1208            case  0: return False;
1209            case -1: break;
1210            default: assert(0);
1211         }
1212         switch ((*rootp)->left->balance) {
1213            case 0:
1214               avl_swr( rootp );
1215               (*rootp)->balance = 1;
1216               (*rootp)->right->balance = -1;
1217               return False;
1218            case -1:
1219               avl_swr( rootp );
1220               (*rootp)->balance = 0;
1221               (*rootp)->right->balance = 0;
1222               return True;
1223            case 1:
1224               break;
1225            default:
1226               assert(0);
1227         }
1228         avl_swl( &((*rootp)->left) );
1229         avl_swr( rootp );
1230         avl_nasty( *rootp );
1231         return True;
1232      }
1233   }
1234   else {
1235      assert(cmpres == 0);
1236      assert((*rootp)==a);
1237      return avl_removeroot_wrk(rootp, kCmp);
1238   }
1239   return 0;
1240}
1241
1242/* Remove the root of the AVL tree *rootp.
1243 * Warning: dumps core if *rootp is empty
1244 */
1245static
1246Bool avl_removeroot_wrk ( AvlNode** rootp,
1247                          Word(*kCmp)(Word,Word) )
1248{
1249   Bool     ch;
1250   AvlNode* a;
1251   if (!(*rootp)->left) {
1252      if (!(*rootp)->right) {
1253         (*rootp) = 0;
1254         return True;
1255      }
1256      (*rootp) = (*rootp)->right;
1257      return True;
1258   }
1259   if (!(*rootp)->right) {
1260      (*rootp) = (*rootp)->left;
1261      return True;
1262   }
1263   if ((*rootp)->balance < 0) {
1264      /* remove from the left subtree */
1265      a = (*rootp)->left;
1266      while (a->right) a = a->right;
1267   } else {
1268      /* remove from the right subtree */
1269      a = (*rootp)->right;
1270      while (a->left) a = a->left;
1271   }
1272   ch = avl_remove_wrk(rootp, a, kCmp);
1273   a->left    = (*rootp)->left;
1274   a->right   = (*rootp)->right;
1275   a->balance = (*rootp)->balance;
1276   (*rootp)   = a;
1277   if(a->balance == 0) return ch;
1278   return False;
1279}
1280
1281static
1282AvlNode* avl_find_node ( AvlNode* t, Word k, Word(*kCmp)(Word,Word) )
1283{
1284   Word cmpres;
1285   while (True) {
1286      if (t == NULL) return NULL;
1287      cmpres = kCmp(t->key, k);
1288      if (cmpres > 0) t = t->left;  else
1289      if (cmpres < 0) t = t->right; else
1290      return t;
1291   }
1292}
1293
1294// Clear the iterator stack.
1295static void stackClear(WordFM* fm)
1296{
1297   Int i;
1298   assert(fm);
1299   for (i = 0; i < WFM_STKMAX; i++) {
1300      fm->nodeStack[i] = NULL;
1301      fm->numStack[i]  = 0;
1302   }
1303   fm->stackTop = 0;
1304}
1305
1306// Push onto the iterator stack.
1307static inline void stackPush(WordFM* fm, AvlNode* n, Int i)
1308{
1309   assert(fm->stackTop < WFM_STKMAX);
1310   assert(1 <= i && i <= 3);
1311   fm->nodeStack[fm->stackTop] = n;
1312   fm-> numStack[fm->stackTop] = i;
1313   fm->stackTop++;
1314}
1315
1316// Pop from the iterator stack.
1317static inline Bool stackPop(WordFM* fm, AvlNode** n, Int* i)
1318{
1319   assert(fm->stackTop <= WFM_STKMAX);
1320
1321   if (fm->stackTop > 0) {
1322      fm->stackTop--;
1323      *n = fm->nodeStack[fm->stackTop];
1324      *i = fm-> numStack[fm->stackTop];
1325      assert(1 <= *i && *i <= 3);
1326      fm->nodeStack[fm->stackTop] = NULL;
1327      fm-> numStack[fm->stackTop] = 0;
1328      return True;
1329   } else {
1330      return False;
1331   }
1332}
1333
1334static
1335AvlNode* avl_dopy ( AvlNode* nd,
1336                    Word(*dopyK)(Word),
1337                    Word(*dopyV)(Word),
1338                    void*(alloc_nofail)(SizeT) )
1339{
1340   AvlNode* nyu;
1341   if (! nd)
1342      return NULL;
1343   nyu = alloc_nofail(sizeof(AvlNode));
1344   assert(nyu);
1345
1346   nyu->left = nd->left;
1347   nyu->right = nd->right;
1348   nyu->balance = nd->balance;
1349
1350   /* Copy key */
1351   if (dopyK) {
1352      nyu->key = dopyK( nd->key );
1353      if (nd->key != 0 && nyu->key == 0)
1354         return NULL; /* oom in key dcopy */
1355   } else {
1356      /* copying assumedly unboxed keys */
1357      nyu->key = nd->key;
1358   }
1359
1360   /* Copy val */
1361   if (dopyV) {
1362      nyu->val = dopyV( nd->val );
1363      if (nd->val != 0 && nyu->val == 0)
1364         return NULL; /* oom in val dcopy */
1365   } else {
1366      /* copying assumedly unboxed vals */
1367      nyu->val = nd->val;
1368   }
1369
1370   /* Copy subtrees */
1371   if (nyu->left) {
1372      nyu->left = avl_dopy( nyu->left, dopyK, dopyV, alloc_nofail );
1373      if (! nyu->left)
1374         return NULL;
1375   }
1376   if (nyu->right) {
1377      nyu->right = avl_dopy( nyu->right, dopyK, dopyV, alloc_nofail );
1378      if (! nyu->right)
1379         return NULL;
1380   }
1381
1382   return nyu;
1383}
1384
1385/* --- Public interface functions --- */
1386
1387/* Initialise a WordFM. */
1388void initFM ( WordFM* fm,
1389              void*   (*alloc_nofail)( SizeT ),
1390              void    (*dealloc)(void*),
1391              Word    (*kCmp)(Word,Word) )
1392{
1393   fm->root         = 0;
1394   fm->kCmp         = kCmp;
1395   fm->alloc_nofail = alloc_nofail;
1396   fm->dealloc      = dealloc;
1397   fm->stackTop     = 0;
1398}
1399
1400/* Allocate and Initialise a WordFM. */
1401WordFM* newFM( void* (*alloc_nofail)( SizeT ),
1402               void  (*dealloc)(void*),
1403               Word  (*kCmp)(Word,Word) )
1404{
1405   WordFM* fm = alloc_nofail(sizeof(WordFM));
1406   assert(fm);
1407   initFM(fm, alloc_nofail, dealloc, kCmp);
1408   return fm;
1409}
1410
1411static void avl_free ( AvlNode* nd,
1412                       void(*kFin)(Word),
1413                       void(*vFin)(Word),
1414                       void(*dealloc)(void*) )
1415{
1416   if (!nd)
1417      return;
1418   if (nd->left)
1419      avl_free(nd->left, kFin, vFin, dealloc);
1420   if (nd->right)
1421      avl_free(nd->right, kFin, vFin, dealloc);
1422   if (kFin)
1423      kFin( nd->key );
1424   if (vFin)
1425      vFin( nd->val );
1426   memset(nd, 0, sizeof(AvlNode));
1427   dealloc(nd);
1428}
1429
1430/* Free up the FM.  If kFin is non-NULL, it is applied to keys
1431   before the FM is deleted; ditto with vFin for vals. */
1432void deleteFM ( WordFM* fm, void(*kFin)(Word), void(*vFin)(Word) )
1433{
1434   void(*dealloc)(void*) = fm->dealloc;
1435   avl_free( fm->root, kFin, vFin, dealloc );
1436   memset(fm, 0, sizeof(WordFM) );
1437   dealloc(fm);
1438}
1439
1440/* Add (k,v) to fm. */
1441void addToFM ( WordFM* fm, Word k, Word v )
1442{
1443   MaybeWord oldV;
1444   AvlNode* node;
1445   node = fm->alloc_nofail( sizeof(struct _AvlNode) );
1446   node->key = k;
1447   node->val = v;
1448   oldV.b = False;
1449   oldV.w = 0;
1450   avl_insert_wrk( &fm->root, &oldV, node, fm->kCmp );
1451   //if (oldV.b && fm->vFin)
1452   //   fm->vFin( oldV.w );
1453   if (oldV.b)
1454      free(node);
1455}
1456
1457// Delete key from fm, returning associated val if found
1458Bool delFromFM ( WordFM* fm, /*OUT*/Word* oldV, Word key )
1459{
1460   AvlNode* node = avl_find_node( fm->root, key, fm->kCmp );
1461   if (node) {
1462      avl_remove_wrk( &fm->root, node, fm->kCmp );
1463      if (oldV)
1464         *oldV = node->val;
1465      fm->dealloc(node);
1466      return True;
1467   } else {
1468      return False;
1469   }
1470}
1471
1472// Look up in fm, assigning found val at spec'd address
1473Bool lookupFM ( WordFM* fm, /*OUT*/Word* valP, Word key )
1474{
1475   AvlNode* node = avl_find_node( fm->root, key, fm->kCmp );
1476   if (node) {
1477      if (valP)
1478         *valP = node->val;
1479      return True;
1480   } else {
1481      return False;
1482   }
1483}
1484
1485Word sizeFM ( WordFM* fm )
1486{
1487   // Hmm, this is a bad way to do this
1488   return fm->root ? size_avl_nonNull( fm->root ) : 0;
1489}
1490
1491// set up FM for iteration
1492void initIterFM ( WordFM* fm )
1493{
1494   assert(fm);
1495   stackClear(fm);
1496   if (fm->root)
1497      stackPush(fm, fm->root, 1);
1498}
1499
1500// get next key/val pair.  Will assert if fm has been modified
1501// or looked up in since initIterFM was called.
1502Bool nextIterFM ( WordFM* fm, /*OUT*/Word* pKey, /*OUT*/Word* pVal )
1503{
1504   Int i = 0;
1505   AvlNode* n = NULL;
1506
1507   assert(fm);
1508
1509   // This in-order traversal requires each node to be pushed and popped
1510   // three times.  These could be avoided by updating nodes in-situ on the
1511   // top of the stack, but the push/pop cost is so small that it's worth
1512   // keeping this loop in this simpler form.
1513   while (stackPop(fm, &n, &i)) {
1514      switch (i) {
1515      case 1:
1516         stackPush(fm, n, 2);
1517         if (n->left)  stackPush(fm, n->left, 1);
1518         break;
1519      case 2:
1520         stackPush(fm, n, 3);
1521         if (pKey) *pKey = n->key;
1522         if (pVal) *pVal = n->val;
1523         return True;
1524      case 3:
1525         if (n->right) stackPush(fm, n->right, 1);
1526         break;
1527      default:
1528         assert(0);
1529      }
1530   }
1531
1532   // Stack empty, iterator is exhausted, return NULL
1533   return False;
1534}
1535
1536// clear the I'm iterating flag
1537void doneIterFM ( WordFM* fm )
1538{
1539}
1540
1541WordFM* dopyFM ( WordFM* fm, Word(*dopyK)(Word), Word(*dopyV)(Word) )
1542{
1543   WordFM* nyu;
1544
1545   /* can't clone the fm whilst iterating on it */
1546   assert(fm->stackTop == 0);
1547
1548   nyu = fm->alloc_nofail( sizeof(WordFM) );
1549   assert(nyu);
1550
1551   *nyu = *fm;
1552
1553   fm->stackTop = 0;
1554   memset(fm->nodeStack, 0, sizeof(fm->nodeStack));
1555   memset(fm->numStack, 0,  sizeof(fm->numStack));
1556
1557   if (nyu->root) {
1558      nyu->root = avl_dopy( nyu->root, dopyK, dopyV, fm->alloc_nofail );
1559      if (! nyu->root)
1560         return NULL;
1561   }
1562
1563   return nyu;
1564}
1565
1566//------------------------------------------------------------------//
1567//---                         end WordFM                         ---//
1568//---                       Implementation                       ---//
1569//------------------------------------------------------------------//
1570
1571/*--------------------------------------------------------------------*/
1572/*--- end                                               cg_merge.c ---*/
1573/*--------------------------------------------------------------------*/
1574