1
2//--------------------------------------------------------------------*/
3//--- DHAT: a Dynamic Heap Analysis Tool                 dh_main.c ---*/
4//--------------------------------------------------------------------*/
5
6/*
7   This file is part of DHAT, a Valgrind tool for profiling the
8   heap usage of programs.
9
10   Copyright (C) 2010-2013 Mozilla Inc
11
12   This program is free software; you can redistribute it and/or
13   modify it under the terms of the GNU General Public License as
14   published by the Free Software Foundation; either version 2 of the
15   License, or (at your option) any later version.
16
17   This program is distributed in the hope that it will be useful, but
18   WITHOUT ANY WARRANTY; without even the implied warranty of
19   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20   General Public License for more details.
21
22   You should have received a copy of the GNU General Public License
23   along with this program; if not, write to the Free Software
24   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
25   02111-1307, USA.
26
27   The GNU General Public License is contained in the file COPYING.
28*/
29
30/* Contributed by Julian Seward <jseward@acm.org> */
31
32
33#include "pub_tool_basics.h"
34#include "pub_tool_libcbase.h"
35#include "pub_tool_libcassert.h"
36#include "pub_tool_libcprint.h"
37#include "pub_tool_machine.h"      // VG_(fnptr_to_fnentry)
38#include "pub_tool_mallocfree.h"
39#include "pub_tool_options.h"
40#include "pub_tool_replacemalloc.h"
41#include "pub_tool_tooliface.h"
42#include "pub_tool_wordfm.h"
43
44#define HISTOGRAM_SIZE_LIMIT 1024
45
46
47//------------------------------------------------------------//
48//--- Globals                                              ---//
49//------------------------------------------------------------//
50
51// Number of guest instructions executed so far.  This is
52// incremented directly from the generated code.
53static ULong g_guest_instrs_executed = 0;
54
55// Summary statistics for the entire run.
56static ULong g_tot_blocks = 0;   // total blocks allocated
57static ULong g_tot_bytes  = 0;   // total bytes allocated
58
59static ULong g_cur_blocks_live = 0; // curr # blocks live
60static ULong g_cur_bytes_live  = 0; // curr # bytes live
61
62static ULong g_max_blocks_live = 0; // bytes and blocks at
63static ULong g_max_bytes_live  = 0; // the max residency point
64
65
66//------------------------------------------------------------//
67//--- an Interval Tree of live blocks                      ---//
68//------------------------------------------------------------//
69
70/* Tracks information about live blocks. */
71typedef
72   struct {
73      Addr        payload;
74      SizeT       req_szB;
75      ExeContext* ap;  /* allocation ec */
76      ULong       allocd_at; /* instruction number */
77      ULong       n_reads;
78      ULong       n_writes;
79      /* Approx histogram, one byte per payload byte.  Counts latch up
80         therefore at 0xFFFF.  Can be NULL if the block is resized or if
81         the block is larger than HISTOGRAM_SIZE_LIMIT. */
82      UShort*     histoW; /* [0 .. req_szB-1] */
83   }
84   Block;
85
86/* May not contain zero-sized blocks.  May not contain
87   overlapping blocks. */
88static WordFM* interval_tree = NULL;  /* WordFM* Block* void */
89
90/* Here's the comparison function.  Since the tree is required
91to contain non-zero sized, non-overlapping blocks, it's good
92enough to consider any overlap as a match. */
93static Word interval_tree_Cmp ( UWord k1, UWord k2 )
94{
95   Block* b1 = (Block*)k1;
96   Block* b2 = (Block*)k2;
97   tl_assert(b1->req_szB > 0);
98   tl_assert(b2->req_szB > 0);
99   if (b1->payload + b1->req_szB <= b2->payload) return -1;
100   if (b2->payload + b2->req_szB <= b1->payload) return  1;
101   return 0;
102}
103
104// 2-entry cache for find_Block_containing
105static Block* fbc_cache0 = NULL;
106static Block* fbc_cache1 = NULL;
107
108static UWord stats__n_fBc_cached = 0;
109static UWord stats__n_fBc_uncached = 0;
110static UWord stats__n_fBc_notfound = 0;
111
112static Block* find_Block_containing ( Addr a )
113{
114   if (LIKELY(fbc_cache0
115              && fbc_cache0->payload <= a
116              && a < fbc_cache0->payload + fbc_cache0->req_szB)) {
117      // found at 0
118      stats__n_fBc_cached++;
119      return fbc_cache0;
120   }
121   if (LIKELY(fbc_cache1
122              && fbc_cache1->payload <= a
123              && a < fbc_cache1->payload + fbc_cache1->req_szB)) {
124      // found at 1; swap 0 and 1
125      Block* tmp = fbc_cache0;
126      fbc_cache0 = fbc_cache1;
127      fbc_cache1 = tmp;
128      stats__n_fBc_cached++;
129      return fbc_cache0;
130   }
131   Block fake;
132   fake.payload = a;
133   fake.req_szB = 1;
134   UWord foundkey = 1;
135   UWord foundval = 1;
136   Bool found = VG_(lookupFM)( interval_tree,
137                               &foundkey, &foundval, (UWord)&fake );
138   if (!found) {
139      stats__n_fBc_notfound++;
140      return NULL;
141   }
142   tl_assert(foundval == 0); // we don't store vals in the interval tree
143   tl_assert(foundkey != 1);
144   Block* res = (Block*)foundkey;
145   tl_assert(res != &fake);
146   // put at the top position
147   fbc_cache1 = fbc_cache0;
148   fbc_cache0 = res;
149   stats__n_fBc_uncached++;
150   return res;
151}
152
153// delete a block; asserts if not found.  (viz, 'a' must be
154// known to be present.)
155static void delete_Block_starting_at ( Addr a )
156{
157   Block fake;
158   fake.payload = a;
159   fake.req_szB = 1;
160   Bool found = VG_(delFromFM)( interval_tree,
161                                NULL, NULL, (Addr)&fake );
162   tl_assert(found);
163   fbc_cache0 = fbc_cache1 = NULL;
164}
165
166
167//------------------------------------------------------------//
168//--- a FM of allocation points (APs)                      ---//
169//------------------------------------------------------------//
170
171typedef
172   struct {
173      // the allocation point that we're summarising stats for
174      ExeContext* ap;
175      // used when printing results
176      Bool shown;
177      // The current number of blocks and bytes live for this AP
178      ULong cur_blocks_live;
179      ULong cur_bytes_live;
180      // The number of blocks and bytes live at the max-liveness
181      // point.  Note this is a bit subtle.  max_blocks_live is not
182      // the maximum number of live blocks, but rather the number of
183      // blocks live at the point of maximum byte liveness.  These are
184      // not necessarily the same thing.
185      ULong max_blocks_live;
186      ULong max_bytes_live;
187      // Total number of blocks and bytes allocated by this AP.
188      ULong tot_blocks;
189      ULong tot_bytes;
190      // Sum of death ages for all blocks allocated by this AP,
191      // that have subsequently been freed.
192      ULong death_ages_sum;
193      ULong deaths;
194      // Total number of reads and writes in all blocks allocated
195      // by this AP.
196      ULong n_reads;
197      ULong n_writes;
198      /* Histogram information.  We maintain a histogram aggregated for
199         all retiring Blocks allocated by this AP, but only if:
200         - this AP has only ever allocated objects of one size
201         - that size is <= HISTOGRAM_SIZE_LIMIT
202         What we need therefore is a mechanism to see if this AP
203         has only ever allocated blocks of one size.
204
205         3 states:
206            Unknown          because no retirement yet
207            Exactly xsize    all retiring blocks are of this size
208            Mixed            multiple different sizes seen
209      */
210      enum { Unknown=999, Exactly, Mixed } xsize_tag;
211      SizeT xsize;
212      UInt* histo; /* [0 .. xsize-1] */
213   }
214   APInfo;
215
216/* maps ExeContext*'s to APInfo*'s.  Note that the keys must match the
217   .ap field in the values. */
218static WordFM* apinfo = NULL;  /* WordFM* ExeContext* APInfo* */
219
220
221/* 'bk' is being introduced (has just been allocated).  Find the
222   relevant APInfo entry for it, or create one, based on the block's
223   allocation EC.  Then, update the APInfo to the extent that we
224   actually can, to reflect the allocation. */
225static void intro_Block ( Block* bk )
226{
227   tl_assert(bk);
228   tl_assert(bk->ap);
229
230   APInfo* api   = NULL;
231   UWord   keyW  = 0;
232   UWord   valW  = 0;
233   Bool    found = VG_(lookupFM)( apinfo,
234                                  &keyW, &valW, (UWord)bk->ap );
235   if (found) {
236      api = (APInfo*)valW;
237      tl_assert(keyW == (UWord)bk->ap);
238   } else {
239      api = VG_(malloc)( "dh.main.intro_Block.1", sizeof(APInfo) );
240      VG_(memset)(api, 0, sizeof(*api));
241      api->ap = bk->ap;
242      Bool present = VG_(addToFM)( apinfo,
243                                   (UWord)bk->ap, (UWord)api );
244      tl_assert(!present);
245      // histo stuff
246      tl_assert(api->deaths == 0);
247      api->xsize_tag = Unknown;
248      api->xsize = 0;
249      if (0) VG_(printf)("api %p   -->  Unknown\n", api);
250   }
251
252   tl_assert(api->ap == bk->ap);
253
254   /* So: update stats to reflect an allocation */
255
256   // # live blocks
257   api->cur_blocks_live++;
258
259   // # live bytes
260   api->cur_bytes_live += bk->req_szB;
261   if (api->cur_bytes_live > api->max_bytes_live) {
262      api->max_bytes_live  = api->cur_bytes_live;
263      api->max_blocks_live = api->cur_blocks_live;
264   }
265
266   // total blocks and bytes allocated here
267   api->tot_blocks++;
268   api->tot_bytes += bk->req_szB;
269
270   // update summary globals
271   g_tot_blocks++;
272   g_tot_bytes += bk->req_szB;
273
274   g_cur_blocks_live++;
275   g_cur_bytes_live += bk->req_szB;
276   if (g_cur_bytes_live > g_max_bytes_live) {
277      g_max_bytes_live = g_cur_bytes_live;
278      g_max_blocks_live = g_cur_blocks_live;
279   }
280}
281
282
283/* 'bk' is retiring (being freed).  Find the relevant APInfo entry for
284   it, which must already exist.  Then, fold info from 'bk' into that
285   entry.  'because_freed' is True if the block is retiring because
286   the client has freed it.  If it is False then the block is retiring
287   because the program has finished, in which case we want to skip the
288   updates of the total blocks live etc for this AP, but still fold in
289   the access counts and histo data that have so far accumulated for
290   the block. */
291static void retire_Block ( Block* bk, Bool because_freed )
292{
293   tl_assert(bk);
294   tl_assert(bk->ap);
295
296   APInfo* api   = NULL;
297   UWord   keyW  = 0;
298   UWord   valW  = 0;
299   Bool    found = VG_(lookupFM)( apinfo,
300                                  &keyW, &valW, (UWord)bk->ap );
301
302   tl_assert(found);
303   api = (APInfo*)valW;
304   tl_assert(api->ap == bk->ap);
305
306   // update stats following this free.
307   if (0)
308   VG_(printf)("ec %p  api->c_by_l %llu  bk->rszB %llu\n",
309               bk->ap, api->cur_bytes_live, (ULong)bk->req_szB);
310
311   // update total blocks live etc for this AP
312   if (because_freed) {
313      tl_assert(api->cur_blocks_live >= 1);
314      tl_assert(api->cur_bytes_live >= bk->req_szB);
315      api->cur_blocks_live--;
316      api->cur_bytes_live -= bk->req_szB;
317
318      api->deaths++;
319
320      tl_assert(bk->allocd_at <= g_guest_instrs_executed);
321      api->death_ages_sum += (g_guest_instrs_executed - bk->allocd_at);
322
323      // update global summary stats
324      tl_assert(g_cur_blocks_live > 0);
325      g_cur_blocks_live--;
326      tl_assert(g_cur_bytes_live >= bk->req_szB);
327      g_cur_bytes_live -= bk->req_szB;
328   }
329
330   // access counts
331   api->n_reads  += bk->n_reads;
332   api->n_writes += bk->n_writes;
333
334   // histo stuff.  First, do state transitions for xsize/xsize_tag.
335   switch (api->xsize_tag) {
336
337      case Unknown:
338         tl_assert(api->xsize == 0);
339         tl_assert(api->deaths == 1 || api->deaths == 0);
340         tl_assert(!api->histo);
341         api->xsize_tag = Exactly;
342         api->xsize = bk->req_szB;
343         if (0) VG_(printf)("api %p   -->  Exactly(%lu)\n", api, api->xsize);
344         // and allocate the histo
345         if (bk->histoW) {
346            api->histo = VG_(malloc)("dh.main.retire_Block.1",
347                                     api->xsize * sizeof(UInt));
348            VG_(memset)(api->histo, 0, api->xsize * sizeof(UInt));
349         }
350         break;
351
352      case Exactly:
353         //tl_assert(api->deaths > 1);
354         if (bk->req_szB != api->xsize) {
355            if (0) VG_(printf)("api %p   -->  Mixed(%lu -> %lu)\n",
356                               api, api->xsize, bk->req_szB);
357            api->xsize_tag = Mixed;
358            api->xsize = 0;
359            // deallocate the histo, if any
360            if (api->histo) {
361               VG_(free)(api->histo);
362               api->histo = NULL;
363            }
364         }
365         break;
366
367      case Mixed:
368         //tl_assert(api->deaths > 1);
369         break;
370
371      default:
372        tl_assert(0);
373   }
374
375   // See if we can fold the histo data from this block into
376   // the data for the AP
377   if (api->xsize_tag == Exactly && api->histo && bk->histoW) {
378      tl_assert(api->xsize == bk->req_szB);
379      UWord i;
380      for (i = 0; i < api->xsize; i++) {
381         // FIXME: do something better in case of overflow of api->histo[..]
382         // Right now, at least don't let it overflow/wrap around
383         if (api->histo[i] <= 0xFFFE0000)
384            api->histo[i] += (UInt)bk->histoW[i];
385      }
386      if (0) VG_(printf)("fold in, AP = %p\n", api);
387   }
388
389
390
391#if 0
392   if (bk->histoB) {
393      VG_(printf)("block retiring, histo %lu: ", bk->req_szB);
394      UWord i;
395      for (i = 0; i < bk->req_szB; i++)
396        VG_(printf)("%u ", (UInt)bk->histoB[i]);
397      VG_(printf)("\n");
398   } else {
399      VG_(printf)("block retiring, no histo %lu\n", bk->req_szB);
400   }
401#endif
402}
403
404/* This handles block resizing.  When a block with AP 'ec' has a
405   size change of 'delta', call here to update the APInfo. */
406static void apinfo_change_cur_bytes_live( ExeContext* ec, Long delta )
407{
408   APInfo* api   = NULL;
409   UWord   keyW  = 0;
410   UWord   valW  = 0;
411   Bool    found = VG_(lookupFM)( apinfo,
412                                  &keyW, &valW, (UWord)ec );
413
414   tl_assert(found);
415   api = (APInfo*)valW;
416   tl_assert(api->ap == ec);
417
418   if (delta < 0) {
419      tl_assert(api->cur_bytes_live >= -delta);
420      tl_assert(g_cur_bytes_live >= -delta);
421   }
422
423   // adjust current live size
424   api->cur_bytes_live += delta;
425   g_cur_bytes_live += delta;
426
427   if (delta > 0 && api->cur_bytes_live > api->max_bytes_live) {
428      api->max_bytes_live  = api->cur_bytes_live;
429      api->max_blocks_live = api->cur_blocks_live;
430   }
431
432   // update global summary stats
433   if (delta > 0 && g_cur_bytes_live > g_max_bytes_live) {
434      g_max_bytes_live = g_cur_bytes_live;
435      g_max_blocks_live = g_cur_blocks_live;
436   }
437   if (delta > 0)
438      g_tot_bytes += delta;
439
440   // adjust total allocation size
441   if (delta > 0)
442      api->tot_bytes += delta;
443}
444
445
446//------------------------------------------------------------//
447//--- update both Block and APInfos after {m,re}alloc/free ---//
448//------------------------------------------------------------//
449
450static
451void* new_block ( ThreadId tid, void* p, SizeT req_szB, SizeT req_alignB,
452                  Bool is_zeroed )
453{
454   tl_assert(p == NULL); // don't handle custom allocators right now
455   SizeT actual_szB /*, slop_szB*/;
456
457   if ((SSizeT)req_szB < 0) return NULL;
458
459   if (req_szB == 0)
460      req_szB = 1;  /* can't allow zero-sized blocks in the interval tree */
461
462   // Allocate and zero if necessary
463   if (!p) {
464      p = VG_(cli_malloc)( req_alignB, req_szB );
465      if (!p) {
466         return NULL;
467      }
468      if (is_zeroed) VG_(memset)(p, 0, req_szB);
469      actual_szB = VG_(cli_malloc_usable_size)(p);
470      tl_assert(actual_szB >= req_szB);
471      /* slop_szB = actual_szB - req_szB; */
472   } else {
473      /* slop_szB = 0; */
474   }
475
476   // Make new HP_Chunk node, add to malloc_list
477   Block* bk = VG_(malloc)("dh.new_block.1", sizeof(Block));
478   bk->payload   = (Addr)p;
479   bk->req_szB   = req_szB;
480   bk->ap        = VG_(record_ExeContext)(tid, 0/*first word delta*/);
481   bk->allocd_at = g_guest_instrs_executed;
482   bk->n_reads   = 0;
483   bk->n_writes  = 0;
484   // set up histogram array, if the block isn't too large
485   bk->histoW = NULL;
486   if (req_szB <= HISTOGRAM_SIZE_LIMIT) {
487      bk->histoW = VG_(malloc)("dh.new_block.2", req_szB * sizeof(UShort));
488      VG_(memset)(bk->histoW, 0, req_szB * sizeof(UShort));
489   }
490
491   Bool present = VG_(addToFM)( interval_tree, (UWord)bk, (UWord)0/*no val*/);
492   tl_assert(!present);
493   fbc_cache0 = fbc_cache1 = NULL;
494
495   intro_Block(bk);
496
497   if (0) VG_(printf)("ALLOC %ld -> %p\n", req_szB, p);
498
499   return p;
500}
501
502static
503void die_block ( void* p, Bool custom_free )
504{
505   tl_assert(!custom_free);  // at least for now
506
507   Block* bk = find_Block_containing( (Addr)p );
508
509   if (!bk) {
510     return; // bogus free
511   }
512
513   tl_assert(bk->req_szB > 0);
514   // assert the block finder is behaving sanely
515   tl_assert(bk->payload <= (Addr)p);
516   tl_assert( (Addr)p < bk->payload + bk->req_szB );
517
518   if (bk->payload != (Addr)p) {
519      return; // bogus free
520   }
521
522   if (0) VG_(printf)(" FREE %p %llu\n",
523                      p, g_guest_instrs_executed - bk->allocd_at);
524
525   retire_Block(bk, True/*because_freed*/);
526
527   VG_(cli_free)( (void*)bk->payload );
528   delete_Block_starting_at( bk->payload );
529   if (bk->histoW) {
530      VG_(free)( bk->histoW );
531      bk->histoW = NULL;
532   }
533   VG_(free)( bk );
534}
535
536
537static
538void* renew_block ( ThreadId tid, void* p_old, SizeT new_req_szB )
539{
540   if (0) VG_(printf)("REALL %p %ld\n", p_old, new_req_szB);
541   void* p_new = NULL;
542
543   tl_assert(new_req_szB > 0); // map 0 to 1
544
545   // Find the old block.
546   Block* bk = find_Block_containing( (Addr)p_old );
547   if (!bk) {
548      return NULL;   // bogus realloc
549   }
550
551   tl_assert(bk->req_szB > 0);
552   // assert the block finder is behaving sanely
553   tl_assert(bk->payload <= (Addr)p_old);
554   tl_assert( (Addr)p_old < bk->payload + bk->req_szB );
555
556   if (bk->payload != (Addr)p_old) {
557      return NULL; // bogus realloc
558   }
559
560   // Keeping the histogram alive in any meaningful way across
561   // block resizing is too darn complicated.  Just throw it away.
562   if (bk->histoW) {
563      VG_(free)(bk->histoW);
564      bk->histoW = NULL;
565   }
566
567   // Actually do the allocation, if necessary.
568   if (new_req_szB <= bk->req_szB) {
569
570      // New size is smaller or same; block not moved.
571      apinfo_change_cur_bytes_live(bk->ap,
572                                   (Long)new_req_szB - (Long)bk->req_szB);
573      bk->req_szB = new_req_szB;
574      return p_old;
575
576   } else {
577
578      // New size is bigger;  make new block, copy shared contents, free old.
579      p_new = VG_(cli_malloc)(VG_(clo_alignment), new_req_szB);
580      if (!p_new) {
581         // Nb: if realloc fails, NULL is returned but the old block is not
582         // touched.  What an awful function.
583         return NULL;
584      }
585      tl_assert(p_new != p_old);
586
587      VG_(memcpy)(p_new, p_old, bk->req_szB);
588      VG_(cli_free)(p_old);
589
590      // Since the block has moved, we need to re-insert it into the
591      // interval tree at the new place.  Do this by removing
592      // and re-adding it.
593      delete_Block_starting_at( (Addr)p_old );
594      // now 'bk' is no longer in the tree, but the Block itself
595      // is still alive
596
597      // Update the metadata.
598      apinfo_change_cur_bytes_live(bk->ap,
599                                   (Long)new_req_szB - (Long)bk->req_szB);
600      bk->payload = (Addr)p_new;
601      bk->req_szB = new_req_szB;
602
603      // and re-add
604      Bool present
605         = VG_(addToFM)( interval_tree, (UWord)bk, (UWord)0/*no val*/);
606      tl_assert(!present);
607      fbc_cache0 = fbc_cache1 = NULL;
608
609      return p_new;
610   }
611   /*NOTREACHED*/
612   tl_assert(0);
613}
614
615
616//------------------------------------------------------------//
617//--- malloc() et al replacement wrappers                  ---//
618//------------------------------------------------------------//
619
620static void* dh_malloc ( ThreadId tid, SizeT szB )
621{
622   return new_block( tid, NULL, szB, VG_(clo_alignment), /*is_zeroed*/False );
623}
624
625static void* dh___builtin_new ( ThreadId tid, SizeT szB )
626{
627   return new_block( tid, NULL, szB, VG_(clo_alignment), /*is_zeroed*/False );
628}
629
630static void* dh___builtin_vec_new ( ThreadId tid, SizeT szB )
631{
632   return new_block( tid, NULL, szB, VG_(clo_alignment), /*is_zeroed*/False );
633}
634
635static void* dh_calloc ( ThreadId tid, SizeT m, SizeT szB )
636{
637   return new_block( tid, NULL, m*szB, VG_(clo_alignment), /*is_zeroed*/True );
638}
639
640static void *dh_memalign ( ThreadId tid, SizeT alignB, SizeT szB )
641{
642   return new_block( tid, NULL, szB, alignB, False );
643}
644
645static void dh_free ( ThreadId tid __attribute__((unused)), void* p )
646{
647   die_block( p, /*custom_free*/False );
648}
649
650static void dh___builtin_delete ( ThreadId tid, void* p )
651{
652   die_block( p, /*custom_free*/False);
653}
654
655static void dh___builtin_vec_delete ( ThreadId tid, void* p )
656{
657   die_block( p, /*custom_free*/False );
658}
659
660static void* dh_realloc ( ThreadId tid, void* p_old, SizeT new_szB )
661{
662   if (p_old == NULL) {
663      return dh_malloc(tid, new_szB);
664   }
665   if (new_szB == 0) {
666      dh_free(tid, p_old);
667      return NULL;
668   }
669   return renew_block(tid, p_old, new_szB);
670}
671
672static SizeT dh_malloc_usable_size ( ThreadId tid, void* p )
673{
674   Block* bk = find_Block_containing( (Addr)p );
675   return bk ? bk->req_szB : 0;
676}
677
678
679//------------------------------------------------------------//
680//--- memory references                                    ---//
681//------------------------------------------------------------//
682
683static
684void inc_histo_for_block ( Block* bk, Addr addr, UWord szB )
685{
686   UWord i, offMin, offMax1;
687   offMin = addr - bk->payload;
688   tl_assert(offMin < bk->req_szB);
689   offMax1 = offMin + szB;
690   if (offMax1 > bk->req_szB)
691      offMax1 = bk->req_szB;
692   //VG_(printf)("%lu %lu   (size of block %lu)\n", offMin, offMax1, bk->req_szB);
693   for (i = offMin; i < offMax1; i++) {
694      UShort n = bk->histoW[i];
695      if (n < 0xFFFF) n++;
696      bk->histoW[i] = n;
697   }
698}
699
700static VG_REGPARM(2)
701void dh_handle_write ( Addr addr, UWord szB )
702{
703   Block* bk = find_Block_containing(addr);
704   if (bk) {
705      bk->n_writes += szB;
706      if (bk->histoW)
707         inc_histo_for_block(bk, addr, szB);
708   }
709}
710
711static VG_REGPARM(2)
712void dh_handle_read ( Addr addr, UWord szB )
713{
714   Block* bk = find_Block_containing(addr);
715   if (bk) {
716      bk->n_reads += szB;
717      if (bk->histoW)
718         inc_histo_for_block(bk, addr, szB);
719   }
720}
721
722
723// Handle reads and writes by syscalls (read == kernel
724// reads user space, write == kernel writes user space).
725// Assumes no such read or write spans a heap block
726// boundary and so we can treat it just as one giant
727// read or write.
728static
729void dh_handle_noninsn_read ( CorePart part, ThreadId tid, const HChar* s,
730                              Addr base, SizeT size )
731{
732   switch (part) {
733      case Vg_CoreSysCall:
734         dh_handle_read(base, size);
735         break;
736      case Vg_CoreSysCallArgInMem:
737         break;
738      case Vg_CoreTranslate:
739         break;
740      default:
741         tl_assert(0);
742   }
743}
744
745static
746void dh_handle_noninsn_write ( CorePart part, ThreadId tid,
747                               Addr base, SizeT size )
748{
749   switch (part) {
750      case Vg_CoreSysCall:
751         dh_handle_write(base, size);
752         break;
753      case Vg_CoreSignal:
754         break;
755      default:
756         tl_assert(0);
757   }
758}
759
760
761//------------------------------------------------------------//
762//--- Instrumentation                                      ---//
763//------------------------------------------------------------//
764
765#define binop(_op, _arg1, _arg2) IRExpr_Binop((_op),(_arg1),(_arg2))
766#define mkexpr(_tmp)             IRExpr_RdTmp((_tmp))
767#define mkU32(_n)                IRExpr_Const(IRConst_U32(_n))
768#define mkU64(_n)                IRExpr_Const(IRConst_U64(_n))
769#define assign(_t, _e)           IRStmt_WrTmp((_t), (_e))
770
771static
772void add_counter_update(IRSB* sbOut, Int n)
773{
774   #if defined(VG_BIGENDIAN)
775   # define END Iend_BE
776   #elif defined(VG_LITTLEENDIAN)
777   # define END Iend_LE
778   #else
779   # error "Unknown endianness"
780   #endif
781   // Add code to increment 'g_guest_instrs_executed' by 'n', like this:
782   //   WrTmp(t1, Load64(&g_guest_instrs_executed))
783   //   WrTmp(t2, Add64(RdTmp(t1), Const(n)))
784   //   Store(&g_guest_instrs_executed, t2)
785   IRTemp t1 = newIRTemp(sbOut->tyenv, Ity_I64);
786   IRTemp t2 = newIRTemp(sbOut->tyenv, Ity_I64);
787   IRExpr* counter_addr = mkIRExpr_HWord( (HWord)&g_guest_instrs_executed );
788
789   IRStmt* st1 = assign(t1, IRExpr_Load(END, Ity_I64, counter_addr));
790   IRStmt* st2 = assign(t2, binop(Iop_Add64, mkexpr(t1), mkU64(n)));
791   IRStmt* st3 = IRStmt_Store(END, counter_addr, mkexpr(t2));
792
793   addStmtToIRSB( sbOut, st1 );
794   addStmtToIRSB( sbOut, st2 );
795   addStmtToIRSB( sbOut, st3 );
796}
797
798static
799void addMemEvent(IRSB* sbOut, Bool isWrite, Int szB, IRExpr* addr,
800                 Int goff_sp)
801{
802   IRType   tyAddr   = Ity_INVALID;
803   const HChar* hName= NULL;
804   void*    hAddr    = NULL;
805   IRExpr** argv     = NULL;
806   IRDirty* di       = NULL;
807
808   const Int THRESH = 4096 * 4; // somewhat arbitrary
809   const Int rz_szB = VG_STACK_REDZONE_SZB;
810
811   tyAddr = typeOfIRExpr( sbOut->tyenv, addr );
812   tl_assert(tyAddr == Ity_I32 || tyAddr == Ity_I64);
813
814   if (isWrite) {
815      hName = "dh_handle_write";
816      hAddr = &dh_handle_write;
817   } else {
818      hName = "dh_handle_read";
819      hAddr = &dh_handle_read;
820   }
821
822   argv = mkIRExprVec_2( addr, mkIRExpr_HWord(szB) );
823
824   /* Add the helper. */
825   tl_assert(hName);
826   tl_assert(hAddr);
827   tl_assert(argv);
828   di = unsafeIRDirty_0_N( 2/*regparms*/,
829                           hName, VG_(fnptr_to_fnentry)( hAddr ),
830                           argv );
831
832   /* Generate the guard condition: "(addr - (SP - RZ)) >u N", for
833      some arbitrary N.  If that fails then addr is in the range (SP -
834      RZ .. SP + N - RZ).  If N is smallish (a page?) then we can say
835      addr is within a page of SP and so can't possibly be a heap
836      access, and so can be skipped. */
837   IRTemp sp = newIRTemp(sbOut->tyenv, tyAddr);
838   addStmtToIRSB( sbOut, assign(sp, IRExpr_Get(goff_sp, tyAddr)));
839
840   IRTemp sp_minus_rz = newIRTemp(sbOut->tyenv, tyAddr);
841   addStmtToIRSB(
842      sbOut,
843      assign(sp_minus_rz,
844             tyAddr == Ity_I32
845                ? binop(Iop_Sub32, mkexpr(sp), mkU32(rz_szB))
846                : binop(Iop_Sub64, mkexpr(sp), mkU64(rz_szB)))
847   );
848
849   IRTemp diff = newIRTemp(sbOut->tyenv, tyAddr);
850   addStmtToIRSB(
851      sbOut,
852      assign(diff,
853             tyAddr == Ity_I32
854                ? binop(Iop_Sub32, addr, mkexpr(sp_minus_rz))
855                : binop(Iop_Sub64, addr, mkexpr(sp_minus_rz)))
856   );
857
858   IRTemp guard = newIRTemp(sbOut->tyenv, Ity_I1);
859   addStmtToIRSB(
860      sbOut,
861      assign(guard,
862             tyAddr == Ity_I32
863                ? binop(Iop_CmpLT32U, mkU32(THRESH), mkexpr(diff))
864                : binop(Iop_CmpLT64U, mkU64(THRESH), mkexpr(diff)))
865   );
866   di->guard = mkexpr(guard);
867
868   addStmtToIRSB( sbOut, IRStmt_Dirty(di) );
869}
870
871static
872IRSB* dh_instrument ( VgCallbackClosure* closure,
873                      IRSB* sbIn,
874                      const VexGuestLayout* layout,
875                      const VexGuestExtents* vge,
876                      const VexArchInfo* archinfo_host,
877                      IRType gWordTy, IRType hWordTy )
878{
879   Int   i, n = 0;
880   IRSB* sbOut;
881   IRTypeEnv* tyenv = sbIn->tyenv;
882
883   const Int goff_sp = layout->offset_SP;
884
885   // We increment the instruction count in two places:
886   // - just before any Ist_Exit statements;
887   // - just before the IRSB's end.
888   // In the former case, we zero 'n' and then continue instrumenting.
889
890   sbOut = deepCopyIRSBExceptStmts(sbIn);
891
892   // Copy verbatim any IR preamble preceding the first IMark
893   i = 0;
894   while (i < sbIn->stmts_used && sbIn->stmts[i]->tag != Ist_IMark) {
895      addStmtToIRSB( sbOut, sbIn->stmts[i] );
896      i++;
897   }
898
899   for (/*use current i*/; i < sbIn->stmts_used; i++) {
900      IRStmt* st = sbIn->stmts[i];
901
902      if (!st || st->tag == Ist_NoOp) continue;
903
904      switch (st->tag) {
905
906         case Ist_IMark: {
907            n++;
908            break;
909         }
910
911         case Ist_Exit: {
912            if (n > 0) {
913               // Add an increment before the Exit statement, then reset 'n'.
914               add_counter_update(sbOut, n);
915               n = 0;
916            }
917            break;
918         }
919
920         case Ist_WrTmp: {
921            IRExpr* data = st->Ist.WrTmp.data;
922            if (data->tag == Iex_Load) {
923               IRExpr* aexpr = data->Iex.Load.addr;
924               // Note also, endianness info is ignored.  I guess
925               // that's not interesting.
926               addMemEvent( sbOut, False/*!isWrite*/,
927                            sizeofIRType(data->Iex.Load.ty),
928                            aexpr, goff_sp );
929            }
930            break;
931         }
932
933         case Ist_Store: {
934            IRExpr* data  = st->Ist.Store.data;
935            IRExpr* aexpr = st->Ist.Store.addr;
936            addMemEvent( sbOut, True/*isWrite*/,
937                         sizeofIRType(typeOfIRExpr(tyenv, data)),
938                         aexpr, goff_sp );
939            break;
940         }
941
942         case Ist_Dirty: {
943            Int      dataSize;
944            IRDirty* d = st->Ist.Dirty.details;
945            if (d->mFx != Ifx_None) {
946               /* This dirty helper accesses memory.  Collect the details. */
947               tl_assert(d->mAddr != NULL);
948               tl_assert(d->mSize != 0);
949               dataSize = d->mSize;
950               // Large (eg. 28B, 108B, 512B on x86) data-sized
951               // instructions will be done inaccurately, but they're
952               // very rare and this avoids errors from hitting more
953               // than two cache lines in the simulation.
954               if (d->mFx == Ifx_Read || d->mFx == Ifx_Modify)
955                  addMemEvent( sbOut, False/*!isWrite*/,
956                               dataSize, d->mAddr, goff_sp );
957               if (d->mFx == Ifx_Write || d->mFx == Ifx_Modify)
958                  addMemEvent( sbOut, True/*isWrite*/,
959                               dataSize, d->mAddr, goff_sp );
960            } else {
961               tl_assert(d->mAddr == NULL);
962               tl_assert(d->mSize == 0);
963            }
964            break;
965         }
966
967         case Ist_CAS: {
968            /* We treat it as a read and a write of the location.  I
969               think that is the same behaviour as it was before IRCAS
970               was introduced, since prior to that point, the Vex
971               front ends would translate a lock-prefixed instruction
972               into a (normal) read followed by a (normal) write. */
973            Int    dataSize;
974            IRCAS* cas = st->Ist.CAS.details;
975            tl_assert(cas->addr != NULL);
976            tl_assert(cas->dataLo != NULL);
977            dataSize = sizeofIRType(typeOfIRExpr(tyenv, cas->dataLo));
978            if (cas->dataHi != NULL)
979               dataSize *= 2; /* since it's a doubleword-CAS */
980            addMemEvent( sbOut, False/*!isWrite*/,
981                         dataSize, cas->addr, goff_sp );
982            addMemEvent( sbOut, True/*isWrite*/,
983                         dataSize, cas->addr, goff_sp );
984            break;
985         }
986
987         case Ist_LLSC: {
988            IRType dataTy;
989            if (st->Ist.LLSC.storedata == NULL) {
990               /* LL */
991               dataTy = typeOfIRTemp(tyenv, st->Ist.LLSC.result);
992               addMemEvent( sbOut, False/*!isWrite*/,
993                            sizeofIRType(dataTy),
994                            st->Ist.LLSC.addr, goff_sp );
995            } else {
996               /* SC */
997               dataTy = typeOfIRExpr(tyenv, st->Ist.LLSC.storedata);
998               addMemEvent( sbOut, True/*isWrite*/,
999                            sizeofIRType(dataTy),
1000                            st->Ist.LLSC.addr, goff_sp );
1001            }
1002            break;
1003         }
1004
1005         default:
1006            break;
1007      }
1008
1009      addStmtToIRSB( sbOut, st );
1010   }
1011
1012   if (n > 0) {
1013      // Add an increment before the SB end.
1014      add_counter_update(sbOut, n);
1015   }
1016   return sbOut;
1017}
1018
1019#undef binop
1020#undef mkexpr
1021#undef mkU32
1022#undef mkU64
1023#undef assign
1024
1025
1026//------------------------------------------------------------//
1027//--- Command line args                                    ---//
1028//------------------------------------------------------------//
1029
1030// FORWARDS
1031static Bool identify_metric ( /*OUT*/ULong(**get_metricP)(APInfo*),
1032                              /*OUT*/Bool* increasingP,
1033                              const HChar* metric_name );
1034
1035static Int    clo_show_top_n = 10;
1036static const HChar *clo_sort_by = "max-bytes-live";
1037
1038static Bool dh_process_cmd_line_option(const HChar* arg)
1039{
1040   if VG_BINT_CLO(arg, "--show-top-n", clo_show_top_n, 1, 100000) {}
1041
1042   else if VG_STR_CLO(arg, "--sort-by", clo_sort_by) {
1043       ULong (*dummyFn)(APInfo*);
1044       Bool dummyB;
1045       Bool ok = identify_metric( &dummyFn, &dummyB, clo_sort_by);
1046       if (!ok)
1047          return False;
1048       // otherwise it's OK, in which case leave it alone.
1049       // show_top_n_apinfos will later convert the string by a
1050       // second call to identify_metric.
1051   }
1052
1053   else
1054      return VG_(replacement_malloc_process_cmd_line_option)(arg);
1055
1056   return True;
1057}
1058
1059
1060static void dh_print_usage(void)
1061{
1062   VG_(printf)(
1063"    --show-top-n=number       show the top <number> alloc points [10]\n"
1064"    --sort-by=string\n"
1065"            sort the allocation points by the metric\n"
1066"            defined by <string>, thusly:\n"
1067"                max-bytes-live    maximum live bytes [default]\n"
1068"                tot-bytes-allocd  total allocation (turnover)\n"
1069"                max-blocks-live   maximum live blocks\n"
1070   );
1071}
1072
1073static void dh_print_debug_usage(void)
1074{
1075   VG_(printf)(
1076"    (none)\n"
1077   );
1078}
1079
1080
1081//------------------------------------------------------------//
1082//--- Finalisation                                         ---//
1083//------------------------------------------------------------//
1084
1085static void show_N_div_100( /*OUT*/HChar* buf, ULong n )
1086{
1087   ULong nK = n / 100;
1088   ULong nR = n % 100;
1089   VG_(sprintf)(buf, "%llu.%s%llu", nK,
1090                nR < 10 ? "0" : "",
1091                nR);
1092}
1093
1094static void show_APInfo ( APInfo* api )
1095{
1096   HChar bufA[80];   // large enough
1097   VG_(memset)(bufA, 0, sizeof(bufA));
1098   if (api->tot_blocks > 0) {
1099      show_N_div_100( bufA, ((ULong)api->tot_bytes * 100ULL)
1100                              / (ULong)api->tot_blocks );
1101   } else {
1102      bufA[0] = 'N'; bufA[1] = 'a'; bufA[2] = 'N';
1103   }
1104
1105   VG_(umsg)("max-live:    %'llu in %'llu blocks\n",
1106             api->max_bytes_live, api->max_blocks_live);
1107   VG_(umsg)("tot-alloc:   %'llu in %'llu blocks (avg size %s)\n",
1108             api->tot_bytes, api->tot_blocks, bufA);
1109
1110   tl_assert(api->tot_blocks >= api->max_blocks_live);
1111   tl_assert(api->tot_bytes >= api->max_bytes_live);
1112
1113   if (api->deaths > 0) {
1114      // Average Age at Death
1115      ULong aad = api->deaths == 0
1116                  ? 0 : (api->death_ages_sum / api->deaths);
1117      // AAD as a fraction of the total program lifetime (so far)
1118      // measured in ten-thousand-ths (aad_frac_10k == 10000 means the
1119      // complete lifetime of the program.
1120      ULong aad_frac_10k
1121         = g_guest_instrs_executed == 0
1122           ? 0 : (10000ULL * aad) / g_guest_instrs_executed;
1123      HChar buf[80];  // large enough
1124      show_N_div_100(buf, aad_frac_10k);
1125      VG_(umsg)("deaths:      %'llu, at avg age %'llu "
1126                "(%s%% of prog lifetime)\n",
1127                api->deaths, aad, buf );
1128   } else {
1129      VG_(umsg)("deaths:      none (none of these blocks were freed)\n");
1130   }
1131
1132   HChar bufR[80], bufW[80];   // large enough
1133   VG_(memset)(bufR, 0, sizeof(bufR));
1134   VG_(memset)(bufW, 0, sizeof(bufW));
1135   if (api->tot_bytes > 0) {
1136      show_N_div_100(bufR, (100ULL * api->n_reads) / api->tot_bytes);
1137      show_N_div_100(bufW, (100ULL * api->n_writes) / api->tot_bytes);
1138   } else {
1139      VG_(strcat)(bufR, "Inf");
1140      VG_(strcat)(bufW, "Inf");
1141   }
1142
1143   VG_(umsg)("acc-ratios:  %s rd, %s wr "
1144             " (%'llu b-read, %'llu b-written)\n",
1145             bufR, bufW,
1146             api->n_reads, api->n_writes);
1147
1148   VG_(pp_ExeContext)(api->ap);
1149
1150   if (api->histo && api->xsize_tag == Exactly) {
1151      VG_(umsg)("\nAggregated access counts by offset:\n");
1152      VG_(umsg)("\n");
1153      UWord i;
1154      if (api->xsize > 0)
1155         VG_(umsg)("[   0]  ");
1156      for (i = 0; i < api->xsize; i++) {
1157         if (i > 0 && (i % 16) == 0 && i != api->xsize-1) {
1158            VG_(umsg)("\n");
1159            VG_(umsg)("[%4lu]  ", i);
1160         }
1161         VG_(umsg)("%u ", api->histo[i]);
1162      }
1163      VG_(umsg)("\n");
1164   }
1165}
1166
1167
1168/* Metric-access functions for APInfos. */
1169static ULong get_metric__max_bytes_live ( APInfo* api ) {
1170   return api->max_bytes_live;
1171}
1172static ULong get_metric__tot_bytes ( APInfo* api ) {
1173   return api->tot_bytes;
1174}
1175static ULong get_metric__max_blocks_live ( APInfo* api ) {
1176   return api->max_blocks_live;
1177}
1178
1179/* Given a string, return the metric-access function and also a Bool
1180   indicating whether we want increasing or decreasing values of the
1181   metric.  This is used twice, once in command line processing, and
1182   then again in show_top_n_apinfos.  Returns False if the given
1183   string could not be identified.*/
1184static Bool identify_metric ( /*OUT*/ULong(**get_metricP)(APInfo*),
1185                              /*OUT*/Bool* increasingP,
1186                              const HChar* metric_name )
1187{
1188   if (0 == VG_(strcmp)(metric_name, "max-bytes-live")) {
1189      *get_metricP = get_metric__max_bytes_live;
1190      *increasingP = False;
1191      return True;
1192   }
1193   if (0 == VG_(strcmp)(metric_name, "tot-bytes-allocd")) {
1194      *get_metricP = get_metric__tot_bytes;
1195      *increasingP = False;
1196      return True;
1197   }
1198   if (0 == VG_(strcmp)(metric_name, "max-blocks-live")) {
1199      *get_metricP = get_metric__max_blocks_live;
1200      *increasingP = False;
1201      return True;
1202   }
1203   return False;
1204}
1205
1206
1207static void show_top_n_apinfos ( void )
1208{
1209   Int   i;
1210   UWord keyW, valW;
1211   ULong (*get_metric)(APInfo*);
1212   Bool  increasing;
1213
1214   const HChar* metric_name = clo_sort_by;
1215   tl_assert(metric_name); // ensured by clo processing
1216
1217   Bool ok = identify_metric( &get_metric, &increasing, metric_name );
1218   tl_assert(ok); // ensured by clo processing
1219
1220   VG_(umsg)("\n");
1221   VG_(umsg)("======== ORDERED BY %s \"%s\": "
1222             "top %d allocators ========\n",
1223             increasing ? "increasing" : "decreasing",
1224             metric_name, clo_show_top_n );
1225
1226   // Clear all .shown bits
1227   VG_(initIterFM)( apinfo );
1228   while (VG_(nextIterFM)( apinfo, &keyW, &valW )) {
1229      APInfo* api = (APInfo*)valW;
1230      tl_assert(api && api->ap == (ExeContext*)keyW);
1231      api->shown = False;
1232   }
1233   VG_(doneIterFM)( apinfo );
1234
1235   // Now print the top N entries.  Each one requires a
1236   // complete scan of the set.  Duh.
1237   for (i = 0; i < clo_show_top_n; i++) {
1238      ULong   best_metric = increasing ? ~0ULL : 0ULL;
1239      APInfo* best_api    = NULL;
1240
1241      VG_(initIterFM)( apinfo );
1242      while (VG_(nextIterFM)( apinfo, &keyW, &valW )) {
1243         APInfo* api = (APInfo*)valW;
1244         if (api->shown)
1245            continue;
1246         ULong metric = get_metric(api);
1247         if (increasing ? (metric < best_metric) : (metric > best_metric)) {
1248            best_metric = metric;
1249            best_api = api;
1250         }
1251      }
1252      VG_(doneIterFM)( apinfo );
1253
1254      if (!best_api)
1255         break; // all APIs have been shown.  Stop.
1256
1257      VG_(umsg)("\n");
1258      VG_(umsg)("-------------------- %d of %d --------------------\n",
1259                i+1, clo_show_top_n );
1260      show_APInfo(best_api);
1261      best_api->shown = True;
1262   }
1263
1264   VG_(umsg)("\n");
1265}
1266
1267
1268static void dh_fini(Int exit_status)
1269{
1270   // Before printing statistics, we must harvest access counts for
1271   // all the blocks that are still alive.  Not doing so gives
1272   // access ratios which are too low (zero, in the worst case)
1273   // for such blocks, since the accesses that do get made will
1274   // (if we skip this step) not get folded into the AP summaries.
1275   UWord keyW, valW;
1276   VG_(initIterFM)( interval_tree );
1277   while (VG_(nextIterFM)( interval_tree, &keyW, &valW )) {
1278      Block* bk = (Block*)keyW;
1279      tl_assert(valW == 0);
1280      tl_assert(bk);
1281      retire_Block(bk, False/*!because_freed*/);
1282   }
1283   VG_(doneIterFM)( interval_tree );
1284
1285   // show results
1286   VG_(umsg)("======== SUMMARY STATISTICS ========\n");
1287   VG_(umsg)("\n");
1288   VG_(umsg)("guest_insns:  %'llu\n", g_guest_instrs_executed);
1289   VG_(umsg)("\n");
1290   VG_(umsg)("max_live:     %'llu in %'llu blocks\n",
1291             g_max_bytes_live, g_max_blocks_live);
1292   VG_(umsg)("\n");
1293   VG_(umsg)("tot_alloc:    %'llu in %'llu blocks\n",
1294             g_tot_bytes, g_tot_blocks);
1295   VG_(umsg)("\n");
1296   if (g_tot_bytes > 0) {
1297      VG_(umsg)("insns per allocated byte: %'llu\n",
1298                g_guest_instrs_executed / g_tot_bytes);
1299      VG_(umsg)("\n");
1300   }
1301
1302   show_top_n_apinfos();
1303
1304   VG_(umsg)("\n");
1305   VG_(umsg)("\n");
1306   VG_(umsg)("==============================================================\n");
1307   VG_(umsg)("\n");
1308   VG_(umsg)("Some hints: (see --help for command line option details):\n");
1309   VG_(umsg)("\n");
1310   VG_(umsg)("* summary stats for whole program are at the top of this output\n");
1311   VG_(umsg)("\n");
1312   VG_(umsg)("* --show-top-n=  controls how many alloc points are shown.\n");
1313   VG_(umsg)("                 You probably want to set it much higher than\n");
1314   VG_(umsg)("                 the default value (10)\n");
1315   VG_(umsg)("\n");
1316   VG_(umsg)("* --sort-by=     specifies the sort key for output.\n");
1317   VG_(umsg)("                 See --help for details.\n");
1318   VG_(umsg)("\n");
1319   VG_(umsg)("* Each allocation stack, by default 12 frames, counts as\n");
1320   VG_(umsg)("  a separate alloc point.  This causes the data to be spread out\n");
1321   VG_(umsg)("  over far too many alloc points.  I strongly suggest using\n");
1322   VG_(umsg)("  --num-callers=4 or some such, to reduce the spreading.\n");
1323   VG_(umsg)("\n");
1324
1325   if (VG_(clo_stats)) {
1326      VG_(dmsg)(" dhat: find_Block_containing:\n");
1327      VG_(dmsg)("             found: %'lu (%'lu cached + %'lu uncached)\n",
1328                stats__n_fBc_cached + stats__n_fBc_uncached,
1329                stats__n_fBc_cached,
1330                stats__n_fBc_uncached);
1331      VG_(dmsg)("          notfound: %'lu\n", stats__n_fBc_notfound);
1332      VG_(dmsg)("\n");
1333   }
1334}
1335
1336
1337//------------------------------------------------------------//
1338//--- Initialisation                                       ---//
1339//------------------------------------------------------------//
1340
1341static void dh_post_clo_init(void)
1342{
1343}
1344
1345static void dh_pre_clo_init(void)
1346{
1347   VG_(details_name)            ("DHAT");
1348   VG_(details_version)         (NULL);
1349   VG_(details_description)     ("a dynamic heap analysis tool");
1350   VG_(details_copyright_author)(
1351      "Copyright (C) 2010-2013, and GNU GPL'd, by Mozilla Inc");
1352   VG_(details_bug_reports_to)  (VG_BUGS_TO);
1353
1354   // Basic functions.
1355   VG_(basic_tool_funcs)          (dh_post_clo_init,
1356                                   dh_instrument,
1357                                   dh_fini);
1358//zz
1359   // Needs.
1360   VG_(needs_libc_freeres)();
1361   VG_(needs_command_line_options)(dh_process_cmd_line_option,
1362                                   dh_print_usage,
1363                                   dh_print_debug_usage);
1364//zz   VG_(needs_client_requests)     (dh_handle_client_request);
1365//zz   VG_(needs_sanity_checks)       (dh_cheap_sanity_check,
1366//zz                                   dh_expensive_sanity_check);
1367   VG_(needs_malloc_replacement)  (dh_malloc,
1368                                   dh___builtin_new,
1369                                   dh___builtin_vec_new,
1370                                   dh_memalign,
1371                                   dh_calloc,
1372                                   dh_free,
1373                                   dh___builtin_delete,
1374                                   dh___builtin_vec_delete,
1375                                   dh_realloc,
1376                                   dh_malloc_usable_size,
1377                                   0 );
1378
1379   VG_(track_pre_mem_read)        ( dh_handle_noninsn_read );
1380   //VG_(track_pre_mem_read_asciiz) ( check_mem_is_defined_asciiz );
1381   VG_(track_post_mem_write)      ( dh_handle_noninsn_write );
1382
1383   tl_assert(!interval_tree);
1384   tl_assert(!fbc_cache0);
1385   tl_assert(!fbc_cache1);
1386
1387   interval_tree = VG_(newFM)( VG_(malloc),
1388                               "dh.main.interval_tree.1",
1389                               VG_(free),
1390                               interval_tree_Cmp );
1391
1392   apinfo = VG_(newFM)( VG_(malloc),
1393                        "dh.main.apinfo.1",
1394                        VG_(free),
1395                        NULL/*unboxedcmp*/ );
1396}
1397
1398VG_DETERMINE_INTERFACE_VERSION(dh_pre_clo_init)
1399
1400//--------------------------------------------------------------------//
1401//--- end                                                dh_main.c ---//
1402//--------------------------------------------------------------------//
1403