1ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
2ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*--------------------------------------------------------------------*/
3ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*--- The leak checker.                             mc_leakcheck.c ---*/
4ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*--------------------------------------------------------------------*/
5ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
6ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*
7ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   This file is part of MemCheck, a heavyweight Valgrind tool for
8ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   detecting memory errors.
9ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
10b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   Copyright (C) 2000-2011 Julian Seward
11ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      jseward@acm.org
12ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
13ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   This program is free software; you can redistribute it and/or
14ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   modify it under the terms of the GNU General Public License as
15ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   published by the Free Software Foundation; either version 2 of the
16ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   License, or (at your option) any later version.
17ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
18ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   This program is distributed in the hope that it will be useful, but
19ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   WITHOUT ANY WARRANTY; without even the implied warranty of
20ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   General Public License for more details.
22ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
23ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   You should have received a copy of the GNU General Public License
24ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   along with this program; if not, write to the Free Software
25ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   02111-1307, USA.
27ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
28ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   The GNU General Public License is contained in the file COPYING.
29ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown*/
30ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
31ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_tool_basics.h"
32ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_tool_vki.h"
33ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_tool_aspacehl.h"
34ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_tool_aspacemgr.h"
35ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_tool_execontext.h"
36ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_tool_hashtable.h"
37ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_tool_libcbase.h"
38ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_tool_libcassert.h"
39ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_tool_libcprint.h"
40ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_tool_libcsignal.h"
41ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_tool_machine.h"
42ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_tool_mallocfree.h"
43ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_tool_options.h"
44ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_tool_oset.h"
45ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_tool_signals.h"
46b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov#include "pub_tool_libcsetjmp.h"    // setjmp facilities
47ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_tool_tooliface.h"     // Needed for mc_include.h
48ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
49ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "mc_include.h"
50ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
51ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*------------------------------------------------------------*/
52ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*--- An overview of leak checking.                        ---*/
53ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*------------------------------------------------------------*/
54ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
55ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Leak-checking is a directed-graph traversal problem.  The graph has
56ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// two kinds of nodes:
57ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - root-set nodes:
58ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   - GP registers of all threads;
59ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   - valid, aligned, pointer-sized data words in valid client memory,
60ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//     including stacks, but excluding words within client heap-allocated
61ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//     blocks (they are excluded so that later on we can differentiate
62ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//     between heap blocks that are indirectly leaked vs. directly leaked).
63ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - heap-allocated blocks.  A block is a mempool chunk or a malloc chunk
64ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   that doesn't contain a mempool chunk.  Nb: the terms "blocks" and
65ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   "chunks" are used interchangeably below.
66ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
67ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// There are two kinds of edges:
68ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - start-pointers, i.e. pointers to the start of a block;
69ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - interior-pointers, i.e. pointers to the interior of a block.
70ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
71ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// We use "pointers" rather than "edges" below.
72ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
73ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Root set nodes only point to blocks.  Blocks only point to blocks;
74ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// a block can point to itself.
75ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
76ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// The aim is to traverse the graph and determine the status of each block.
77ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
78ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// There are 9 distinct cases.  See memcheck/docs/mc-manual.xml for details.
79ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Presenting all nine categories to the user is probably too much.
80ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Currently we do this:
81ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - definitely lost:  case 3
82ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - indirectly lost:  case 4, 9
83ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - possibly lost:    cases 5..8
84ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - still reachable:  cases 1, 2
85ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
86ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// It's far from clear that this is the best possible categorisation;  it's
87ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// accreted over time without any central guiding principle.
88ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
89ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*------------------------------------------------------------*/
90ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*--- XXX: Thoughts for improvement.                       ---*/
91ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*------------------------------------------------------------*/
92ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
93ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// From the user's point of view:
94ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - If they aren't using interior-pointers, they just have to fix the
95ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   directly lost blocks, and the indirectly lost ones will be fixed as
96ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   part of that.  Any possibly lost blocks will just be due to random
97ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   pointer garbage and can be ignored.
98ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
99ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - If they are using interior-pointers, the fact that they currently are not
100ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   being told which ones might be directly lost vs. indirectly lost makes
101ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   it hard to know where to begin.
102ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
103ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// All this makes me wonder if new option is warranted:
104ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// --follow-interior-pointers.  By default it would be off, the leak checker
105ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// wouldn't follow interior-pointers and there would only be 3 categories:
106ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// R, DL, IL.
107ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
108ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// If turned on, then it would show 7 categories (R, DL, IL, DR/DL, IR/IL,
109ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// IR/IL/DL, IL/DL).  That output is harder to understand but it's your own
110ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// damn fault for using interior-pointers...
111ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
112ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// ----
113ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
114ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Also, why are two blank lines printed between each loss record?
115ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// [bug 197930]
116ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
117ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// ----
118ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
119ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Also, --show-reachable is a bad name because it also turns on the showing
120ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// of indirectly leaked blocks(!)  It would be better named --show-all or
121ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// --show-all-heap-blocks, because that's the end result.
122ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
123ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// ----
124ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
125ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Also, the VALGRIND_LEAK_CHECK and VALGRIND_QUICK_LEAK_CHECK aren't great
126ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// names.  VALGRIND_FULL_LEAK_CHECK and VALGRIND_SUMMARY_LEAK_CHECK would be
127ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// better.
128ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
129ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// ----
130ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
131ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Also, VALGRIND_COUNT_LEAKS and VALGRIND_COUNT_LEAK_BLOCKS aren't great as
132ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// they combine direct leaks and indirect leaks into one.  New, more precise
133ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// ones (they'll need new names) would be good.  If more categories are
134ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// used, as per the --follow-interior-pointers option, they should be
135ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// updated accordingly.  And they should use a struct to return the values.
136ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
137ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// ----
138ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
139ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Also, for this case:
140ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
141ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//  (4)  p4      BBB ---> AAA
142ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
143ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// BBB is definitely directly lost.  AAA is definitely indirectly lost.
144ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Here's the relevant loss records printed for a full check (each block is
145ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// 16 bytes):
146ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
147ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// ==20397== 16 bytes in 1 blocks are indirectly lost in loss record 9 of 15
148ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// ==20397==    at 0x4C2694E: malloc (vg_replace_malloc.c:177)
149ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// ==20397==    by 0x400521: mk (leak-cases.c:49)
150ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// ==20397==    by 0x400578: main (leak-cases.c:72)
151ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
152ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// ==20397== 32 (16 direct, 16 indirect) bytes in 1 blocks are definitely
153ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// lost in loss record 14 of 15
154ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// ==20397==    at 0x4C2694E: malloc (vg_replace_malloc.c:177)
155ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// ==20397==    by 0x400521: mk (leak-cases.c:49)
156ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// ==20397==    by 0x400580: main (leak-cases.c:72)
157ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
158ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// The first one is fine -- it describes AAA.
159ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
160ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// The second one is for BBB.  It's correct in that 16 bytes in 1 block are
161ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// directly lost. It's also correct that 16 are indirectly lost as a result,
162ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// but it means that AAA is being counted twice in the loss records.  (It's
163ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// not, thankfully, counted twice in the summary counts).  Argh.
164ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
165ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// This would be less confusing for the second one:
166ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
167ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// ==20397== 16 bytes in 1 blocks are definitely lost in loss record 14
168ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// of 15 (and 16 bytes in 1 block are indirectly lost as a result;  they
169ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// are mentioned elsewhere (if --show-reachable=yes is given!))
170ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// ==20397==    at 0x4C2694E: malloc (vg_replace_malloc.c:177)
171ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// ==20397==    by 0x400521: mk (leak-cases.c:49)
172ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// ==20397==    by 0x400580: main (leak-cases.c:72)
173ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
174ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// But ideally we'd present the loss record for the directly lost block and
175ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// then the resultant indirectly lost blocks and make it clear the
176ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// dependence.  Double argh.
177ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
178ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*------------------------------------------------------------*/
179ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*--- The actual algorithm.                                ---*/
180ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*------------------------------------------------------------*/
181ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
182ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - Find all the blocks (a.k.a. chunks) to check.  Mempool chunks require
183ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   some special treatment because they can be within malloc'd blocks.
184ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - Scan every word in the root set (GP registers and valid
185ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   non-heap memory words).
186ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   - First, we skip if it doesn't point to valid memory.
187ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   - Then, we see if it points to the start or interior of a block.  If
188ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//     so, we push the block onto the mark stack and mark it as having been
189ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//     reached.
190ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - Then, we process the mark stack, repeating the scanning for each block;
191ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   this can push more blocks onto the mark stack.  We repeat until the
192ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   mark stack is empty.  Each block is marked as definitely or possibly
193ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   reachable, depending on whether interior-pointers were required to
194ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   reach it.
195ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - At this point we know for every block if it's reachable or not.
196ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - We then push each unreached block onto the mark stack, using the block
197ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   number as the "clique" number.
198ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - We process the mark stack again, this time grouping blocks into cliques
199ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   in order to facilitate the directly/indirectly lost categorisation.
200ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - We group blocks by their ExeContexts and categorisation, and print them
201ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   if --leak-check=full.  We also print summary numbers.
202ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
203ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// A note on "cliques":
204ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - A directly lost block is one with no pointers to it.  An indirectly
205ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   lost block is one that is pointed to by a directly or indirectly lost
206ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   block.
207ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - Each directly lost block has zero or more indirectly lost blocks
208ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   hanging off it.  All these blocks together form a "clique".  The
209ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   directly lost block is called the "clique leader".  The clique number
210ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   is the number (in lc_chunks[]) of the clique leader.
211ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - Actually, a directly lost block may be pointed to if it's part of a
212ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   cycle.  In that case, there may be more than one choice for the clique
213ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   leader, and the choice is arbitrary.  Eg. if you have A-->B and B-->A
214ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   either A or B could be the clique leader.
215ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - Cliques cannot overlap, and will be truncated to avoid this.  Eg. if we
216ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   have A-->C and B-->C, the two cliques will be {A,C} and {B}, or {A} and
217ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   {B,C} (again the choice is arbitrary).  This is because we don't want
218ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   to count a block as indirectly lost more than once.
219ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//
220ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// A note on 'is_prior_definite':
221ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - This is a boolean used in various places that indicates if the chain
222ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   up to the prior node (prior to the one being considered) is definite.
223ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - In the clique == -1 case:
224ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   - if True it means that the prior node is a root-set node, or that the
225ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//     prior node is a block which is reachable from the root-set via
226ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//     start-pointers.
227ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   - if False it means that the prior node is a block that is only
228ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//     reachable from the root-set via a path including at least one
229ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//     interior-pointer.
230ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - In the clique != -1 case, currently it's always True because we treat
231ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   start-pointers and interior-pointers the same for direct/indirect leak
232ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   checking.  If we added a PossibleIndirectLeak state then this would
233ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//   change.
234ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
235ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
236ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Define to debug the memory-leak-detector.
237ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define VG_DEBUG_LEAKCHECK 0
238ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define VG_DEBUG_CLIQUE    0
239ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
240ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
241ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*------------------------------------------------------------*/
242ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*--- Getting the initial chunks, and searching them.      ---*/
243ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*------------------------------------------------------------*/
244ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
245ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Compare the MC_Chunks by 'data' (i.e. the address of the block).
246ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic Int compare_MC_Chunks(void* n1, void* n2)
247ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
248ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   MC_Chunk* mc1 = *(MC_Chunk**)n1;
249ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   MC_Chunk* mc2 = *(MC_Chunk**)n2;
250ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (mc1->data < mc2->data) return -1;
251ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (mc1->data > mc2->data) return  1;
252ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   return 0;
253ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
254ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
255ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#if VG_DEBUG_LEAKCHECK
256ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Used to sanity-check the fast binary-search mechanism.
257ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic
258ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff BrownInt find_chunk_for_OLD ( Addr       ptr,
259ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                         MC_Chunk** chunks,
260ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                         Int        n_chunks )
261ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
262ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
263ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Int  i;
264ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Addr a_lo, a_hi;
265ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   PROF_EVENT(70, "find_chunk_for_OLD");
266ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   for (i = 0; i < n_chunks; i++) {
267ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      PROF_EVENT(71, "find_chunk_for_OLD(loop)");
268ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      a_lo = chunks[i]->data;
269ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      a_hi = ((Addr)chunks[i]->data) + chunks[i]->szB;
270ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (a_lo <= ptr && ptr < a_hi)
271ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         return i;
272ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
273ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   return -1;
274ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
275ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#endif
276ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
277ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Find the i such that ptr points at or inside the block described by
278ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// chunks[i].  Return -1 if none found.  This assumes that chunks[]
279ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// has been sorted on the 'data' field.
280ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic
281ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff BrownInt find_chunk_for ( Addr       ptr,
282ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                     MC_Chunk** chunks,
283ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                     Int        n_chunks )
284ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
285ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Addr a_mid_lo, a_mid_hi;
286ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Int lo, mid, hi, retVal;
287ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // VG_(printf)("find chunk for %p = ", ptr);
288ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   retVal = -1;
289ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   lo = 0;
290ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   hi = n_chunks-1;
291ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   while (True) {
292ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // Invariant: current unsearched space is from lo to hi, inclusive.
293ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (lo > hi) break; // not found
294ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
295ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      mid      = (lo + hi) / 2;
296ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      a_mid_lo = chunks[mid]->data;
297ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      a_mid_hi = chunks[mid]->data + chunks[mid]->szB;
298ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // Extent of block 'mid' is [a_mid_lo .. a_mid_hi).
299ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // Special-case zero-sized blocks - treat them as if they had
300ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // size 1.  Not doing so causes them to not cover any address
301ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // range at all and so will never be identified as the target of
302ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // any pointer, which causes them to be incorrectly reported as
303ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // definitely leaked.
304ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (chunks[mid]->szB == 0)
305ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         a_mid_hi++;
306ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
307ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (ptr < a_mid_lo) {
308ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         hi = mid-1;
309ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         continue;
310ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
311ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (ptr >= a_mid_hi) {
312ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         lo = mid+1;
313ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         continue;
314ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
315ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      tl_assert(ptr >= a_mid_lo && ptr < a_mid_hi);
316ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      retVal = mid;
317ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      break;
318ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
319ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
320ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#  if VG_DEBUG_LEAKCHECK
321ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   tl_assert(retVal == find_chunk_for_OLD ( ptr, chunks, n_chunks ));
322ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#  endif
323ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // VG_(printf)("%d\n", retVal);
324ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   return retVal;
325ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
326ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
327ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
328ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic MC_Chunk**
329ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownfind_active_chunks(UInt* pn_chunks)
330ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
331ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Our goal is to construct a set of chunks that includes every
332ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // mempool chunk, and every malloc region that *doesn't* contain a
333ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // mempool chunk.
334ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   MC_Mempool *mp;
335ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   MC_Chunk **mallocs, **chunks, *mc;
336ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   UInt n_mallocs, n_chunks, m, s;
337ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Bool *malloc_chunk_holds_a_pool_chunk;
338ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
339ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // First we collect all the malloc chunks into an array and sort it.
340ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // We do this because we want to query the chunks by interior
341ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // pointers, requiring binary search.
342ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   mallocs = (MC_Chunk**) VG_(HT_to_array)( MC_(malloc_list), &n_mallocs );
343ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (n_mallocs == 0) {
344ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      tl_assert(mallocs == NULL);
345ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      *pn_chunks = 0;
346ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return NULL;
347ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
348ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   VG_(ssort)(mallocs, n_mallocs, sizeof(VgHashNode*), compare_MC_Chunks);
349ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
350ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Then we build an array containing a Bool for each malloc chunk,
351ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // indicating whether it contains any mempools.
352ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   malloc_chunk_holds_a_pool_chunk = VG_(calloc)( "mc.fas.1",
353ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                                                  n_mallocs, sizeof(Bool) );
354ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   n_chunks = n_mallocs;
355ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
356ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Then we loop over the mempool tables. For each chunk in each
357ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // pool, we set the entry in the Bool array corresponding to the
358ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // malloc chunk containing the mempool chunk.
359ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   VG_(HT_ResetIter)(MC_(mempool_list));
360ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   while ( (mp = VG_(HT_Next)(MC_(mempool_list))) ) {
361ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      VG_(HT_ResetIter)(mp->chunks);
362ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      while ( (mc = VG_(HT_Next)(mp->chunks)) ) {
363ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
364ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // We'll need to record this chunk.
365ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         n_chunks++;
366ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
367ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // Possibly invalidate the malloc holding the beginning of this chunk.
368ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         m = find_chunk_for(mc->data, mallocs, n_mallocs);
369ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (m != -1 && malloc_chunk_holds_a_pool_chunk[m] == False) {
370ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            tl_assert(n_chunks > 0);
371ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            n_chunks--;
372ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            malloc_chunk_holds_a_pool_chunk[m] = True;
373ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         }
374ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
375ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // Possibly invalidate the malloc holding the end of this chunk.
376ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (mc->szB > 1) {
377ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            m = find_chunk_for(mc->data + (mc->szB - 1), mallocs, n_mallocs);
378ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            if (m != -1 && malloc_chunk_holds_a_pool_chunk[m] == False) {
379ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown               tl_assert(n_chunks > 0);
380ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown               n_chunks--;
381ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown               malloc_chunk_holds_a_pool_chunk[m] = True;
382ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            }
383ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         }
384ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
385ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
386ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   tl_assert(n_chunks > 0);
387ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
388ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Create final chunk array.
389ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   chunks = VG_(malloc)("mc.fas.2", sizeof(VgHashNode*) * (n_chunks));
390ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   s = 0;
391ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
392ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Copy the mempool chunks and the non-marked malloc chunks into a
393ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // combined array of chunks.
394ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   VG_(HT_ResetIter)(MC_(mempool_list));
395ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   while ( (mp = VG_(HT_Next)(MC_(mempool_list))) ) {
396ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      VG_(HT_ResetIter)(mp->chunks);
397ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      while ( (mc = VG_(HT_Next)(mp->chunks)) ) {
398ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         tl_assert(s < n_chunks);
399ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         chunks[s++] = mc;
400ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
401ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
402ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   for (m = 0; m < n_mallocs; ++m) {
403ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (!malloc_chunk_holds_a_pool_chunk[m]) {
404ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         tl_assert(s < n_chunks);
405ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         chunks[s++] = mallocs[m];
406ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
407ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
408ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   tl_assert(s == n_chunks);
409ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
410ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Free temporaries.
411ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   VG_(free)(mallocs);
412ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   VG_(free)(malloc_chunk_holds_a_pool_chunk);
413ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
414ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   *pn_chunks = n_chunks;
415ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
416ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   return chunks;
417ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
418ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
419ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*------------------------------------------------------------*/
420ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*--- The leak detector proper.                            ---*/
421ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*------------------------------------------------------------*/
422ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
423ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Holds extra info about each block during leak checking.
424ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Browntypedef
425ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   struct {
426ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      UInt  state:2;    // Reachedness.
427ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      UInt  pending:1;  // Scan pending.
428ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      SizeT indirect_szB : (sizeof(SizeT)*8)-3; // If Unreached, how many bytes
429ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                                                //   are unreachable from here.
430ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
431ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   LC_Extra;
432ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
433ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// An array holding pointers to every chunk we're checking.  Sorted by address.
434ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic MC_Chunk** lc_chunks;
435ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// How many chunks we're dealing with.
436ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic Int        lc_n_chunks;
437b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov// chunks will be converted and merged in loss record, maintained in lr_table
438b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov// lr_table elements are kept from one leak_search to another to implement
439b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov// the "print new/changed leaks" client request
440b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanovstatic OSet*        lr_table;
441b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov
442b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov// DeltaMode used the last time we called detect_memory_leaks.
443b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov// The recorded leak errors must be output using a logic based on this delta_mode.
444b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov// The below avoids replicating the delta_mode in each LossRecord.
445b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy StepanovLeakCheckDeltaMode MC_(detect_memory_leaks_last_delta_mode);
446b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov
447ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
448ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// This has the same number of entries as lc_chunks, and each entry
449ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// in lc_chunks corresponds with the entry here (ie. lc_chunks[i] and
450ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// lc_extras[i] describe the same block).
451ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic LC_Extra* lc_extras;
452ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
453ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Records chunks that are currently being processed.  Each element in the
454ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// stack is an index into lc_chunks and lc_extras.  Its size is
455ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// 'lc_n_chunks' because in the worst case that's how many chunks could be
456ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// pushed onto it (actually I think the maximum is lc_n_chunks-1 but let's
457ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// be conservative).
458ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic Int* lc_markstack;
459ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// The index of the top element of the stack; -1 if the stack is empty, 0 if
460ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// the stack has one element, 1 if it has two, etc.
461ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic Int  lc_markstack_top;
462ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
463ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Keeps track of how many bytes of memory we've scanned, for printing.
464ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// (Nb: We don't keep track of how many register bytes we've scanned.)
465ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic SizeT lc_scanned_szB;
466ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
467ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
468ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff BrownSizeT MC_(bytes_leaked)     = 0;
469ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff BrownSizeT MC_(bytes_indirect)   = 0;
470ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff BrownSizeT MC_(bytes_dubious)    = 0;
471ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff BrownSizeT MC_(bytes_reachable)  = 0;
472ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff BrownSizeT MC_(bytes_suppressed) = 0;
473ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
474ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff BrownSizeT MC_(blocks_leaked)     = 0;
475ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff BrownSizeT MC_(blocks_indirect)   = 0;
476ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff BrownSizeT MC_(blocks_dubious)    = 0;
477ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff BrownSizeT MC_(blocks_reachable)  = 0;
478ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff BrownSizeT MC_(blocks_suppressed) = 0;
479ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
480ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
481ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Determines if a pointer is to a chunk.  Returns the chunk number et al
482ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// via call-by-reference.
483ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic Bool
484ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownlc_is_a_chunk_ptr(Addr ptr, Int* pch_no, MC_Chunk** pch, LC_Extra** pex)
485ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
486ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Int ch_no;
487ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   MC_Chunk* ch;
488ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   LC_Extra* ex;
489ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
490ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Quick filter.
491ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (!VG_(am_is_valid_for_client)(ptr, 1, VKI_PROT_READ)) {
492ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return False;
493ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   } else {
494ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      ch_no = find_chunk_for(ptr, lc_chunks, lc_n_chunks);
495ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      tl_assert(ch_no >= -1 && ch_no < lc_n_chunks);
496ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
497ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (ch_no == -1) {
498ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         return False;
499ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      } else {
500ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // Ok, we've found a pointer to a chunk.  Get the MC_Chunk and its
501ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // LC_Extra.
502ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         ch = lc_chunks[ch_no];
503ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         ex = &(lc_extras[ch_no]);
504ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
505ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         tl_assert(ptr >= ch->data);
506ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         tl_assert(ptr < ch->data + ch->szB + (ch->szB==0  ? 1  : 0));
507ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
508ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (VG_DEBUG_LEAKCHECK)
509ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            VG_(printf)("ptr=%#lx -> block %d\n", ptr, ch_no);
510ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
511ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         *pch_no = ch_no;
512ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         *pch    = ch;
513ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         *pex    = ex;
514ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
515ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         return True;
516ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
517ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
518ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
519ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
520ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Push a chunk (well, just its index) onto the mark stack.
521ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic void lc_push(Int ch_no, MC_Chunk* ch)
522ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
523ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (!lc_extras[ch_no].pending) {
524ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (0) {
525ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         VG_(printf)("pushing %#lx-%#lx\n", ch->data, ch->data + ch->szB);
526ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
527ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lc_markstack_top++;
528ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      tl_assert(lc_markstack_top < lc_n_chunks);
529ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lc_markstack[lc_markstack_top] = ch_no;
530ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      tl_assert(!lc_extras[ch_no].pending);
531ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lc_extras[ch_no].pending = True;
532ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
533ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
534ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
535ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Return the index of the chunk on the top of the mark stack, or -1 if
536ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// there isn't one.
537ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic Bool lc_pop(Int* ret)
538ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
539ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (-1 == lc_markstack_top) {
540ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return False;
541ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   } else {
542ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      tl_assert(0 <= lc_markstack_top && lc_markstack_top < lc_n_chunks);
543ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      *ret = lc_markstack[lc_markstack_top];
544ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lc_markstack_top--;
545ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      tl_assert(lc_extras[*ret].pending);
546ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lc_extras[*ret].pending = False;
547ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return True;
548ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
549ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
550ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
551b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov// Partial fix for https://bugs.kde.org/show_bug.cgi?id=280271
552b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov// Only used in valgrind-variant now.
553b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanovstatic Bool vv_lc_is_start_pointer(Addr ptr, MC_Chunk* chunk)
554b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov{
555b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   // Pointers to the start of the chunk are indeed start-pointers
556b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   if (ptr == chunk->data)
557b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      return True;
558b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov
559b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   // Below are a few heuristics to reduce the number of false positive
560b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   // "possibly lost" reports on C++ types by treating some interior-pointers
561b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   // as start-pointers (inspired by the Dr. Memory article at CGO2011).
562b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov
563b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   // Shortcut: heuristics assume 'ptr' is word-aligned.
564b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   if (ptr != VG_ROUNDUP(ptr, sizeof(Addr)))
565b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      return False;
566b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov
567b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   if (ptr == chunk->data + sizeof(Addr)) {
568b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      // Pointer to a new[]-allocated buffer?
569b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      SizeT sz_from_header = *(SizeT*)chunk->data,
570b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            expected_sz = chunk->szB - sizeof(Addr);
571b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      if (sz_from_header > 0 && sz_from_header <= expected_sz &&
572b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov          expected_sz % sz_from_header == 0)
573b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         return True;
574b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   }
575b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov
576b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   if (ptr == chunk->data + 3*sizeof(Addr)) {
577b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      // Pointer to std::string internals?
578b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      SizeT assumed_len = *(SizeT*)chunk->data,
579b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            assumed_capacity = *((SizeT*)chunk->data + 1);
580b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      if (assumed_len <= assumed_capacity) {
581b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         // std::string
582b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         if (chunk->szB - 3*sizeof(SizeT) == assumed_capacity + 1)
583b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            return True;
584b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov
585b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         // std::basic_string<unsigned short> a.k.a. string16
586b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         if (chunk->szB - 3*sizeof(SizeT) == 2*(assumed_capacity + 1))
587b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            return True;
588b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov
589b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         // std::basic_string<wchar_t> on Linux
590b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         if (chunk->szB - 3*sizeof(SizeT) == 4*(assumed_capacity + 1))
591b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            return True;
592b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      }
593b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   }
594b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov
595b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   if (ptr == chunk->data + 2*sizeof(Addr)) {
596b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      // Pointer to a nss_ZAlloc-allocated buffer?
597b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      // It adds a header like this: 'struct { void *ptr; uint32 size };'
598b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      SizeT sz_from_header = *(UInt*)(chunk->data + sizeof(Addr)),
599b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            expected_sz = chunk->szB - 2*sizeof(Addr);
600b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      tl_assert(sizeof(UInt) == 4);
601b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      if (sz_from_header == expected_sz)
602b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         return True;
603b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   }
604b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov
605b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   return False;
606b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov}
607ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
608ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// If 'ptr' is pointing to a heap-allocated block which hasn't been seen
609ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// before, push it onto the mark stack.
610ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic void
611ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownlc_push_without_clique_if_a_chunk_ptr(Addr ptr, Bool is_prior_definite)
612ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
613ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Int ch_no;
614ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   MC_Chunk* ch;
615ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   LC_Extra* ex;
616ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
617ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if ( ! lc_is_a_chunk_ptr(ptr, &ch_no, &ch, &ex) )
618ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return;
619ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
620ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Possibly upgrade the state, ie. one of:
621ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // - Unreached --> Possible
622ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // - Unreached --> Reachable
623ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // - Possible  --> Reachable
624b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   if (vv_lc_is_start_pointer(ptr, ch) &&
625b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov       is_prior_definite && ex->state != Reachable) {
626ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // 'ptr' points to the start of the block, and the prior node is
627ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // definite, which means that this block is definitely reachable.
628ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      ex->state = Reachable;
629ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
630ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // State has changed to Reachable so (re)scan the block to make
631ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // sure any blocks it points to are correctly marked.
632ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lc_push(ch_no, ch);
633ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
634ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   } else if (ex->state == Unreached) {
635ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // Either 'ptr' is a interior-pointer, or the prior node isn't definite,
636ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // which means that we can only mark this block as possibly reachable.
637ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      ex->state = Possible;
638ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
639ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // State has changed to Possible so (re)scan the block to make
640ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // sure any blocks it points to are correctly marked.
641ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lc_push(ch_no, ch);
642ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
643ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
644ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
645ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic void
646ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownlc_push_if_a_chunk_ptr_register(Addr ptr)
647ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
648ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   lc_push_without_clique_if_a_chunk_ptr(ptr, /*is_prior_definite*/True);
649ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
650ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
651ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// If ptr is pointing to a heap-allocated block which hasn't been seen
652ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// before, push it onto the mark stack.  Clique is the index of the
653ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// clique leader.
654ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic void
655ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownlc_push_with_clique_if_a_chunk_ptr(Addr ptr, Int clique)
656ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
657ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Int ch_no;
658ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   MC_Chunk* ch;
659ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   LC_Extra* ex;
660ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
661ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   tl_assert(0 <= clique && clique < lc_n_chunks);
662ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
663ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if ( ! lc_is_a_chunk_ptr(ptr, &ch_no, &ch, &ex) )
664ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return;
665ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
666ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // If it's not Unreached, it's already been handled so ignore it.
667ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // If ch_no==clique, it's the clique leader, which means this is a cyclic
668ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // structure;  again ignore it because it's already been handled.
669ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (ex->state == Unreached && ch_no != clique) {
670ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // Note that, unlike reachable blocks, we currently don't distinguish
671ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // between start-pointers and interior-pointers here.  We probably
672ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // should, though.
673ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      ex->state = IndirectLeak;
674ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lc_push(ch_no, ch);
675ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
676ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // Add the block to the clique, and add its size to the
677ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // clique-leader's indirect size.  Also, if the new block was
678ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // itself a clique leader, it isn't any more, so add its
679ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // indirect_szB to the new clique leader.
680ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (VG_DEBUG_CLIQUE) {
681ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (ex->indirect_szB > 0)
682ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            VG_(printf)("  clique %d joining clique %d adding %lu+%lu\n",
683b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                        ch_no, clique, (unsigned long)ch->szB,
684b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov			(unsigned long)ex->indirect_szB);
685ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         else
686ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            VG_(printf)("  block %d joining clique %d adding %lu\n",
687b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                        ch_no, clique, (unsigned long)ch->szB);
688ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
689ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
690ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lc_extras[clique].indirect_szB += ch->szB;
691ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lc_extras[clique].indirect_szB += ex->indirect_szB;
692ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      ex->indirect_szB = 0;    // Shouldn't matter.
693ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
694ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
695ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
696ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic void
697ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownlc_push_if_a_chunk_ptr(Addr ptr, Int clique, Bool is_prior_definite)
698ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
699ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (-1 == clique)
700ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lc_push_without_clique_if_a_chunk_ptr(ptr, is_prior_definite);
701ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   else
702ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lc_push_with_clique_if_a_chunk_ptr(ptr, clique);
703ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
704ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
705ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
706b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanovstatic VG_MINIMAL_JMP_BUF(memscan_jmpbuf);
707ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
708ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic
709ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownvoid scan_all_valid_memory_catcher ( Int sigNo, Addr addr )
710ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
711ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (0)
712ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      VG_(printf)("OUCH! sig=%d addr=%#lx\n", sigNo, addr);
713ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (sigNo == VKI_SIGSEGV || sigNo == VKI_SIGBUS)
714b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      VG_MINIMAL_LONGJMP(memscan_jmpbuf);
715ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
716ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
717ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Scan a block of memory between [start, start+len).  This range may
718ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// be bogus, inaccessable, or otherwise strange; we deal with it.  For each
719ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// valid aligned word we assume it's a pointer to a chunk a push the chunk
720ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// onto the mark stack if so.
721ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic void
722ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownlc_scan_memory(Addr start, SizeT len, Bool is_prior_definite, Int clique)
723ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
724ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Addr ptr = VG_ROUNDUP(start,     sizeof(Addr));
725ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Addr end = VG_ROUNDDN(start+len, sizeof(Addr));
726ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   vki_sigset_t sigmask;
727ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
728ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (VG_DEBUG_LEAKCHECK)
729ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      VG_(printf)("scan %#lx-%#lx (%lu)\n", start, end, len);
730ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
731ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   VG_(sigprocmask)(VKI_SIG_SETMASK, NULL, &sigmask);
732ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   VG_(set_fault_catcher)(scan_all_valid_memory_catcher);
733ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
734ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // We might be in the middle of a page.  Do a cheap check to see if
735ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // it's valid;  if not, skip onto the next page.
736ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (!VG_(am_is_valid_for_client)(ptr, sizeof(Addr), VKI_PROT_READ))
737ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      ptr = VG_PGROUNDUP(ptr+1);        // First page is bad - skip it.
738ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
739ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   while (ptr < end) {
740ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      Addr addr;
741ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
742ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // Skip invalid chunks.
743ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if ( ! MC_(is_within_valid_secondary)(ptr) ) {
744ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         ptr = VG_ROUNDUP(ptr+1, SM_SIZE);
745ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         continue;
746ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
747ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
748ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // Look to see if this page seems reasonable.
749ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if ((ptr % VKI_PAGE_SIZE) == 0) {
750ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (!VG_(am_is_valid_for_client)(ptr, sizeof(Addr), VKI_PROT_READ)) {
751ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            ptr += VKI_PAGE_SIZE;      // Bad page - skip it.
752ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            continue;
753ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         }
754ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
755ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
756b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      if (VG_MINIMAL_SETJMP(memscan_jmpbuf) == 0) {
757ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if ( MC_(is_valid_aligned_word)(ptr) ) {
758ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            lc_scanned_szB += sizeof(Addr);
759ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            addr = *(Addr *)ptr;
760ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            // If we get here, the scanned word is in valid memory.  Now
761ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            // let's see if its contents point to a chunk.
762ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            lc_push_if_a_chunk_ptr(addr, clique, is_prior_definite);
763ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         } else if (0 && VG_DEBUG_LEAKCHECK) {
764ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            VG_(printf)("%#lx not valid\n", ptr);
765ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         }
766ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         ptr += sizeof(Addr);
767ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      } else {
768ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // We need to restore the signal mask, because we were
769ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // longjmped out of a signal handler.
770ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         VG_(sigprocmask)(VKI_SIG_SETMASK, &sigmask, NULL);
771ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
772ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         ptr = VG_PGROUNDUP(ptr+1);     // Bad page - skip it.
773ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
774ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
775ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
776ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   VG_(sigprocmask)(VKI_SIG_SETMASK, &sigmask, NULL);
777ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   VG_(set_fault_catcher)(NULL);
778ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
779ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
780ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
781ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// Process the mark stack until empty.
782ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic void lc_process_markstack(Int clique)
783ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
784ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Int  top = -1;    // shut gcc up
785ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Bool is_prior_definite;
786ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
787ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   while (lc_pop(&top)) {
788ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      tl_assert(top >= 0 && top < lc_n_chunks);
789ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
790ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // See comment about 'is_prior_definite' at the top to understand this.
791ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      is_prior_definite = ( Possible != lc_extras[top].state );
792ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
793ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lc_scan_memory(lc_chunks[top]->data, lc_chunks[top]->szB,
794ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                     is_prior_definite, clique);
795ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
796ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
797ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
798ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic Word cmp_LossRecordKey_LossRecord(const void* key, const void* elem)
799ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
800ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   LossRecordKey* a = (LossRecordKey*)key;
801ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   LossRecordKey* b = &(((LossRecord*)elem)->key);
802ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
803ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Compare on states first because that's fast.
804ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (a->state < b->state) return -1;
805ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (a->state > b->state) return  1;
806ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Ok, the states are equal.  Now compare the locations, which is slower.
807ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (VG_(eq_ExeContext)(
808ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            MC_(clo_leak_resolution), a->allocated_at, b->allocated_at))
809ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return 0;
810ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Different locations.  Ordering is arbitrary, just use the ec pointer.
811ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (a->allocated_at < b->allocated_at) return -1;
812ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (a->allocated_at > b->allocated_at) return  1;
813ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   VG_(tool_panic)("bad LossRecord comparison");
814ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
815ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
816ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic Int cmp_LossRecords(void* va, void* vb)
817ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
818ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   LossRecord* lr_a = *(LossRecord**)va;
819ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   LossRecord* lr_b = *(LossRecord**)vb;
820ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   SizeT total_szB_a = lr_a->szB + lr_a->indirect_szB;
821ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   SizeT total_szB_b = lr_b->szB + lr_b->indirect_szB;
822ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
823ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // First compare by sizes.
824ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (total_szB_a < total_szB_b) return -1;
825ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (total_szB_a > total_szB_b) return  1;
826ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // If size are equal, compare by states.
827ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (lr_a->key.state < lr_b->key.state) return -1;
828ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (lr_a->key.state > lr_b->key.state) return  1;
829ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // If they're still equal here, it doesn't matter that much, but we keep
830ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // comparing other things so that regtests are as deterministic as
831ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // possible.  So:  compare num_blocks.
832ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (lr_a->num_blocks < lr_b->num_blocks) return -1;
833ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (lr_a->num_blocks > lr_b->num_blocks) return  1;
834ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Finally, compare ExeContext addresses... older ones are likely to have
835ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // lower addresses.
836ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (lr_a->key.allocated_at < lr_b->key.allocated_at) return -1;
837ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (lr_a->key.allocated_at > lr_b->key.allocated_at) return  1;
838ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   return 0;
839ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
840ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
841b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanovstatic void print_results(ThreadId tid, LeakCheckParams lcp)
842ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
843ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Int          i, n_lossrecords;
844ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   LossRecord** lr_array;
845ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   LossRecord*  lr;
846ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Bool         is_suppressed;
847b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   SizeT        old_bytes_leaked      = MC_(bytes_leaked); /* to report delta in summary */
848b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   SizeT        old_bytes_indirect    = MC_(bytes_indirect);
849b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   SizeT        old_bytes_dubious     = MC_(bytes_dubious);
850b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   SizeT        old_bytes_reachable   = MC_(bytes_reachable);
851b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   SizeT        old_bytes_suppressed  = MC_(bytes_suppressed);
852b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   SizeT        old_blocks_leaked     = MC_(blocks_leaked);
853b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   SizeT        old_blocks_indirect   = MC_(blocks_indirect);
854b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   SizeT        old_blocks_dubious    = MC_(blocks_dubious);
855b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   SizeT        old_blocks_reachable  = MC_(blocks_reachable);
856b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   SizeT        old_blocks_suppressed = MC_(blocks_suppressed);
857b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov
858b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   if (lr_table == NULL)
859b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      // Create the lr_table, which holds the loss records.
860b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      // If the lr_table already exists, it means it contains
861b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      // loss_records from the previous leak search. The old_*
862b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      // values in these records are used to implement the
863b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      // leak check delta mode
864b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      lr_table =
865b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         VG_(OSetGen_Create)(offsetof(LossRecord, key),
866b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                             cmp_LossRecordKey_LossRecord,
867b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                             VG_(malloc), "mc.pr.1",
868b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                             VG_(free));
869ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
870ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
871ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Convert the chunks into loss records, merging them where appropriate.
872ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   for (i = 0; i < lc_n_chunks; i++) {
873ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      MC_Chunk*     ch = lc_chunks[i];
874ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      LC_Extra*     ex = &(lc_extras)[i];
875ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      LossRecord*   old_lr;
876ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      LossRecordKey lrkey;
877ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lrkey.state        = ex->state;
878ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lrkey.allocated_at = ch->where;
879ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
880ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      old_lr = VG_(OSetGen_Lookup)(lr_table, &lrkey);
881ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (old_lr) {
882ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // We found an existing loss record matching this chunk.  Update the
883ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // loss record's details in-situ.  This is safe because we don't
884ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // change the elements used as the OSet key.
885ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         old_lr->szB          += ch->szB;
886ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         old_lr->indirect_szB += ex->indirect_szB;
887ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         old_lr->num_blocks++;
888ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      } else {
889ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // No existing loss record matches this chunk.  Create a new loss
890ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // record, initialise it from the chunk, and insert it into lr_table.
891ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         lr = VG_(OSetGen_AllocNode)(lr_table, sizeof(LossRecord));
892ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         lr->key              = lrkey;
893ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         lr->szB              = ch->szB;
894ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         lr->indirect_szB     = ex->indirect_szB;
895ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         lr->num_blocks       = 1;
896b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         lr->old_szB          = 0;
897b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         lr->old_indirect_szB = 0;
898b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         lr->old_num_blocks   = 0;
899ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         VG_(OSetGen_Insert)(lr_table, lr);
900ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
901ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
902ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   n_lossrecords = VG_(OSetGen_Size)(lr_table);
903ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
904ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Create an array of pointers to the loss records.
905ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   lr_array = VG_(malloc)("mc.pr.2", n_lossrecords * sizeof(LossRecord*));
906ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   i = 0;
907ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   VG_(OSetGen_ResetIter)(lr_table);
908ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   while ( (lr = VG_(OSetGen_Next)(lr_table)) ) {
909ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lr_array[i++] = lr;
910ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
911ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   tl_assert(i == n_lossrecords);
912ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
913ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Sort the array by loss record sizes.
914ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   VG_(ssort)(lr_array, n_lossrecords, sizeof(LossRecord*),
915ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown              cmp_LossRecords);
916ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
917ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Zero totals.
918ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   MC_(blocks_leaked)     = MC_(bytes_leaked)     = 0;
919ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   MC_(blocks_indirect)   = MC_(bytes_indirect)   = 0;
920ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   MC_(blocks_dubious)    = MC_(bytes_dubious)    = 0;
921ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   MC_(blocks_reachable)  = MC_(bytes_reachable)  = 0;
922ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   MC_(blocks_suppressed) = MC_(bytes_suppressed) = 0;
923ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
924ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Print the loss records (in size order) and collect summary stats.
925ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   for (i = 0; i < n_lossrecords; i++) {
926b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      Bool count_as_error, print_record, delta_considered;
927ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // Rules for printing:
928ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // - We don't show suppressed loss records ever (and that's controlled
929ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      //   within the error manager).
930ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // - We show non-suppressed loss records that are not "reachable" if
931ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      //   --leak-check=yes.
932ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // - We show all non-suppressed loss records if --leak-check=yes and
933ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      //   --show-reachable=yes.
934ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      //
935ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // Nb: here "reachable" means Reachable *or* IndirectLeak;  note that
936ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // this is different to "still reachable" used elsewhere because it
937ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // includes indirectly lost blocks!
938ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      //
939ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lr = lr_array[i];
940b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      switch (lcp.deltamode) {
941b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         case LCD_Any:
942b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            delta_considered = lr->num_blocks > 0;
943b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            break;
944b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         case LCD_Increased:
945b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            delta_considered
946b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov               = lr_array[i]->szB > lr_array[i]->old_szB
947b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                 || lr_array[i]->indirect_szB > lr_array[i]->old_indirect_szB
948b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                 || lr->num_blocks > lr->old_num_blocks;
949b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            break;
950b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         case LCD_Changed:
951b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            delta_considered = lr_array[i]->szB != lr_array[i]->old_szB
952b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            || lr_array[i]->indirect_szB != lr_array[i]->old_indirect_szB
953b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            || lr->num_blocks != lr->old_num_blocks;
954b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            break;
955b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         default:
956b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            tl_assert(0);
957b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      }
958b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov
959b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      print_record = lcp.mode == LC_Full && delta_considered &&
960b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                     ( lcp.show_reachable ||
961ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                       Unreached == lr->key.state ||
962b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                       ( lcp.show_possibly_lost &&
963ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                         Possible  == lr->key.state ) );
964b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      // We don't count a leaks as errors with lcp.mode==LC_Summary.
965ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // Otherwise you can get high error counts with few or no error
966ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // messages, which can be confusing.  Also, you could argue that
967ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // indirect leaks should be counted as errors, but it seems better to
968ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // make the counting criteria similar to the printing criteria.  So we
969ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // don't count them.
970b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      count_as_error = lcp.mode == LC_Full && delta_considered &&
971ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                       ( Unreached == lr->key.state ||
972ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                         Possible  == lr->key.state );
973b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      if ((Reachable == lr->key.state && !MC_(clo_show_reachable)) ||
974b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov          (Possible  == lr->key.state && !MC_(clo_show_possibly_lost)))
975b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         is_suppressed = False;
976b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      else
977b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         is_suppressed = MC_(record_leak_error)(tid, i+1, n_lossrecords, lr,
978b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                                                print_record, count_as_error);
979ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
980ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (is_suppressed) {
981ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         MC_(blocks_suppressed) += lr->num_blocks;
982ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         MC_(bytes_suppressed)  += lr->szB;
983ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
984ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      } else if (Unreached == lr->key.state) {
985ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         MC_(blocks_leaked)     += lr->num_blocks;
986ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         MC_(bytes_leaked)      += lr->szB;
987ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
988ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      } else if (IndirectLeak == lr->key.state) {
989ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         MC_(blocks_indirect)   += lr->num_blocks;
990ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         MC_(bytes_indirect)    += lr->szB;
991ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
992ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      } else if (Possible == lr->key.state) {
993ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         MC_(blocks_dubious)    += lr->num_blocks;
994ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         MC_(bytes_dubious)     += lr->szB;
995ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
996ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      } else if (Reachable == lr->key.state) {
997ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         MC_(blocks_reachable)  += lr->num_blocks;
998ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         MC_(bytes_reachable)   += lr->szB;
999ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1000ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      } else {
1001ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         VG_(tool_panic)("unknown loss mode");
1002ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
1003ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
1004ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1005b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   for (i = 0; i < n_lossrecords; i++)
1006b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      {
1007b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         if (lr->num_blocks == 0)
1008b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            // remove from lr_table the old loss_records with 0 bytes found
1009b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            VG_(OSetGen_Remove) (lr_table, &lr_array[i]->key);
1010b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         else
1011b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            {
1012b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov               // move the leak sizes to old_* and zero the current sizes
1013b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov               // for next leak search
1014b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov               lr_array[i]->old_szB          = lr_array[i]->szB;
1015b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov               lr_array[i]->old_indirect_szB = lr_array[i]->indirect_szB;
1016b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov               lr_array[i]->old_num_blocks   = lr_array[i]->num_blocks;
1017b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov               lr_array[i]->szB              = 0;
1018b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov               lr_array[i]->indirect_szB     = 0;
1019b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov               lr_array[i]->num_blocks       = 0;
1020b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            }
1021b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      }
1022b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   VG_(free)(lr_array);
1023b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov
1024ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (VG_(clo_verbosity) > 0 && !VG_(clo_xml)) {
1025b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      char d_bytes[20];
1026b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      char d_blocks[20];
1027b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov
1028ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      VG_(umsg)("LEAK SUMMARY:\n");
1029b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      VG_(umsg)("   definitely lost: %'lu%s bytes in %'lu%s blocks\n",
1030b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                MC_(bytes_leaked),
1031b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                MC_(snprintf_delta) (d_bytes, 20, MC_(bytes_leaked), old_bytes_leaked, lcp.deltamode),
1032b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                MC_(blocks_leaked),
1033b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                MC_(snprintf_delta) (d_blocks, 20, MC_(blocks_leaked), old_blocks_leaked, lcp.deltamode));
1034b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      VG_(umsg)("   indirectly lost: %'lu%s bytes in %'lu%s blocks\n",
1035b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                MC_(bytes_indirect),
1036b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                MC_(snprintf_delta) (d_bytes, 20, MC_(bytes_indirect), old_bytes_indirect, lcp.deltamode),
1037b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                MC_(blocks_indirect),
1038b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                MC_(snprintf_delta) (d_blocks, 20, MC_(blocks_indirect), old_blocks_indirect, lcp.deltamode) );
1039b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      VG_(umsg)("     possibly lost: %'lu%s bytes in %'lu%s blocks\n",
1040b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                MC_(bytes_dubious),
1041b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                MC_(snprintf_delta) (d_bytes, 20, MC_(bytes_dubious), old_bytes_dubious, lcp.deltamode),
1042b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                MC_(blocks_dubious),
1043b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                MC_(snprintf_delta) (d_blocks, 20, MC_(blocks_dubious), old_blocks_dubious, lcp.deltamode) );
1044b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      VG_(umsg)("   still reachable: %'lu%s bytes in %'lu%s blocks\n",
1045b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                MC_(bytes_reachable),
1046b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                MC_(snprintf_delta) (d_bytes, 20, MC_(bytes_reachable), old_bytes_reachable, lcp.deltamode),
1047b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                MC_(blocks_reachable),
1048b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                MC_(snprintf_delta) (d_blocks, 20, MC_(blocks_reachable), old_blocks_reachable, lcp.deltamode) );
1049b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      VG_(umsg)("        suppressed: %'lu%s bytes in %'lu%s blocks\n",
1050b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                MC_(bytes_suppressed),
1051b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                MC_(snprintf_delta) (d_bytes, 20, MC_(bytes_suppressed), old_bytes_suppressed, lcp.deltamode),
1052b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                MC_(blocks_suppressed),
1053b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                MC_(snprintf_delta) (d_blocks, 20, MC_(blocks_suppressed), old_blocks_suppressed, lcp.deltamode) );
1054b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      if (lcp.mode != LC_Full &&
1055ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown          (MC_(blocks_leaked) + MC_(blocks_indirect) +
1056ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown           MC_(blocks_dubious) + MC_(blocks_reachable)) > 0) {
1057b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         if (lcp.requested_by_monitor_command)
1058b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            VG_(umsg)("To see details of leaked memory, give 'full' arg to leak_check\n");
1059b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         else
1060b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            VG_(umsg)("Rerun with --leak-check=full to see details "
1061b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                      "of leaked memory\n");
1062ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
1063b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      if (lcp.mode == LC_Full &&
1064b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov          MC_(blocks_reachable) > 0 && !lcp.show_reachable)
1065ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      {
1066ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         VG_(umsg)("Reachable blocks (those to which a pointer "
1067ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                   "was found) are not shown.\n");
1068b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         if (lcp.requested_by_monitor_command)
1069b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            VG_(umsg)("To see them, add 'reachable any' args to leak_check\n");
1070b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         else
1071b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov            VG_(umsg)("To see them, rerun with: --leak-check=full "
1072b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                      "--show-reachable=yes\n");
1073ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
1074ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      VG_(umsg)("\n");
1075ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
1076ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
1077ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1078ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*------------------------------------------------------------*/
1079ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*--- Top-level entry point.                               ---*/
1080ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*------------------------------------------------------------*/
1081ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1082b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanovvoid MC_(detect_memory_leaks) ( ThreadId tid, LeakCheckParams lcp)
1083ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
1084ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Int i, j;
1085ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1086b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   tl_assert(lcp.mode != LC_Off);
1087b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov
1088b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   MC_(detect_memory_leaks_last_delta_mode) = lcp.deltamode;
1089ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1090ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Get the chunks, stop if there were none.
1091ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   lc_chunks = find_active_chunks(&lc_n_chunks);
1092ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (lc_n_chunks == 0) {
1093ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      tl_assert(lc_chunks == NULL);
1094b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      if (lr_table != NULL) {
1095b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         // forget the previous recorded LossRecords as next leak search will in any case
1096b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         // just create new leaks.
1097b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         // Maybe it would be better to rather call print_result ?
1098b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         // (at least when leak decrease are requested)
1099b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         // This will then output all LossRecords with a size decreasing to 0
1100b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov         VG_(OSetGen_Destroy) (lr_table);
1101b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov      }
1102ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (VG_(clo_verbosity) >= 1 && !VG_(clo_xml)) {
1103ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         VG_(umsg)("All heap blocks were freed -- no leaks are possible\n");
1104ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         VG_(umsg)("\n");
1105ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
1106ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return;
1107ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
1108ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1109ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Sort the array so blocks are in ascending order in memory.
1110ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   VG_(ssort)(lc_chunks, lc_n_chunks, sizeof(VgHashNode*), compare_MC_Chunks);
1111ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1112ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Sanity check -- make sure they're in order.
1113ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   for (i = 0; i < lc_n_chunks-1; i++) {
1114ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      tl_assert( lc_chunks[i]->data <= lc_chunks[i+1]->data);
1115ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
1116ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1117ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Sanity check -- make sure they don't overlap.  The one exception is that
1118ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // we allow a MALLOCLIKE block to sit entirely within a malloc() block.
1119ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // This is for bug 100628.  If this occurs, we ignore the malloc() block
1120ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // for leak-checking purposes.  This is a hack and probably should be done
1121ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // better, but at least it's consistent with mempools (which are treated
1122ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // like this in find_active_chunks).  Mempools have a separate VgHashTable
1123ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // for mempool chunks, but if custom-allocated blocks are put in a separate
1124ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // table from normal heap blocks it makes free-mismatch checking more
1125ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // difficult.
1126ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   //
1127ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // If this check fails, it probably means that the application
1128ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // has done something stupid with VALGRIND_MALLOCLIKE_BLOCK client
1129ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // requests, eg. has made overlapping requests (which are
1130ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // nonsensical), or used VALGRIND_MALLOCLIKE_BLOCK for stack locations;
1131ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // again nonsensical.
1132ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   //
1133ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   for (i = 0; i < lc_n_chunks-1; i++) {
1134ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      MC_Chunk* ch1 = lc_chunks[i];
1135ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      MC_Chunk* ch2 = lc_chunks[i+1];
1136ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1137ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      Addr start1    = ch1->data;
1138ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      Addr start2    = ch2->data;
1139ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      Addr end1      = ch1->data + ch1->szB - 1;
1140ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      Addr end2      = ch2->data + ch2->szB - 1;
1141ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      Bool isCustom1 = ch1->allockind == MC_AllocCustom;
1142ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      Bool isCustom2 = ch2->allockind == MC_AllocCustom;
1143ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1144ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (end1 < start2) {
1145ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // Normal case - no overlap.
1146ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1147ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // We used to allow exact duplicates, I'm not sure why.  --njn
1148ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      //} else if (start1 == start2 && end1 == end2) {
1149ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // Degenerate case: exact duplicates.
1150ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1151ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      } else if (start1 >= start2 && end1 <= end2 && isCustom1 && !isCustom2) {
1152ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // Block i is MALLOCLIKE and entirely within block i+1.
1153ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // Remove block i+1.
1154ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         for (j = i+1; j < lc_n_chunks-1; j++) {
1155ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            lc_chunks[j] = lc_chunks[j+1];
1156ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         }
1157ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         lc_n_chunks--;
1158ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1159ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      } else if (start2 >= start1 && end2 <= end1 && isCustom2 && !isCustom1) {
1160ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // Block i+1 is MALLOCLIKE and entirely within block i.
1161ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // Remove block i.
1162ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         for (j = i; j < lc_n_chunks-1; j++) {
1163ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            lc_chunks[j] = lc_chunks[j+1];
1164ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         }
1165ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         lc_n_chunks--;
1166ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1167ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      } else {
1168ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         VG_(umsg)("Block 0x%lx..0x%lx overlaps with block 0x%lx..0x%lx",
1169b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov                   start1, end1, start2, end2);
1170ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         VG_(umsg)("This is usually caused by using VALGRIND_MALLOCLIKE_BLOCK");
1171ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         VG_(umsg)("in an inappropriate way.");
1172ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         tl_assert (0);
1173ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
1174ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
1175ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1176ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Initialise lc_extras.
1177ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   lc_extras = VG_(malloc)( "mc.dml.2", lc_n_chunks * sizeof(LC_Extra) );
1178ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   for (i = 0; i < lc_n_chunks; i++) {
1179ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lc_extras[i].state        = Unreached;
1180ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lc_extras[i].pending      = False;
1181ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lc_extras[i].indirect_szB = 0;
1182ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
1183ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1184ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Initialise lc_markstack.
1185ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   lc_markstack = VG_(malloc)( "mc.dml.2", lc_n_chunks * sizeof(Int) );
1186ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   for (i = 0; i < lc_n_chunks; i++) {
1187ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lc_markstack[i] = -1;
1188ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
1189ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   lc_markstack_top = -1;
1190ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1191ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Verbosity.
1192ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (VG_(clo_verbosity) > 1 && !VG_(clo_xml)) {
1193ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      VG_(umsg)( "Searching for pointers to %'d not-freed blocks\n",
1194ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                 lc_n_chunks );
1195ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
1196ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1197ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Scan the memory root-set, pushing onto the mark stack any blocks
1198ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // pointed to.
1199ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   {
1200ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      Int   n_seg_starts;
1201ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      Addr* seg_starts = VG_(get_segment_starts)( &n_seg_starts );
1202ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1203ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      tl_assert(seg_starts && n_seg_starts > 0);
1204ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1205ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      lc_scanned_szB = 0;
1206ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1207ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // VG_(am_show_nsegments)( 0, "leakcheck");
1208ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      for (i = 0; i < n_seg_starts; i++) {
1209ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         SizeT seg_size;
1210ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         NSegment const* seg = VG_(am_find_nsegment)( seg_starts[i] );
1211ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         tl_assert(seg);
1212ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1213ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (seg->kind != SkFileC && seg->kind != SkAnonC) continue;
1214ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (!(seg->hasR && seg->hasW))                    continue;
1215ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (seg->isCH)                                    continue;
1216ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1217ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // Don't poke around in device segments as this may cause
1218ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // hangs.  Exclude /dev/zero just in case someone allocated
1219ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // memory by explicitly mapping /dev/zero.
1220ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (seg->kind == SkFileC
1221ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown             && (VKI_S_ISCHR(seg->mode) || VKI_S_ISBLK(seg->mode))) {
1222ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            HChar* dev_name = VG_(am_get_filename)( (NSegment*)seg );
1223ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            if (dev_name && 0 == VG_(strcmp)(dev_name, "/dev/zero")) {
1224ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown               // Don't skip /dev/zero.
1225ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            } else {
1226ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown               // Skip this device mapping.
1227ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown               continue;
1228ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            }
1229ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         }
1230ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1231ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (0)
1232ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            VG_(printf)("ACCEPT %2d  %#lx %#lx\n", i, seg->start, seg->end);
1233ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1234ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // Scan the segment.  We use -1 for the clique number, because this
1235ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // is a root-set.
1236ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         seg_size = seg->end - seg->start + 1;
1237ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (VG_(clo_verbosity) > 2) {
1238ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            VG_(message)(Vg_DebugMsg,
1239ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                         "  Scanning root segment: %#lx..%#lx (%lu)\n",
1240ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                         seg->start, seg->end, seg_size);
1241ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         }
1242ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         lc_scan_memory(seg->start, seg_size, /*is_prior_definite*/True, -1);
1243ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
1244ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
1245ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1246ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Scan GP registers for chunk pointers.
1247ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   VG_(apply_to_GP_regs)(lc_push_if_a_chunk_ptr_register);
1248ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1249ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Process the pushed blocks.  After this, every block that is reachable
1250ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // from the root-set has been traced.
1251ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   lc_process_markstack(/*clique*/-1);
1252ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1253ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (VG_(clo_verbosity) > 1 && !VG_(clo_xml)) {
1254ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      VG_(umsg)("Checked %'lu bytes\n", lc_scanned_szB);
1255ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      VG_(umsg)( "\n" );
1256ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
1257ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1258ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Trace all the leaked blocks to determine which are directly leaked and
1259ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // which are indirectly leaked.  For each Unreached block, push it onto
1260ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // the mark stack, and find all the as-yet-Unreached blocks reachable
1261ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // from it.  These form a clique and are marked IndirectLeak, and their
1262ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // size is added to the clique leader's indirect size.  If one of the
1263ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // found blocks was itself a clique leader (from a previous clique), then
1264ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // the cliques are merged.
1265ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   for (i = 0; i < lc_n_chunks; i++) {
1266ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      MC_Chunk* ch = lc_chunks[i];
1267ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      LC_Extra* ex = &(lc_extras[i]);
1268ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1269ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (VG_DEBUG_CLIQUE)
1270ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         VG_(printf)("cliques: %d at %#lx -> Loss state %d\n",
1271ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                     i, ch->data, ex->state);
1272ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1273ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      tl_assert(lc_markstack_top == -1);
1274ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1275ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (ex->state == Unreached) {
1276ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (VG_DEBUG_CLIQUE)
1277ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            VG_(printf)("%d: gathering clique %#lx\n", i, ch->data);
1278ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1279ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // Push this Unreached block onto the stack and process it.
1280ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         lc_push(i, ch);
1281ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         lc_process_markstack(i);
1282ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1283ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         tl_assert(lc_markstack_top == -1);
1284ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         tl_assert(ex->state == Unreached);
1285ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
1286ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
1287ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1288b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov   print_results( tid, lcp);
1289ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1290ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   VG_(free) ( lc_chunks );
1291ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   VG_(free) ( lc_extras );
1292ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   VG_(free) ( lc_markstack );
1293ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
1294ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
1295ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*--------------------------------------------------------------------*/
1296ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*--- end                                                          ---*/
1297ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*--------------------------------------------------------------------*/
1298