Add --pages-as-heap option to Massif. Bug 203256.

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@11200
This commit is contained in:
Nicholas Nethercote 2010-07-01 02:35:03 +00:00
parent fb6d1e0427
commit ddc229eb09
5 changed files with 321 additions and 93 deletions

11
NEWS
View File

@ -6,7 +6,7 @@ Improvements:
- XXX: Mac OS 10.6 support (32 and 64 bit)
- XXX: Much faster startup on Mac OS 10.5 for 64-bit programs.
- --smc-check=all is much faster
- Valgrind runs much faster when the --smc-check=all option is given.
- Cachegrind has a new processing script, cg_diff, which finds the
difference between two profiles. It's very useful for evaluating the
@ -16,6 +16,15 @@ Improvements:
--threshold option has changed; this is unlikely to affect many people, if
you do use it please see the user manual for details.
- Massif has a new option, --pages-as-heap, which is disabled by default.
When enabled, instead of tracking allocations at the level of heap blocks
(as allocated with malloc/new/new[]), it instead tracks memory allocations
at the level of memory pages (as mapped by mmap, brk, etc). Each mapped
page is treated as its own block. Interpreting the page-level output is
harder than the heap-level output, but this option is useful if you want
to account for every byte of memory used by a program.
Release 3.5.0 (19 August 2009)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -64,6 +64,15 @@ Char** VG_(client_envp) = NULL;
/* Path to library directory */
const Char *VG_(libdir) = VG_LIBDIR;
const Char *VG_(LD_PRELOAD_var_name) =
#if defined(VGO_linux) || defined(VGO_aix5)
"LD_PRELOAD";
#elif defined(VGO_darwin)
"DYLD_INSERT_LIBRARIES";
#else
# error Unknown OS
#endif
/* We do getenv without libc's help by snooping around in
VG_(client_envp) as determined at startup time. */
Char *VG_(getenv)(Char *varname)

View File

@ -44,6 +44,10 @@ extern Char* VG_(getenv) ( Char* name );
/* Path to all our library/aux files */
extern const Char *VG_(libdir);
// The name of the LD_PRELOAD-equivalent variable. It varies across
// platforms.
extern const Char* VG_(LD_PRELOAD_var_name);
/* ---------------------------------------------------------------------
Important syscalls
------------------------------------------------------------------ */

View File

@ -545,11 +545,11 @@ file, which will almost certainly make it unreadable by ms_print.</para>
<sect2 id="ms-manual.not-measured"
xreflabel="Memory Allocations Not Measured by Massif">
<title>Memory Allocations Not Measured by Massif</title>
xreflabel="Measuring All Memory in a Process">
<title>Measuring All Memory in a Process</title>
<para>
It is worth emphasising that Massif measures only heap memory, i.e. memory
allocated with
It is worth emphasising that by default Massif measures only heap memory, i.e.
memory allocated with
<function>malloc</function>,
<function>calloc</function>,
<function>realloc</function>,
@ -576,13 +576,49 @@ not the lower-level system calls.
<para>
Furthermore, a client program may use these lower-level system calls
directly to allocate memory. Massif does not measure these. Nor does it
measure the size of code, data and BSS segments. Therefore, the numbers
reported by Massif may be significantly smaller than those reported by tools
such as <filename>top</filename> that measure a program's total size in
directly to allocate memory. By default, Massif does not measure these. Nor
does it measure the size of code, data and BSS segments. Therefore, the
numbers reported by Massif may be significantly smaller than those reported by
tools such as <filename>top</filename> that measure a program's total size in
memory.
</para>
<para>
However, if you wish to measure <emphasis>all</emphasis> the memory used by
your program, you can use the <option>--pages-as-heap=yes</option>. When this
option is enabled, Massif's normal heap block profiling is replaced by
lower-level page profiling. Every page allocated via
<function>mmap</function> and similar system calls is treated as a distinct
block. This means that code, data and BSS segments are all measured, as they
are just memory pages. Even the stack is measured, since it is ultimately
allocated (and extended when necessary) via <function>mmap</function>; for
this reason <option>--stacks=yes</option> is not allowed in conjunction with
<option>--pages-as-heap=yes</option>.
</para>
<para>
After <option>--pages-as-heap=yes</option> is used, ms_print's output is
mostly unchanged. One difference is that the start of each detailed snapshot
says:
</para>
<screen><![CDATA[
(page allocation syscalls) mmap/mremap/brk, --alloc-fns, etc.
]]></screen>
<para>instead of the usual</para>:
<screen><![CDATA[
(heap allocation functions) malloc/new/new[], --alloc-fns, etc.
]]></screen>
<para>
The stack traces in the output may be more difficult to read, and interpreting
them may require some detailed understanding of the lower levels of a program
like the memory allocators. But for some programs having the full information
about memory usage can be very useful.
</para>
</sect2>

