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