View File

@ -176,6 +176,7 @@ Number of snapshots: 50
#include "pub_tool_options.h"
#include "pub_tool_replacemalloc.h"
#include "pub_tool_stacktrace.h"
#include "pub_tool_threadstate.h"
#include "pub_tool_tooliface.h"
#include "pub_tool_xarray.h"
#include "pub_tool_clientstate.h"
@ -190,6 +191,10 @@ Number of snapshots: 50
// of detail, enough to tell how many bytes each line of code is responsible
// for, more or less. The main data structure is a tree representing the
// call tree beneath all the allocation functions like malloc().
// (Alternatively, if --pages-as-heap=yes is specified, memory is tracked at
// the page level, and each page is treated much like a heap block. We use
// "heap" throughout below to cover this case because the concepts are all the
// same.)
//
// "Snapshots" are recordings of the memory usage. There are two basic
// kinds:
@ -280,14 +285,17 @@ static SizeT peak_snapshot_total_szB = 0;
// memory. An alternative to milliseconds as a unit of program "time".
static ULong total_allocs_deallocs_szB = 0;
// We don't start taking snapshots until the first basic block is executed,
// rather than doing it in ms_post_clo_init (which is the obvious spot), for
// two reasons.
// When running with --heap=yes --pages-as-heap=no, we don't start taking
// snapshots until the first basic block is executed, rather than doing it in
// ms_post_clo_init (which is the obvious spot), for two reasons.
// - It lets us ignore stack events prior to that, because they're not
// really proper ones and just would screw things up.
// - Because there's still some core initialisation to do, and so there
// would be an artificial time gap between the first and second snapshots.
//
// When running with --heap=yes --pages-as-heap=yes, snapshots start much
// earlier due to new_mem_startup so this isn't relevant.
//
static Bool have_started_executing_code = False;
//------------------------------------------------------------//
@ -393,12 +401,13 @@ static Char* TimeUnit_to_string(TimeUnit time_unit)
}
}
static Bool clo_heap = True;
static Bool clo_heap = True;
// clo_heap_admin is deliberately a word-sized type. At one point it was
// a UInt, but this caused problems on 64-bit machines when it was
// multiplied by a small negative number and then promoted to a
// word-sized type -- it ended up with a value of 4.2 billion. Sigh.
static SSizeT clo_heap_admin = 8;
static Bool clo_pages_as_heap = False;
static Bool clo_stacks = False;
static Int clo_depth = 30;
static double clo_threshold = 1.0; // percentage
@ -417,29 +426,34 @@ static Bool ms_process_cmd_line_option(Char* arg)
// Remember the arg for later use.
VG_(addToXA)(args_for_massif, &arg);
if VG_BOOL_CLO(arg, "--heap", clo_heap) {}
else if VG_BOOL_CLO(arg, "--stacks", clo_stacks) {}
if VG_BOOL_CLO(arg, "--heap", clo_heap) {}
else if VG_BINT_CLO(arg, "--heap-admin", clo_heap_admin, 0, 1024) {}
else if VG_BINT_CLO(arg, "--heap-admin", clo_heap_admin, 0, 1024) {}
else if VG_BINT_CLO(arg, "--depth", clo_depth, 1, MAX_DEPTH) {}
else if VG_BOOL_CLO(arg, "--stacks", clo_stacks) {}
else if VG_DBL_CLO(arg, "--threshold", clo_threshold) {}
else if VG_BOOL_CLO(arg, "--pages-as-heap", clo_pages_as_heap) {}
else if VG_BINT_CLO(arg, "--depth", clo_depth, 1, MAX_DEPTH) {}
else if VG_STR_CLO(arg, "--alloc-fn", tmp_str) {
VG_(addToXA)(alloc_fns, &tmp_str);
}
else if VG_STR_CLO(arg, "--ignore-fn", tmp_str) {
VG_(addToXA)(ignore_fns, &tmp_str);
}
else if VG_DBL_CLO(arg, "--threshold", clo_threshold) {}
else if VG_DBL_CLO(arg, "--peak-inaccuracy", clo_peak_inaccuracy) {}
else if VG_BINT_CLO(arg, "--detailed-freq", clo_detailed_freq, 1, 10000) {}
else if VG_BINT_CLO(arg, "--max-snapshots", clo_max_snapshots, 10, 1000) {}
else if VG_XACT_CLO(arg, "--time-unit=i", clo_time_unit, TimeI) {}
else if VG_XACT_CLO(arg, "--time-unit=ms", clo_time_unit, TimeMS) {}
else if VG_XACT_CLO(arg, "--time-unit=B", clo_time_unit, TimeB) {}
else if VG_XACT_CLO(arg, "--time-unit=i", clo_time_unit, TimeI) {}
else if VG_XACT_CLO(arg, "--time-unit=ms", clo_time_unit, TimeMS) {}
else if VG_XACT_CLO(arg, "--time-unit=B", clo_time_unit, TimeB) {}
else if VG_BINT_CLO(arg, "--detailed-freq", clo_detailed_freq, 1, 10000) {}
else if VG_BINT_CLO(arg, "--max-snapshots", clo_max_snapshots, 10, 1000) {}
else if VG_STR_CLO(arg, "--alloc-fn", tmp_str) {
VG_(addToXA)(alloc_fns, &tmp_str);
}
else if VG_STR_CLO(arg, "--ignore-fn", tmp_str) {
VG_(addToXA)(ignore_fns, &tmp_str);
}
else if VG_STR_CLO(arg, "--massif-out-file", clo_massif_out_file) {}
else
@ -455,6 +469,7 @@ static void ms_print_usage(void)
" --heap-admin=<size> average admin bytes per heap block;\n"
" ignored if --heap=no [8]\n"
" --stacks=no|yes profile stack(s) [no]\n"
" --pages-as-heap=no|yes profile memory at the page level [no]\n"
" --depth=<number> depth of contexts [30]\n"
" --alloc-fn=<name> specify <name> as an alloc function [empty]\n"
" --ignore-fn=<name> ignore heap allocations within <name> [empty]\n"
@ -842,7 +857,7 @@ static Bool fn_should_be_ignored(Addr ip)
// Nb: it's possible to end up with an empty trace, eg. if 'main' is marked
// as an alloc-fn. This is ok.
static
Int get_IPs( ThreadId tid, Bool is_custom_alloc, Addr ips[])
Int get_IPs( ThreadId tid, Bool exclude_first_entry, Addr ips[])
{
static Char buf[BUF_LEN];
Int n_ips, i, n_alloc_fns_removed;
@ -877,11 +892,11 @@ Int get_IPs( ThreadId tid, Bool is_custom_alloc, Addr ips[])
// If the original stack trace is smaller than asked-for, redo=False.
if (n_ips < clo_depth + overestimate) { redo = False; }
// Filter out alloc fns. If it's a non-custom block, we remove the
// first entry (which will be one of malloc, __builtin_new, etc)
// without looking at it, because VG_(get_fnname) is expensive (it
// involves calls to VG_(malloc)/VG_(free)).
n_alloc_fns_removed = ( is_custom_alloc ? 0 : 1 );
// Filter out alloc fns. If requested, we automatically remove the
// first entry (which presumably will be something like malloc or
// __builtin_new that we're sure to filter out) without looking at it,
// because VG_(get_fnname) is expensive.
n_alloc_fns_removed = ( exclude_first_entry ? 1 : 0 );
for (i = n_alloc_fns_removed; i < n_ips; i++) {
if (VG_(get_fnname)(ips[i], buf, BUF_LEN)) {
if (is_member_fn(alloc_fns, buf)) {
@ -912,14 +927,14 @@ Int get_IPs( ThreadId tid, Bool is_custom_alloc, Addr ips[])
// Gets an XCon and puts it in the tree. Returns the XCon's bottom-XPt.
// Unless the allocation should be ignored, in which case we return NULL.
static XPt* get_XCon( ThreadId tid, Bool is_custom_alloc )
static XPt* get_XCon( ThreadId tid, Bool exclude_first_entry )
{
static Addr ips[MAX_IPS];
Int i;
XPt* xpt = alloc_xpt;
// After this call, the IPs we want are in ips[0]..ips[n_ips-1].
Int n_ips = get_IPs(tid, is_custom_alloc, ips);
Int n_ips = get_IPs(tid, exclude_first_entry, ips);
// Should we ignore this allocation? (Nb: n_ips can be zero, eg. if
// 'main' is marked as an alloc-fn.)
@ -996,7 +1011,7 @@ static XPt* get_XCon( ThreadId tid, Bool is_custom_alloc )
// Update 'szB' of every XPt in the XCon, by percolating upwards.
static void update_XCon(XPt* xpt, SSizeT space_delta)
{
tl_assert(True == clo_heap);
tl_assert(clo_heap);
tl_assert(NULL != xpt);
if (0 == space_delta)
@ -1323,7 +1338,9 @@ take_snapshot(Snapshot* snapshot, SnapshotKind kind, Time my_time,
Bool is_detailed)
{
tl_assert(!is_snapshot_in_use(snapshot));
tl_assert(have_started_executing_code);
if (!clo_pages_as_heap) {
tl_assert(have_started_executing_code);
}
// Heap and heap admin.
if (clo_heap) {
@ -1518,31 +1535,11 @@ static void update_heap_stats(SSizeT heap_szB_delta, Int heap_extra_szB_delta)
}
static
void* new_block ( ThreadId tid, void* p, SizeT req_szB, SizeT req_alignB,
Bool is_zeroed )
void* record_block( ThreadId tid, void* p, SizeT req_szB, SizeT slop_szB,
Bool exclude_first_entry, Bool maybe_snapshot )
{
HP_Chunk* hc;
Bool is_custom_alloc = (NULL != p);
SizeT actual_szB, slop_szB;
if ((SSizeT)req_szB < 0) return NULL;
// Allocate and zero if necessary
if (!p) {
p = VG_(cli_malloc)( req_alignB, req_szB );
if (!p) {
return NULL;
}
if (is_zeroed) VG_(memset)(p, 0, req_szB);
actual_szB = VG_(malloc_usable_size)(p);
tl_assert(actual_szB >= req_szB);
slop_szB = actual_szB - req_szB;
} else {
slop_szB = 0;
}
// Make new HP_Chunk node, add to malloc_list
hc = VG_(malloc)("ms.main.nb.1", sizeof(HP_Chunk));
HP_Chunk* hc = VG_(malloc)("ms.main.rb.1", sizeof(HP_Chunk));
hc->req_szB = req_szB;
hc->slop_szB = slop_szB;
hc->data = (Addr)p;
@ -1550,9 +1547,9 @@ void* new_block ( ThreadId tid, void* p, SizeT req_szB, SizeT req_alignB,
VG_(HT_add_node)(malloc_list, hc);
if (clo_heap) {
VERB(3, "<<< new_mem_heap (%lu, %lu)\n", req_szB, slop_szB);
VERB(3, "<<< record_block (%lu, %lu)\n", req_szB, slop_szB);
hc->where = get_XCon( tid, is_custom_alloc );
hc->where = get_XCon( tid, exclude_first_entry );
if (hc->where) {
// Update statistics.
@ -1565,7 +1562,9 @@ void* new_block ( ThreadId tid, void* p, SizeT req_szB, SizeT req_alignB,
update_XCon(hc->where, req_szB);
// Maybe take a snapshot.
maybe_take_snapshot(Normal, " alloc");
if (maybe_snapshot) {
maybe_take_snapshot(Normal, " alloc");
}
} else {
// Ignored allocation.
@ -1581,7 +1580,33 @@ void* new_block ( ThreadId tid, void* p, SizeT req_szB, SizeT req_alignB,
}
static __inline__
void die_block ( void* p, Bool custom_free )
void* alloc_and_record_block ( ThreadId tid, SizeT req_szB, SizeT req_alignB,
Bool is_zeroed )
{
SizeT actual_szB, slop_szB;
void* p;
if ((SSizeT)req_szB < 0) return NULL;
// Allocate and zero if necessary.
p = VG_(cli_malloc)( req_alignB, req_szB );
if (!p) {
return NULL;
}
if (is_zeroed) VG_(memset)(p, 0, req_szB);
actual_szB = VG_(malloc_usable_size)(p);
tl_assert(actual_szB >= req_szB);
slop_szB = actual_szB - req_szB;
// Record block.
record_block(tid, p, req_szB, slop_szB, /*exclude_first_entry*/True,
/*maybe_snapshot*/True);
return p;
}
static __inline__
void unrecord_block ( void* p, Bool maybe_snapshot )
{
// Remove HP_Chunk from malloc_list
HP_Chunk* hc = VG_(HT_remove)(malloc_list, (UWord)p);
@ -1590,14 +1615,16 @@ void die_block ( void* p, Bool custom_free )
}
if (clo_heap) {
VERB(3, "<<< die_mem_heap\n");
VERB(3, "<<< unrecord_block\n");
if (hc->where) {
// Update statistics.
n_heap_frees++;
// Maybe take a peak snapshot, since it's a deallocation.
maybe_take_snapshot(Peak, "de-PEAK");
if (maybe_snapshot) {
maybe_take_snapshot(Peak, "de-PEAK");
}
// Update heap stats.
update_heap_stats(-hc->req_szB, -clo_heap_admin - hc->slop_szB);
@ -1606,7 +1633,9 @@ void die_block ( void* p, Bool custom_free )
update_XCon(hc->where, -hc->req_szB);
// Maybe take a snapshot.
maybe_take_snapshot(Normal, "dealloc");
if (maybe_snapshot) {
maybe_take_snapshot(Normal, "dealloc");
}
} else {
n_ignored_heap_frees++;
@ -1619,8 +1648,6 @@ void die_block ( void* p, Bool custom_free )
// Actually free the chunk, and the heap block (if necessary)
VG_(free)( hc ); hc = NULL;
if (!custom_free)
VG_(cli_free)( p );
}
// Nb: --ignore-fn is tricky for realloc. If the block's original alloc was
@ -1630,7 +1657,7 @@ void die_block ( void* p, Bool custom_free )
// growing such a block, but for consistency (it also simplifies things) we
// ignore such reallocs as well.
static __inline__
void* renew_block ( ThreadId tid, void* p_old, SizeT new_req_szB )
void* realloc_block ( ThreadId tid, void* p_old, SizeT new_req_szB )
{
HP_Chunk* hc;
void* p_new;
@ -1647,8 +1674,9 @@ void* renew_block ( ThreadId tid, void* p_old, SizeT new_req_szB )
old_req_szB = hc->req_szB;
old_slop_szB = hc->slop_szB;
tl_assert(!clo_pages_as_heap); // Shouldn't be here if --pages-as-heap=yes.
if (clo_heap) {
VERB(3, "<<< renew_mem_heap (%lu)\n", new_req_szB);
VERB(3, "<<< realloc_block (%lu)\n", new_req_szB);
if (hc->where) {
// Update statistics.
@ -1696,7 +1724,7 @@ void* renew_block ( ThreadId tid, void* p_old, SizeT new_req_szB )
// Update XTree.
if (clo_heap) {
new_where = get_XCon( tid, /*custom_malloc*/False);
new_where = get_XCon( tid, /*exclude_first_entry*/True);
if (!is_ignored && new_where) {
hc->where = new_where;
update_XCon(old_where, -old_req_szB);
@ -1745,47 +1773,50 @@ void* renew_block ( ThreadId tid, void* p_old, SizeT new_req_szB )
static void* ms_malloc ( ThreadId tid, SizeT szB )
{
return new_block( tid, NULL, szB, VG_(clo_alignment), /*is_zeroed*/False );
return alloc_and_record_block( tid, szB, VG_(clo_alignment), /*is_zeroed*/False );
}
static void* ms___builtin_new ( ThreadId tid, SizeT szB )
{
return new_block( tid, NULL, szB, VG_(clo_alignment), /*is_zeroed*/False );
return alloc_and_record_block( tid, szB, VG_(clo_alignment), /*is_zeroed*/False );
}
static void* ms___builtin_vec_new ( ThreadId tid, SizeT szB )
{
return new_block( tid, NULL, szB, VG_(clo_alignment), /*is_zeroed*/False );
return alloc_and_record_block( tid, szB, VG_(clo_alignment), /*is_zeroed*/False );
}
static void* ms_calloc ( ThreadId tid, SizeT m, SizeT szB )
{
return new_block( tid, NULL, m*szB, VG_(clo_alignment), /*is_zeroed*/True );
return alloc_and_record_block( tid, m*szB, VG_(clo_alignment), /*is_zeroed*/True );
}
static void *ms_memalign ( ThreadId tid, SizeT alignB, SizeT szB )
{
return new_block( tid, NULL, szB, alignB, False );
return alloc_and_record_block( tid, szB, alignB, False );
}
static void ms_free ( ThreadId tid __attribute__((unused)), void* p )
{
die_block( p, /*custom_free*/False );
unrecord_block(p, /*maybe_snapshot*/True);
VG_(cli_free)(p);
}
static void ms___builtin_delete ( ThreadId tid, void* p )
{
die_block( p, /*custom_free*/False);
unrecord_block(p, /*maybe_snapshot*/True);
VG_(cli_free)(p);
}
static void ms___builtin_vec_delete ( ThreadId tid, void* p )
{
die_block( p, /*custom_free*/False );
unrecord_block(p, /*maybe_snapshot*/True);
VG_(cli_free)(p);
}
static void* ms_realloc ( ThreadId tid, void* p_old, SizeT new_szB )
{
return renew_block(tid, p_old, new_szB);
return realloc_block(tid, p_old, new_szB);
}
static SizeT ms_malloc_usable_size ( ThreadId tid, void* p )
@ -1795,6 +1826,89 @@ static SizeT ms_malloc_usable_size ( ThreadId tid, void* p )
return ( hc ? hc->req_szB + hc->slop_szB : 0 );
}
//------------------------------------------------------------//
//--- Page handling ---//
//------------------------------------------------------------//
static
void ms_record_page_mem ( Addr a, SizeT len )
{
ThreadId tid = VG_(get_running_tid)();
Addr end;
tl_assert(VG_IS_PAGE_ALIGNED(len));
tl_assert(len >= VKI_PAGE_SIZE);
// Record the first N-1 pages as blocks, but don't do any snapshots.
for (end = a + len - VKI_PAGE_SIZE; a < end; a += VKI_PAGE_SIZE) {
record_block( tid, (void*)a, VKI_PAGE_SIZE, /*slop_szB*/0,
/*exclude_first_entry*/False, /*maybe_snapshot*/False );
}
// Record the last page as a block, and maybe do a snapshot afterwards.
record_block( tid, (void*)a, VKI_PAGE_SIZE, /*slop_szB*/0,
/*exclude_first_entry*/False, /*maybe_snapshot*/True );
}
static
void ms_unrecord_page_mem( Addr a, SizeT len )
{
Addr end;
tl_assert(VG_IS_PAGE_ALIGNED(len));
tl_assert(len >= VKI_PAGE_SIZE);
for (end = a + len - VKI_PAGE_SIZE; a < end; a += VKI_PAGE_SIZE) {
unrecord_block((void*)a, /*maybe_snapshot*/False);
}
unrecord_block((void*)a, /*maybe_snapshot*/True);
}
//------------------------------------------------------------//
static
void ms_new_mem_mmap ( Addr a, SizeT len,
Bool rr, Bool ww, Bool xx, ULong di_handle )
{
tl_assert(VG_IS_PAGE_ALIGNED(len));
ms_record_page_mem(a, len);
}
static
void ms_new_mem_startup( Addr a, SizeT len,
Bool rr, Bool ww, Bool xx, ULong di_handle )
{
// startup maps are always be page-sized, except the trampoline page is
// marked by the core as only being the size of the trampoline itself,
// which is something like 57 bytes. Round it up to page size.
len = VG_PGROUNDUP(len);
ms_record_page_mem(a, len);
}
static
void ms_new_mem_brk ( Addr a, SizeT len, ThreadId tid )
{
tl_assert(VG_IS_PAGE_ALIGNED(len));
ms_record_page_mem(a, len);
}
static
void ms_copy_mem_remap( Addr from, Addr to, SizeT len)
{
tl_assert(VG_IS_PAGE_ALIGNED(len));
ms_unrecord_page_mem(from, len);
ms_record_page_mem(to, len);
}
static
void ms_die_mem_munmap( Addr a, SizeT len )
{
tl_assert(VG_IS_PAGE_ALIGNED(len));
ms_unrecord_page_mem(a, len);
}
static
void ms_die_mem_brk( Addr a, SizeT len )
{
tl_assert(VG_IS_PAGE_ALIGNED(len));
ms_unrecord_page_mem(a, len);
}
//------------------------------------------------------------//
//--- Stacks ---//
//------------------------------------------------------------//
@ -1862,17 +1976,16 @@ static Bool ms_handle_client_request ( ThreadId tid, UWord* argv, UWord* ret )
{
switch (argv[0]) {
case VG_USERREQ__MALLOCLIKE_BLOCK: {
void* res;
void* p = (void*)argv[1];
SizeT szB = argv[2];
res = new_block( tid, p, szB, /*alignB--ignored*/0, /*is_zeroed*/False );
tl_assert(res == p);
record_block( tid, p, szB, /*slop_szB*/0, /*exclude_first_entry*/False,
/*maybe_snapshot*/True );
*ret = 0;
return True;
}
case VG_USERREQ__FREELIKE_BLOCK: {
void* p = (void*)argv[1];
die_block( p, /*custom_free*/True );
unrecord_block(p, /*maybe_snapshot*/True);
*ret = 0;
return True;
}
@ -2019,8 +2132,15 @@ static void pp_snapshot_SXPt(Int fd, SXPt* sxpt, Int depth, Char* depth_str,
case SigSXPt:
// Print the SXPt itself.
if (0 == depth) {
ip_desc =
"(heap allocation functions) malloc/new/new[], --alloc-fns, etc.";
if (clo_heap) {
ip_desc =
( clo_pages_as_heap
? "(page allocation syscalls) mmap/mremap/brk, --alloc-fns, etc."
: "(heap allocation functions) malloc/new/new[], --alloc-fns, etc."
);
} else {
// XXX: --alloc-fns?
}
} else {
// If it's main-or-below-main, we (if appropriate) ignore everything
// below it by pretending it has no children.
@ -2261,17 +2381,56 @@ static void ms_fini(Int exit_status)
static void ms_post_clo_init(void)
{
Int i;
Char* LD_PRELOAD_val;
Char* s;
Char* s2;
// Check options.
if (clo_threshold < 0 || clo_threshold > 100) {
VG_(umsg)("--threshold must be between 0.0 and 100.0\n");
VG_(err_bad_option)("--threshold");
}
// If we have --heap=no, set --heap-admin to zero, just to make sure we
// don't accidentally use a non-zero heap-admin size somewhere.
if (clo_pages_as_heap) {
if (clo_stacks) {
VG_(umsg)("--pages-as-heap=yes cannot be used with --stacks=yes\n");
VG_(err_bad_option)("--pages-as-heap=yes with --stacks=yes");
}
}
if (!clo_heap) {
clo_heap_admin = 0;
clo_pages_as_heap = False;
}
// If --pages-as-heap=yes we don't want malloc replacement to occur. So we
// disable vgpreload_massif-$PLATFORM.so by removing it from LD_PRELOAD (or
// platform-equivalent). We replace it entirely with spaces because then
// the linker doesn't complain (it does complain if we just change the name
// to a bogus file). This is a bit of a hack, but LD_PRELOAD is setup well
// before tool initialisation, so this seems the best way to do it.
if (clo_pages_as_heap) {
clo_heap_admin = 0; // No heap admin on pages.
LD_PRELOAD_val = VG_(getenv)( (Char*)VG_(LD_PRELOAD_var_name) );
tl_assert(LD_PRELOAD_val);
// Make sure the vgpreload_core-$PLATFORM entry is there, for sanity.
s2 = VG_(strstr)(LD_PRELOAD_val, "vgpreload_core");
tl_assert(s2);
// Now find the vgpreload_massif-$PLATFORM entry.
s2 = VG_(strstr)(LD_PRELOAD_val, "vgpreload_massif");
tl_assert(s2);
// Blank out everything to the previous ':', which must be there because
// of the preceding vgpreload_core-$PLATFORM entry.
for (s = s2; *s != ':'; s--) {
*s = ' ';
}
// Blank out everything to the end of the entry, which will be '\0' if
// LD_PRELOAD was empty before Valgrind started, or ':' otherwise.
for (s = s2; *s != ':' && *s != '\0'; s++) {
*s = ' ';
}
}
// Print alloc-fns and ignore-fns, if necessary.
@ -2300,6 +2459,17 @@ static void ms_post_clo_init(void)
VG_(track_die_mem_stack_signal) ( die_mem_stack_signal );
}
if (clo_pages_as_heap) {
VG_(track_new_mem_startup) ( ms_new_mem_startup );
VG_(track_new_mem_brk) ( ms_new_mem_brk );
VG_(track_new_mem_mmap) ( ms_new_mem_mmap );
VG_(track_copy_mem_remap) ( ms_copy_mem_remap );
VG_(track_die_mem_brk) ( ms_die_mem_brk );
VG_(track_die_mem_munmap) ( ms_die_mem_munmap );
}
// Initialise snapshot array, and sanity-check it.
snapshots = VG_(malloc)("ms.main.mpoci.1",
sizeof(Snapshot) * clo_max_snapshots);