mirror of
git://sourceware.org/git/glibc.git
synced 2026-09-09 05:59:27 +08:00
Compare commits
44
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02f4ecb623 | ||
|
|
8926b32dba | ||
|
|
4f460c8944 | ||
|
|
275d577540 | ||
|
|
528795b019 | ||
|
|
4a6779937b | ||
|
|
ae99936eea | ||
|
|
56414a409a | ||
|
|
599a9da8e9 | ||
|
|
6f47f3e632 | ||
|
|
c841d237c1 | ||
|
|
7a738e7f52 | ||
|
|
b096a21663 | ||
|
|
9c879c0b31 | ||
|
|
f214e72974 | ||
|
|
f57aaed3b2 | ||
|
|
2c5e3bf401 | ||
|
|
b28259c68e | ||
|
|
25cfec5974 | ||
|
|
f9a7d78b73 | ||
|
|
e4650ee4a8 | ||
|
|
fcf17bd0d9 | ||
|
|
0eacff38a3 | ||
|
|
cfc89672ed | ||
|
|
497bcfe9ac | ||
|
|
6ce11061fb | ||
|
|
b856f645a2 | ||
|
|
e4cff5fd83 | ||
|
|
77e65f7bfe | ||
|
|
1329743b58 | ||
|
|
2d7af358a3 | ||
|
|
7328eadb40 | ||
|
|
0742c96f78 | ||
|
|
8aecd66e28 | ||
|
|
06f4408f64 | ||
|
|
46b44370f4 | ||
|
|
5bd0885c9d | ||
|
|
9ca7776445 | ||
|
|
d6045255ae | ||
|
|
d35e84028c | ||
|
|
9c79af3a2c | ||
|
|
1322011d96 | ||
|
|
649255b5d3 | ||
|
|
850602a447 |
+1
-1
@@ -848,7 +848,7 @@ endif # $(+cflags) == ""
|
||||
libio-include = -I$(..)libio
|
||||
|
||||
# List of non-library modules that we build.
|
||||
built-modules = iconvprogs iconvdata ldconfig lddlibc4 libmemusage \
|
||||
built-modules = iconvprogs iconvdata ldconfig lddlibc4 libmemusage libmtracectl \
|
||||
libSegFault libpcprofile librpcsvc locale-programs \
|
||||
memusagestat nonlib nscd extramodules libnldbl
|
||||
|
||||
|
||||
@@ -74,6 +74,8 @@ multi-arch = @multi_arch@
|
||||
|
||||
mach-interface-list = @mach_interface_list@
|
||||
|
||||
experimental-malloc = @experimental_malloc@
|
||||
|
||||
nss-crypt = @libc_cv_nss_crypt@
|
||||
|
||||
# Configuration options.
|
||||
|
||||
@@ -666,6 +666,7 @@ build_pt_chown
|
||||
build_nscd
|
||||
link_obsolete_rpc
|
||||
libc_cv_nss_crypt
|
||||
experimental_malloc
|
||||
enable_werror
|
||||
all_warnings
|
||||
force_install
|
||||
@@ -770,6 +771,7 @@ enable_kernel
|
||||
enable_all_warnings
|
||||
enable_werror
|
||||
enable_multi_arch
|
||||
enable_experimental_malloc
|
||||
enable_nss_crypt
|
||||
enable_obsolete_rpc
|
||||
enable_systemtap
|
||||
@@ -1436,6 +1438,8 @@ Optional Features:
|
||||
--disable-werror do not build with -Werror
|
||||
--enable-multi-arch enable single DSO with optimizations for multiple
|
||||
architectures
|
||||
--disable-experimental-malloc
|
||||
disable experimental malloc features
|
||||
--enable-nss-crypt enable libcrypt to use nss
|
||||
--enable-obsolete-rpc build and install the obsolete RPC code for
|
||||
link-time usage
|
||||
@@ -3492,6 +3496,15 @@ else
|
||||
fi
|
||||
|
||||
|
||||
# Check whether --enable-experimental-malloc was given.
|
||||
if test "${enable_experimental_malloc+set}" = set; then :
|
||||
enableval=$enable_experimental_malloc; experimental_malloc=$enableval
|
||||
else
|
||||
experimental_malloc=yes
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Check whether --enable-nss-crypt was given.
|
||||
if test "${enable_nss_crypt+set}" = set; then :
|
||||
enableval=$enable_nss_crypt; nss_crypt=$enableval
|
||||
|
||||
@@ -301,6 +301,13 @@ AC_ARG_ENABLE([multi-arch],
|
||||
[multi_arch=$enableval],
|
||||
[multi_arch=default])
|
||||
|
||||
AC_ARG_ENABLE([experimental-malloc],
|
||||
AC_HELP_STRING([--disable-experimental-malloc],
|
||||
[disable experimental malloc features]),
|
||||
[experimental_malloc=$enableval],
|
||||
[experimental_malloc=yes])
|
||||
AC_SUBST(experimental_malloc)
|
||||
|
||||
AC_ARG_ENABLE([nss-crypt],
|
||||
AC_HELP_STRING([--enable-nss-crypt],
|
||||
[enable libcrypt to use nss]),
|
||||
|
||||
+22
-3
@@ -49,7 +49,7 @@ install-lib := libmcheck.a
|
||||
non-lib.a := libmcheck.a
|
||||
|
||||
# Additional library.
|
||||
extra-libs = libmemusage
|
||||
extra-libs = libmemusage libmtracectl
|
||||
extra-libs-others = $(extra-libs)
|
||||
|
||||
# Helper objects for some tests.
|
||||
@@ -64,11 +64,30 @@ test-extras = \
|
||||
libmemusage-routines = memusage
|
||||
libmemusage-inhibit-o = $(filter-out .os,$(object-suffixes))
|
||||
|
||||
libmtracectl-routines = mtrace-ctl
|
||||
libmtracectl-inhibit-o = $(filter-out .os,$(object-suffixes))
|
||||
|
||||
others: $(objpfx)trace_run $(objpfx)trace2wl $(objpfx)trace_dump
|
||||
install-bin = trace_run trace2wl trace_dump
|
||||
|
||||
$(objpfx)trace_run: $(objpfx)trace_run.o
|
||||
$(LINK.o) -o $@ $(objpfx)trace_run.o -lpthread
|
||||
|
||||
$(objpfx)trace_dump: $(objpfx)trace_dump.o
|
||||
$(LINK.o) -o $@ $(objpfx)trace_dump.o
|
||||
|
||||
$(objpfx)trace2wl: $(objpfx)trace2wl.o
|
||||
$(LINK.o) -o $@ $(objpfx)trace2wl.o
|
||||
|
||||
$(objpfx)tst-malloc-backtrace: $(shared-thread-library)
|
||||
$(objpfx)tst-malloc-thread-exit: $(shared-thread-library)
|
||||
$(objpfx)tst-malloc-thread-fail: $(shared-thread-library)
|
||||
$(objpfx)tst-malloc-fork-deadlock: $(shared-thread-library)
|
||||
|
||||
ifeq ($(experimental-malloc),yes)
|
||||
CPPFLAGS-malloc.c += -DUSE_TCACHE
|
||||
endif
|
||||
|
||||
# Export the __malloc_initialize_hook variable to libc.so.
|
||||
LDFLAGS-tst-mallocstate = -rdynamic
|
||||
|
||||
@@ -80,7 +99,7 @@ aux := set-freeres thread-freeres
|
||||
|
||||
# The Perl script to analyze the output of the mtrace functions.
|
||||
ifneq ($(PERL),no)
|
||||
install-bin-script = mtrace
|
||||
install-bin-script += mtrace
|
||||
generated += mtrace
|
||||
|
||||
# The Perl script will print addresses and to do this nicely we must know
|
||||
@@ -99,7 +118,7 @@ ifneq ($(cross-compiling),yes)
|
||||
# If the gd library is available we build the `memusagestat' program.
|
||||
ifneq ($(LIBGD),no)
|
||||
others: $(objpfx)memusage
|
||||
install-bin = memusagestat
|
||||
install-bin += memusagestat
|
||||
install-bin-script += memusage
|
||||
generated += memusagestat memusage
|
||||
extra-objs += memusagestat.o
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
<title>Using the dj/malloc GLIBC COPR repo</title>
|
||||
<H1 align=center>Using the dj/malloc GLIBC COPR repo</H1>
|
||||
|
||||
<p>The purpose of this document is to assist folks in testing out my
|
||||
custom dj/malloc branch of the upstream GLIBC git repo. This COPR
|
||||
repo has pre-built RPMs for easy installation in a test
|
||||
environment.</p>
|
||||
|
||||
See <a href="https://copr.fedorainfracloud.org/coprs/djdelorie/glibc_dj_malloc/">https://copr.fedorainfracloud.org/coprs/djdelorie/glibc_dj_malloc/</a>
|
||||
for links and other information.
|
||||
|
||||
<h2>Installing the COPR Repo</h2>
|
||||
|
||||
<pre>
|
||||
$ <b>cd /etc/yum.repos.d/</b>
|
||||
</pre>
|
||||
|
||||
<h3>RHEL7</h3>
|
||||
|
||||
|
||||
<pre>
|
||||
$ <b>wget https://copr.fedorainfracloud.org/coprs/djdelorie/glibc_dj_malloc/repo/epel-7/djdelorie-glibc_dj_malloc-epel-7.repo</b>
|
||||
|
||||
$ <b>yum update</b>
|
||||
|
||||
$ <b>init 6</b>
|
||||
</pre>
|
||||
|
||||
<h3>Fedora</h3>
|
||||
|
||||
<pre>
|
||||
$ <b>dnf copr enable djdelorie/glibc_dj_malloc</b>
|
||||
|
||||
$ <b>dnf clean all</b> (optional)
|
||||
$ <b>dnf update</b>
|
||||
|
||||
$ <b>init 6</b>
|
||||
</pre>
|
||||
|
||||
<h3>Missing Dependencies</h2>
|
||||
|
||||
If dnf complains about missing dependencies, see if you have
|
||||
non-x86_64 variants of glibc installed, and remove them:
|
||||
|
||||
<pre>
|
||||
$ <b>rpm -qa | grep ^glibc | grep -v x86_64</b>
|
||||
</pre>
|
||||
|
||||
<h3>Confirming Installation</h2>
|
||||
|
||||
<pre>
|
||||
$ <b>rpm -qa | grep glibc</b>
|
||||
glibc-all-langpacks-2.23.90-alphadj9.fc23.x86_64
|
||||
glibc-2.23.90-alphadj9.fc23.x86_64
|
||||
glibc-common-2.23.90-alphadj9.fc23.x86_64
|
||||
</pre>
|
||||
|
||||
<h2>Capturing to the Trace Buffer</h2>
|
||||
|
||||
<p>One key new feature in this malloc is a high-speed trace buffer
|
||||
that records every malloc, free, etc call with a minimum of added
|
||||
latency. This is an improvement over the existing trace feature for
|
||||
applications that are performance-critical. There is a private
|
||||
(i.e. glibc-internal) API for activating this buffer, which is
|
||||
enabled via a provided DSO:</p>
|
||||
|
||||
<pre>
|
||||
$ <b>LD_PRELOAD=/lib64/libmtracectl.so ls</b>
|
||||
</pre>
|
||||
|
||||
<p>Replace lib64 with lib, or whatever suitable path you've installed
|
||||
into, for 32-bit machines or machines with non-standard layouts, which
|
||||
I don't support, but you never know...</p>
|
||||
|
||||
<pre>
|
||||
$ <b>ls -l /tmp/mtrace-*</b>
|
||||
-rw-r--r--. 1 root root 12422 Jun 2 20:53 mtrace.out.1188
|
||||
</pre>
|
||||
|
||||
<p>Each generated file is a binary file, specific to the architecture,
|
||||
with one record per trace record entry. Some programs are included
|
||||
in the COPR repo to process the generated files. Please make sure
|
||||
you process these files on the same architecture as they were
|
||||
generated on.</p>
|
||||
|
||||
<h2>Sending Us Trace Files</h2>
|
||||
|
||||
<p>If we ask you to send us a trace file, please rename and compress
|
||||
it to make the file easier to transfer and keep track of.</p>
|
||||
|
||||
|
||||
<pre>
|
||||
$ <b>cd /tmp</b>
|
||||
$ <b>gzip -9 mtrace.out.1188</b>
|
||||
$ <b>mv mtrace.out.1188.gz f24-ls-fred.mtrace.gz</b> (or whatever name fits :)
|
||||
</pre>
|
||||
|
||||
<p>Then mail <tt>f24-ls-fred.mtrace.gz</tt> to dj@redhat.com (or
|
||||
whoever is asking for it, of course)</p>
|
||||
|
||||
<h2>Workload Simulator</h2>
|
||||
|
||||
<p>This build also includes a set of tools to "play back" a recorded
|
||||
trace, which can be helpful in diagnosing memory-related performance
|
||||
issues. Such workloads might be locally generated as part of a
|
||||
benchmark suite, for example.</p>
|
||||
|
||||
<pre>
|
||||
trace2wl <em>outfile</em> [<em>infile ...</em>]
|
||||
</pre>
|
||||
|
||||
If an infile is not provided, input is read from stdin.
|
||||
|
||||
<pre>
|
||||
$ trace2wl /tmp/ls.wl /tmp/mtrace-22172.out
|
||||
</pre>
|
||||
|
||||
The resulting file is a "workload" - a data file that tells the
|
||||
simulator how to play back all the malloc/free/etc calls. This file
|
||||
is not human-readable, but a compact binary datafile intended to be
|
||||
used only by the simulator.
|
||||
|
||||
<pre>
|
||||
trace_run <em>workload.wl</em>
|
||||
</pre>
|
||||
|
||||
<p>Note: trace_run only works on intel processors with the RDTSCP
|
||||
opcode, which is only available on reasonably modern processors. To
|
||||
see if your processor supports this opcode, look for
|
||||
the <b>rdtscp</b> cpu flag:
|
||||
|
||||
<pre>
|
||||
$ <b>grep rdtscp /proc/cpuinfo</b>
|
||||
</pre>
|
||||
|
||||
If you get lines like "flags : <lots of flags>" then you have support
|
||||
and trace_run will work. If the grep returns nothing, you don't.
|
||||
|
||||
<pre>
|
||||
$ <b>trace_run /tmp/ls.wl</b>
|
||||
488,004 cycles
|
||||
106 usec wall time
|
||||
0 usec across 1 thread
|
||||
0 Kb Max RSS (1,228 -> 1,228)
|
||||
|
||||
Avg malloc time: 385 in 154 calls
|
||||
Avg calloc time: 0 in 1 calls
|
||||
Avg realloc time: 0 in 1 calls
|
||||
Avg free time: 194 in 14 calls
|
||||
Total call time: 62,033 cycles
|
||||
</pre>
|
||||
|
||||
Note:
|
||||
see <a href="http://developers.redhat.com/blog/2016/03/11/practical-micro-benchmarking-with-ltrace-and-sched/">Practical
|
||||
Micro-Benchmarking with ltrace and sched</a> to get more stable
|
||||
numbers.
|
||||
|
||||
<h2>Tunables</h2>
|
||||
|
||||
<pre>
|
||||
MALLOC_TCACHE_COUNT=<i>count</i>
|
||||
MALLOC_TCACHE_MAX=<i>bytes</i>
|
||||
</pre>
|
||||
|
||||
<p><tt>count</tt> can be anything from 0 to whatever.</p>
|
||||
|
||||
<p><tt>bytes</tt> can be anything from 0 to 63*2*sizeof(void *)-1 (503
|
||||
for 32-bit, 1007 for 64-bit).</p>
|
||||
|
||||
<p><tt>mallopt</tt> parameters are (private):</p>
|
||||
|
||||
<pre>
|
||||
#define M_TCACHE_COUNT -9
|
||||
#define M_TCACHE_MAX -10
|
||||
</pre>
|
||||
|
||||
<h2>Uninstalling</h2>
|
||||
|
||||
To uninstall the custom build and revert to an official release, you
|
||||
"simly" disable the COPR repo and downgrade to the latest "released" version:
|
||||
|
||||
<pre>
|
||||
$ <b>vi /etc/yum.repos.d/_copr_djdelorie-glibc_dj_malloc.repo</b>
|
||||
</pre>
|
||||
|
||||
change this line from 1 to 0:
|
||||
|
||||
<pre>
|
||||
enabled=0
|
||||
</pre>
|
||||
|
||||
Then:
|
||||
|
||||
<pre>
|
||||
$ <b>dnf --allowerasing downgrade glibc</b>
|
||||
</pre>
|
||||
|
||||
(replace "dnf" with "yum" for RHEL 7)
|
||||
@@ -72,5 +72,14 @@ libc {
|
||||
__libc_scratch_buffer_grow;
|
||||
__libc_scratch_buffer_grow_preserve;
|
||||
__libc_scratch_buffer_set_array_size;
|
||||
|
||||
# malloc trace hooks for mtrace-ctl
|
||||
__malloc_trace_init;
|
||||
__malloc_trace_pause;
|
||||
__malloc_trace_unpause;
|
||||
__malloc_trace_stop;
|
||||
__malloc_trace_sync;
|
||||
|
||||
__malloc_scan_chunks;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,7 +319,23 @@ ptmalloc_init (void)
|
||||
if (memcmp (envline, "ARENA_TEST", 10) == 0)
|
||||
__libc_mallopt (M_ARENA_TEST, atoi (&envline[11]));
|
||||
}
|
||||
#if USE_TCACHE
|
||||
if (!__builtin_expect (__libc_enable_secure, 0))
|
||||
{
|
||||
if (memcmp (envline, "TCACHE_MAX", 10) == 0)
|
||||
__libc_mallopt (M_TCACHE_MAX, atoi (&envline[11]));
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
#if USE_TCACHE
|
||||
case 12:
|
||||
if (!__builtin_expect (__libc_enable_secure, 0))
|
||||
{
|
||||
if (memcmp (envline, "TCACHE_COUNT", 12) == 0)
|
||||
__libc_mallopt (M_TCACHE_COUNT, atoi (&envline[13]));
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case 15:
|
||||
if (!__builtin_expect (__libc_enable_secure, 0))
|
||||
{
|
||||
|
||||
+1079
-24
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,195 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* This module is a stand-alone control program for malloc's internal
|
||||
trace buffer. It is intended to be preloaded like this:
|
||||
|
||||
LD_PRELOAD=/usr/lib/libmtracectl.so ./myprog
|
||||
|
||||
This module uses the following environment variables:
|
||||
|
||||
MTRACE_CTL_COUNT - how many records to store (default: 1000). Each
|
||||
record is 32 bytes, and the entire buffer is mmap'd at once. If
|
||||
the buffer isn't big enough, it will overwrite early records with
|
||||
newer ones. The total number of trace records is reported in the
|
||||
output file so that a larger buffer may be allocated on future runs.
|
||||
|
||||
MTRACE_CTL_FILE - the output file name (default:
|
||||
/tmp/mtrace-$$.out). Note that the default is per-pid but there is
|
||||
no way to specify a per-pid pattern via this environment variable.
|
||||
|
||||
The output file will contain a header that says how many trace
|
||||
records were seen (which is usually more or less than the trace
|
||||
buffer size). The trace buffer is then dumped one entry per line.
|
||||
|
||||
*/
|
||||
|
||||
#include "mtrace.h"
|
||||
|
||||
#define estr(str) write (2, str, strlen (str))
|
||||
|
||||
#if 0
|
||||
static void
|
||||
err(const char *str)
|
||||
{
|
||||
estr (str);
|
||||
estr ("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* mtrace_start - checks for buffer, allocates one if needed, starts trace.
|
||||
* mtrace_stop - stops tracing
|
||||
* mtrace_sync - syncs the buffer
|
||||
* mtrace_reset - resets buffer state to intial state
|
||||
*/
|
||||
|
||||
struct _malloc_trace_buffer_s *mtrace_buffer = NULL;
|
||||
size_t mtrace_buffer_bytesize = 0;
|
||||
|
||||
int
|
||||
mtrace_start (void)
|
||||
{
|
||||
const char *e;
|
||||
char *fname;
|
||||
int sequence = 0;
|
||||
|
||||
e = getenv("MTRACE_CTL_COUNT");
|
||||
if (!e)
|
||||
e = "1000";
|
||||
|
||||
e = getenv("MTRACE_CTL_FILE");
|
||||
if (!e)
|
||||
e = "/tmp/mtrace.out";
|
||||
|
||||
fname = alloca (strlen(e) + 30);
|
||||
sprintf(fname, "%s.%d", e, getpid());
|
||||
while (access (fname, F_OK) == 0)
|
||||
{
|
||||
sequence ++;
|
||||
sprintf(fname, "%s.%d.%d", e, getpid(), sequence);
|
||||
}
|
||||
|
||||
estr ("mtrace-ctl: writing to ");
|
||||
estr (fname);
|
||||
estr ("\n");
|
||||
|
||||
__malloc_trace_init (fname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
mtrace_stop (void)
|
||||
{
|
||||
size_t count;
|
||||
char line[100];
|
||||
|
||||
count = __malloc_trace_stop ();
|
||||
sprintf (line, "mtrace-ctl: %lld entries recorded\n", (long long)count);
|
||||
estr (line);
|
||||
}
|
||||
|
||||
void
|
||||
mtrace_sync (void)
|
||||
{
|
||||
__malloc_trace_sync ();
|
||||
// __malloc_trace_buffer_ptr buf = __malloc_get_trace_buffer (&size, &head);
|
||||
// msync (buf, size * sizeof(struct __malloc_trace_buffer_s), MS_SYNC | MS_INVALIDATE);
|
||||
}
|
||||
|
||||
void
|
||||
mtrace_reset (void)
|
||||
{
|
||||
|
||||
__malloc_trace_stop ();
|
||||
mtrace_start ();
|
||||
}
|
||||
|
||||
void __attribute__((constructor))
|
||||
mtrace_ctor(void)
|
||||
{
|
||||
if (mtrace_start ())
|
||||
exit (1);
|
||||
}
|
||||
|
||||
void __attribute__((destructor))
|
||||
mtrace_dtor(void)
|
||||
{
|
||||
mtrace_stop ();
|
||||
mtrace_sync ();
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
const char * const typenames[] = {
|
||||
"unused ",
|
||||
"malloc ",
|
||||
"calloc ",
|
||||
"free ",
|
||||
"realloc ",
|
||||
"memalign",
|
||||
"valloc ",
|
||||
"pvalloc ",
|
||||
};
|
||||
|
||||
void __attribute__((destructor))
|
||||
djend(void)
|
||||
{
|
||||
char *e;
|
||||
FILE *outf;
|
||||
size_t head, size, i;
|
||||
|
||||
e = getenv("MTRACE_CTL_FILE");
|
||||
if (!e)
|
||||
{
|
||||
static char fname[100];
|
||||
sprintf(fname, "/tmp/mtrace-%d.out", getpid());
|
||||
e = fname;
|
||||
}
|
||||
|
||||
outf = fopen(e, "w");
|
||||
if (!outf)
|
||||
err("cannot open output file");
|
||||
setbuf (outf, NULL);
|
||||
|
||||
fprintf (outf, "%ld out of %ld events captured\n", (long)head, (long)size);
|
||||
|
||||
fprintf (outf, "threadid type path ptr1 size ptr2\n");
|
||||
for (i=0; i<size; i++)
|
||||
{
|
||||
__malloc_trace_buffer_ptr t = buf + (i+head) % size;
|
||||
|
||||
switch (t->type)
|
||||
{
|
||||
case __MTB_TYPE_UNUSED:
|
||||
break;
|
||||
default:
|
||||
fprintf (outf, "%08x %s %c%c%c%c%c%c%c%c %016llx %016llx %016llx\n",
|
||||
t->thread,
|
||||
typenames[t->type],
|
||||
t->path_thread_cache ? 'T' : '-',
|
||||
t->path_cpu_cache ? 'c' : '-',
|
||||
t->path_cpu_cache2 ? 'C' : '-',
|
||||
t->path_sbrk ? 's' : '-',
|
||||
t->path_mmap ? 'M' : '-',
|
||||
t->path_munmap ? 'U' : '-',
|
||||
t->path_m_f_realloc ? 'R' : '-',
|
||||
t->path_hook ? 'H' : '-',
|
||||
(long long unsigned int) (size_t) t->ptr1,
|
||||
(long long unsigned int) t->size,
|
||||
(long long unsigned int) (size_t) t->ptr2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose (outf);
|
||||
|
||||
munmap (buf, size * sizeof(struct __malloc_trace_buffer_s));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
/* Malloc trace buffer API and definitions.
|
||||
Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
Contributed by DJ Delorie <dj@redhat.com>.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation; either version 2.1 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; see the file COPYING.LIB. If
|
||||
not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* The trace buffer contains a minimal amount of information which can
|
||||
be used to help reconstruct the memory profile of the calling
|
||||
application. */
|
||||
|
||||
struct __malloc_trace_buffer_s {
|
||||
int32_t thread; /* if thread_t > 32 bits, this is the LSBs */
|
||||
|
||||
uint32_t type:8; /* call type - malloc, free, etc */
|
||||
|
||||
uint32_t path_thread_cache:1; /* malloc/free interacted with the thread cache */
|
||||
uint32_t path_cpu_cache:1; /* malloc/free interacted with the per-cpu cache */
|
||||
uint32_t path_cpu_cache2:1; /* per-cpu cache contention required realloc */
|
||||
uint32_t path_sbrk:1; /* sbrk was called */
|
||||
uint32_t path_mmap:1; /* mmap was called */
|
||||
uint32_t path_munmap:1; /* munmap was called */
|
||||
uint32_t path_m_f_realloc:1; /* realloc became malloc/free (i.e. next few records) */
|
||||
uint32_t path_hook:1; /* A hook was used to complete the request */
|
||||
uint32_t path_unsorted_add:1; /* something was added to the unsorted bin */
|
||||
uint32_t path_unsorted_remove:1; /* something was removed from the unsorted bin */
|
||||
uint32_t path_unsorted_empty:1; /* the unsorted bin was emptied */
|
||||
uint32_t path_fastbin_add:1; /* something was added to a fastbin */
|
||||
uint32_t path_fastbin_remove:1; /* something was removed from a fastbin */
|
||||
uint32_t path_malloc_consolidate:1; /* something was removed from a fastbin */
|
||||
uint32_t path:10; /* remaining bits */
|
||||
|
||||
/* FREE - pointer to allocation to free.
|
||||
REALLOC - pointer to original allocation.
|
||||
POSIX_MEMALIGN - error code */
|
||||
void *ptr1;
|
||||
/* pointer to new allocation. */
|
||||
void *ptr2;
|
||||
/* requested size. */
|
||||
size_t size;
|
||||
/* FREE - internal size of deallocation.
|
||||
REALLOC - internal size of original allocation.
|
||||
MEMALIGN - alignment.
|
||||
POSIX_MEMALIGN - alignment. */
|
||||
size_t size2;
|
||||
/* internal size of new allocation. */
|
||||
size_t size3;
|
||||
/* Pad out to 64-bytes for future uses and mmap'd window alignment. */
|
||||
size_t pad[2];
|
||||
};
|
||||
|
||||
typedef struct __malloc_trace_buffer_s *__malloc_trace_buffer_ptr;
|
||||
|
||||
/* Initialize the trace buffer and backing file. The file is
|
||||
overwritten if it already exists. */
|
||||
void __malloc_trace_init (char *filename);
|
||||
|
||||
/* All remaining functions return current count of trace records. */
|
||||
|
||||
/* Pause - but don't stop - tracing. */
|
||||
size_t __malloc_trace_pause (void);
|
||||
|
||||
/* Resume tracing where it left off when paused. */
|
||||
size_t __malloc_trace_unpause (void);
|
||||
|
||||
/* Stop tracing and clean up all the trace buffer mappings. */
|
||||
size_t __malloc_trace_stop (void);
|
||||
|
||||
/* Sync all buffer data to file (typically a no-op on Linux). */
|
||||
size_t __malloc_trace_sync (void);
|
||||
|
||||
|
||||
#define __MTB_TYPE_UNUSED 0
|
||||
|
||||
/* ptr1 is 0x1234, size is sizeof(void *) - there is one of these at
|
||||
the beginning of the trace. */
|
||||
#define __MTB_TYPE_MAGIC 255
|
||||
|
||||
/* ptr2 = malloc (size) */
|
||||
#define __MTB_TYPE_MALLOC 1
|
||||
|
||||
/* ptr2 = calloc (size) */
|
||||
#define __MTB_TYPE_CALLOC 2
|
||||
|
||||
/* free (ptr1) */
|
||||
#define __MTB_TYPE_FREE 3
|
||||
|
||||
/* ptr2 = realloc (ptr1, size) */
|
||||
#define __MTB_TYPE_REALLOC 4
|
||||
|
||||
/* ptr2 = memalign (size2, size) */
|
||||
#define __MTB_TYPE_MEMALIGN 5
|
||||
|
||||
/* ptr2 = valloc (size) */
|
||||
#define __MTB_TYPE_VALLOC 6
|
||||
|
||||
/* ptr2 = pvalloc (size) */
|
||||
#define __MTB_TYPE_PVALLOC 7
|
||||
|
||||
/* ptr2 = posix_memalign (ptr1, size2, size) */
|
||||
#define __MTB_TYPE_POSIX_MEMALIGN 8
|
||||
|
||||
typedef enum {
|
||||
MSCAN_UNUSED,
|
||||
MSCAN_ARENA,
|
||||
MSCAN_HEAP,
|
||||
MSCAN_CHUNK_USED,
|
||||
MSCAN_CHUNK_FREE,
|
||||
MSCAN_FASTBIN_FREE,
|
||||
MSCAN_UNSORTED,
|
||||
MSCAN_TOP,
|
||||
MSCAN_TCACHE
|
||||
} MSCAN_Types;
|
||||
|
||||
void __malloc_scan_chunks (void (*callback)(void * /*ptr*/, size_t /*length*/, int /*type*/));
|
||||
|
||||
/* Codes for the simulator/workload programs. */
|
||||
#define C_NOP 0
|
||||
#define C_DONE 1
|
||||
#define C_MALLOC 2
|
||||
#define C_CALLOC 3
|
||||
#define C_REALLOC 4
|
||||
#define C_FREE 5
|
||||
#define C_SYNC_W 6
|
||||
#define C_SYNC_R 7
|
||||
#define C_ALLOC_PTRS 8
|
||||
#define C_ALLOC_SYNCS 9
|
||||
#define C_NTHREADS 10
|
||||
#define C_START_THREAD 11
|
||||
#define C_MEMALIGN 12
|
||||
#define C_VALLOC 13
|
||||
#define C_PVALLOC 14
|
||||
#define C_POSIX_MEMALIGN 15
|
||||
@@ -0,0 +1,812 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <getopt.h>
|
||||
#include <time.h>
|
||||
|
||||
/* The trace file looks like an array of struct __malloc_trace_buffer_s */
|
||||
#include "mtrace.h"
|
||||
|
||||
/* This application is "run once and exit" so there's no cleanup code. */
|
||||
|
||||
typedef unsigned char byte;
|
||||
|
||||
struct __malloc_trace_buffer_s *trace_records;
|
||||
size_t num_trace_records;
|
||||
|
||||
int verbose = 0;
|
||||
int use_file_buffers = 0;
|
||||
|
||||
//------------------------------------------------------------
|
||||
// File data buffers
|
||||
|
||||
static int tmpfile_idx = 0;
|
||||
static char *tmpdir;
|
||||
static int tmpdir_len;
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
/* If we're using memory buffers, we chain from first_buffer to
|
||||
last_buffer as a linked list. If we're using disk buffers, we only
|
||||
use last_buffer, which points to a fixed buffer. */
|
||||
|
||||
typedef struct BufferBlock {
|
||||
struct BufferBlock *next;
|
||||
byte buf[BUFFER_SIZE];
|
||||
} BufferBlock;
|
||||
|
||||
typedef struct Buffer {
|
||||
char *filename;
|
||||
int fd;
|
||||
BufferBlock *first_buffer;
|
||||
BufferBlock *last_buffer;
|
||||
|
||||
size_t count_total;
|
||||
size_t count_last;
|
||||
} Buffer;
|
||||
|
||||
void
|
||||
Buffer__ctor(Buffer *this)
|
||||
{
|
||||
if (use_file_buffers)
|
||||
{
|
||||
this->filename = (char *) malloc (tmpdir_len + 7);
|
||||
sprintf (this->filename, "%s%06d", tmpdir, tmpfile_idx);
|
||||
tmpfile_idx ++;
|
||||
this->fd = -1;
|
||||
}
|
||||
this->first_buffer = this->last_buffer = (BufferBlock *) malloc (sizeof(BufferBlock));
|
||||
this->first_buffer->next = NULL;
|
||||
this->count_total = this->count_last = 0;
|
||||
}
|
||||
|
||||
void
|
||||
Buffer__add (Buffer *this, char x)
|
||||
{
|
||||
if (this->count_last == BUFFER_SIZE)
|
||||
{
|
||||
if (use_file_buffers)
|
||||
{
|
||||
if (this->fd == -1)
|
||||
{
|
||||
this->fd = open(this->filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
|
||||
if (this->fd < 0)
|
||||
{
|
||||
fprintf(stderr, "cannot create temporary file %s for writing\n", this->filename);
|
||||
perror("The error was");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
write (this->fd, this->last_buffer->buf, BUFFER_SIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
BufferBlock *b = (BufferBlock *) malloc (sizeof(BufferBlock));
|
||||
b->next = NULL;
|
||||
this->last_buffer->next = b;
|
||||
this->last_buffer = b;
|
||||
}
|
||||
this->count_last = 0;
|
||||
}
|
||||
this->last_buffer->buf[this->count_last] = x;
|
||||
this->count_last ++;
|
||||
this->count_total ++;
|
||||
}
|
||||
|
||||
void
|
||||
Buffer__add_int (Buffer *this, size_t val)
|
||||
{
|
||||
byte buf[sizeof(size_t)*2];
|
||||
int i = 0;
|
||||
|
||||
buf[i++] = val & 127;
|
||||
while (val > 127)
|
||||
{
|
||||
val >>= 7;
|
||||
buf[i++] = (val & 127) | 128;
|
||||
}
|
||||
while (i > 0)
|
||||
Buffer__add (this, buf[--i]);
|
||||
}
|
||||
|
||||
void
|
||||
Buffer__write (Buffer *this, int fd)
|
||||
{
|
||||
if (use_file_buffers)
|
||||
{
|
||||
byte buf[BUFFER_SIZE];
|
||||
int count;
|
||||
struct stat s;
|
||||
|
||||
if (this->fd != -1)
|
||||
close (this->fd);
|
||||
|
||||
if (this->count_total != this->count_last)
|
||||
{
|
||||
if (stat(this->filename, &s) < 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot stat %s\n", this->filename);
|
||||
perror("The error was");
|
||||
exit(1);
|
||||
}
|
||||
if (s.st_size != this->count_total - this->count_last)
|
||||
{
|
||||
fprintf(stderr, "File %s is %ld, not %ld-%ld !\n", this->filename, s.st_size, this->count_total, this->count_last);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
this->fd = open (this->filename, O_RDONLY);
|
||||
while ((count = read (this->fd, buf, BUFFER_SIZE)) > 0)
|
||||
write (fd, buf, count);
|
||||
unlink (this->filename);
|
||||
}
|
||||
|
||||
this->fd = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
BufferBlock *b;
|
||||
|
||||
for (b = this->first_buffer; b != this->last_buffer; b = b->next)
|
||||
write (fd, b->buf, BUFFER_SIZE);
|
||||
}
|
||||
|
||||
if (this->count_last)
|
||||
write (fd, this->last_buffer->buf, this->count_last);
|
||||
this->count_last = 0;
|
||||
}
|
||||
|
||||
void
|
||||
Buffer__clear (Buffer *this)
|
||||
{
|
||||
if (use_file_buffers)
|
||||
{
|
||||
if (this->fd != -1)
|
||||
close (this->fd);
|
||||
unlink (this->filename);
|
||||
this->fd = -1;
|
||||
}
|
||||
else
|
||||
while (this->first_buffer != this->last_buffer)
|
||||
{
|
||||
BufferBlock *b = this->first_buffer->next;
|
||||
free (this->first_buffer);
|
||||
this->first_buffer = b;
|
||||
}
|
||||
this->count_total = this->count_last = 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
|
||||
typedef struct Hash {
|
||||
/* Each page table is an array of pointers to page tables... */
|
||||
void *pt[256];
|
||||
int count;
|
||||
} Hash;
|
||||
|
||||
void **
|
||||
Hash__hash (Hash *this, size_t key)
|
||||
{
|
||||
int i, pi;
|
||||
void **pt;
|
||||
|
||||
if (sizeof (size_t) >= 8)
|
||||
key >>= 3;
|
||||
else
|
||||
key >>= 2;
|
||||
|
||||
pt = this->pt;
|
||||
for (i=sizeof(size_t)-1; i>0; i--)
|
||||
{
|
||||
pi = (key >> (i*8)) & 0xff;
|
||||
if (pt[pi] == NULL)
|
||||
pt[pi] = (void **) calloc (256, sizeof(void *));
|
||||
pt = pt[pi];
|
||||
}
|
||||
|
||||
pi = key & 0xff;
|
||||
return &(pt[pi]);
|
||||
}
|
||||
|
||||
void
|
||||
Hash__ctor (Hash *this)
|
||||
{
|
||||
memset (&this->pt, 0, sizeof(this->pt));
|
||||
this->count = 0;
|
||||
}
|
||||
|
||||
void *
|
||||
Hash__lookup (Hash *this, size_t key)
|
||||
{
|
||||
void **pp = Hash__hash (this, key);
|
||||
return *pp;
|
||||
}
|
||||
|
||||
void
|
||||
Hash__add (Hash *this, size_t key, void *data)
|
||||
{
|
||||
void **pp = Hash__hash (this, key);
|
||||
*pp = data;
|
||||
this->count ++;
|
||||
}
|
||||
|
||||
void
|
||||
Hash__remove (Hash *this, size_t key)
|
||||
{
|
||||
void **pp = Hash__hash (this, key);
|
||||
*pp = NULL;
|
||||
this->count --;
|
||||
}
|
||||
|
||||
void
|
||||
Hash__getvals_1 (void **pt, int *count, int level)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<256; i++)
|
||||
if (pt[i])
|
||||
{
|
||||
if (level == sizeof(size_t)-1)
|
||||
(*count) ++;
|
||||
else
|
||||
Hash__getvals_1 ((void **) pt[i], count, level+1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Hash__getvals_2 (void **pt, void ***table, int level)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<256; i++)
|
||||
if (pt[i])
|
||||
{
|
||||
if (level == sizeof(size_t)-1)
|
||||
*(*table)++ = pt[i];
|
||||
else
|
||||
Hash__getvals_2 ((void **) pt[i], table, level+1);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
Hash__getvals (Hash *this, void ***vals)
|
||||
{
|
||||
int count = 0;
|
||||
void **valtmp;
|
||||
Hash__getvals_1 (this->pt, &count, 0);
|
||||
printf("count is %d vs %d\n", count, this->count);
|
||||
*vals = (void **) malloc (count * sizeof (void *));
|
||||
valtmp = (*vals);
|
||||
Hash__getvals_2 (this->pt, &valtmp, 0);
|
||||
printf("vals is %ld\n", valtmp - *vals);
|
||||
return count;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
|
||||
typedef struct PerThread {
|
||||
int started;
|
||||
Buffer workload;
|
||||
} PerThread;
|
||||
|
||||
void
|
||||
PerThread__ctor (PerThread *this)
|
||||
{
|
||||
this->started = 0;
|
||||
Buffer__ctor (&(this->workload));
|
||||
}
|
||||
|
||||
void
|
||||
PerThread__add (PerThread *this, byte x)
|
||||
{
|
||||
Buffer__add (&(this->workload), x);
|
||||
}
|
||||
|
||||
void
|
||||
PerThread__add_int (PerThread *this, size_t x)
|
||||
{
|
||||
Buffer__add_int (&(this->workload), x);
|
||||
}
|
||||
|
||||
Hash *per_thread;
|
||||
|
||||
typedef enum {
|
||||
R_no_reason,
|
||||
R_not_seen,
|
||||
R_alloc,
|
||||
R_previously_freed,
|
||||
R_previously_realloced,
|
||||
R_realloc,
|
||||
R_memalign,
|
||||
R_posix_memalign
|
||||
} Reasons;
|
||||
|
||||
const char *reasons_str[] = {
|
||||
"",
|
||||
"not seen",
|
||||
"alloc",
|
||||
"previously free'd",
|
||||
"previously realloc'd",
|
||||
"realloc",
|
||||
"memalign",
|
||||
"posix memalign"
|
||||
};
|
||||
|
||||
typedef struct PerAddr {
|
||||
PerThread *owner;
|
||||
void *ptr;
|
||||
size_t idx;
|
||||
size_t reason_idx;
|
||||
struct __malloc_trace_buffer_s *inverted;
|
||||
unsigned char valid;
|
||||
unsigned char reason;
|
||||
} PerAddr;
|
||||
|
||||
void
|
||||
PerAddr__ctor (PerAddr *this, void *_ptr)
|
||||
{
|
||||
this->owner = NULL;
|
||||
this->ptr = _ptr;
|
||||
this->valid = 0;
|
||||
this->reason = R_not_seen;
|
||||
this->inverted = NULL;
|
||||
}
|
||||
|
||||
// Don't start at zero, zero is special.
|
||||
size_t addr_count = 1;
|
||||
|
||||
Hash *per_addr;
|
||||
|
||||
PerAddr *
|
||||
get_addr (void *ptr)
|
||||
{
|
||||
PerAddr *p;
|
||||
if (ptr == NULL)
|
||||
return NULL;
|
||||
p = Hash__lookup (per_addr, (size_t)ptr);
|
||||
if (!p)
|
||||
{
|
||||
p = (PerAddr *) malloc (sizeof (PerAddr));
|
||||
PerAddr__ctor (p, ptr);
|
||||
Hash__add (per_addr, (size_t)ptr, p);
|
||||
p->idx = addr_count ++;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
size_t sync_counter = 0;
|
||||
|
||||
// Insert a release/acquire pair to transfer ownership of data
|
||||
// from thread TREL to thread TACK
|
||||
void
|
||||
sync_threads (PerThread *trel, PerThread *tacq)
|
||||
{
|
||||
if (trel == tacq)
|
||||
return;
|
||||
sync_counter ++;
|
||||
PerThread__add (trel, C_SYNC_W);
|
||||
PerThread__add_int (trel, sync_counter);
|
||||
PerThread__add (tacq, C_SYNC_R);
|
||||
PerThread__add_int (tacq, sync_counter);
|
||||
}
|
||||
|
||||
void
|
||||
acq_ptr (PerThread *thread, PerAddr *addr)
|
||||
{
|
||||
if (addr == NULL)
|
||||
return;
|
||||
if (addr->owner != NULL && addr->owner != thread)
|
||||
sync_threads (addr->owner, thread);
|
||||
addr->owner = thread;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
|
||||
/* These are the state variables for the whole trace. */
|
||||
PerThread *master_thread = NULL;
|
||||
int last_tid = -1;
|
||||
PerThread *thread = NULL;
|
||||
int pending_inversions = 0;
|
||||
int fixed_inversions = 0;
|
||||
|
||||
static void
|
||||
process_one_trace_record (struct __malloc_trace_buffer_s *r)
|
||||
{
|
||||
size_t i = r - trace_records;
|
||||
|
||||
// Quick-skip for NULs at EOF
|
||||
if (r->type == __MTB_TYPE_UNUSED)
|
||||
return;
|
||||
|
||||
if (verbose > 1)
|
||||
printf("\033[32m%8x %2x (0x%p, 0x%x) = 0x%p\033[0m\n",
|
||||
r->thread, r->type, r->ptr1, (int)r->size, r->ptr2);
|
||||
|
||||
if (r->thread != last_tid)
|
||||
{
|
||||
thread = Hash__lookup (per_thread, r->thread);
|
||||
if (thread == NULL)
|
||||
{
|
||||
thread = (PerThread *) malloc (sizeof (PerThread));
|
||||
PerThread__ctor (thread);
|
||||
Hash__add (per_thread, r->thread, thread);
|
||||
}
|
||||
last_tid = r->thread;
|
||||
}
|
||||
if (!master_thread)
|
||||
{
|
||||
master_thread = thread;
|
||||
thread->started = 1;
|
||||
}
|
||||
else if (!thread->started)
|
||||
{
|
||||
sync_threads (master_thread, thread);
|
||||
thread->started = 1;
|
||||
}
|
||||
|
||||
|
||||
PerAddr *pa1 = get_addr(r->ptr1);
|
||||
PerAddr *pa2 = get_addr(r->ptr2);
|
||||
|
||||
switch (r->type)
|
||||
{
|
||||
case __MTB_TYPE_UNUSED:
|
||||
case __MTB_TYPE_MAGIC:
|
||||
break;
|
||||
|
||||
case __MTB_TYPE_MALLOC:
|
||||
case __MTB_TYPE_CALLOC:
|
||||
case __MTB_TYPE_VALLOC:
|
||||
case __MTB_TYPE_PVALLOC:
|
||||
if (pa2 && pa2->valid)
|
||||
{
|
||||
if (pa2->inverted)
|
||||
{
|
||||
printf ("%ld: pointer %p alloc'd again? (possible multi-level inversion) size %ld %ld:%s\n",
|
||||
i, pa2->ptr, (long int)r->size, pa2->reason_idx, reasons_str[pa2->reason]);
|
||||
// exit (1);
|
||||
}
|
||||
pa2->inverted = r;
|
||||
fixed_inversions ++;
|
||||
pending_inversions ++;
|
||||
return;
|
||||
}
|
||||
|
||||
acq_ptr (thread, pa2);
|
||||
|
||||
if (r->type == __MTB_TYPE_MALLOC)
|
||||
PerThread__add (thread, C_MALLOC);
|
||||
if (r->type == __MTB_TYPE_CALLOC)
|
||||
PerThread__add (thread, C_CALLOC);
|
||||
if (r->type == __MTB_TYPE_VALLOC)
|
||||
PerThread__add (thread, C_VALLOC);
|
||||
if (r->type == __MTB_TYPE_PVALLOC)
|
||||
PerThread__add (thread, C_PVALLOC);
|
||||
|
||||
PerThread__add_int (thread, pa2 ? pa2->idx : 0);
|
||||
PerThread__add_int (thread, r->size);
|
||||
if (pa2)
|
||||
{
|
||||
pa2->valid = 1;
|
||||
pa2->reason = R_alloc;
|
||||
pa2->reason_idx = i;
|
||||
}
|
||||
break;
|
||||
|
||||
case __MTB_TYPE_FREE:
|
||||
acq_ptr (thread, pa1);
|
||||
if (pa1 == NULL)
|
||||
{
|
||||
PerThread__add (thread, C_FREE);
|
||||
PerThread__add_int (thread, 0);
|
||||
}
|
||||
else if (pa1->valid)
|
||||
{
|
||||
PerThread__add (thread, C_FREE);
|
||||
PerThread__add_int (thread, pa1->idx);
|
||||
pa1->valid = 0;
|
||||
pa1->reason = R_previously_freed;
|
||||
pa1->reason_idx = i;
|
||||
|
||||
if (pa1->inverted)
|
||||
{
|
||||
process_one_trace_record (pa1->inverted);
|
||||
pa1->inverted = NULL;
|
||||
pending_inversions --;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%ld: invalid pointer %p passed to free: %ld:%s\n",
|
||||
i, pa1->ptr, pa1->reason_idx, reasons_str[pa1->reason]);
|
||||
}
|
||||
break;
|
||||
|
||||
case __MTB_TYPE_REALLOC:
|
||||
if (pa1 && pa1->owner)
|
||||
acq_ptr (thread, pa1);
|
||||
if (pa2 && pa2->owner)
|
||||
acq_ptr (thread, pa2);
|
||||
PerThread__add (thread, C_REALLOC);
|
||||
PerThread__add_int (thread, pa2 ? pa2->idx : 0);
|
||||
PerThread__add_int (thread, pa1 ? pa1->idx : 0);
|
||||
PerThread__add_int (thread, r->size);
|
||||
|
||||
// handle inversion here too, eventually - both the alloc and free sides.
|
||||
if (pa1)
|
||||
{
|
||||
pa1->valid = 0;
|
||||
pa1->reason = R_previously_realloced;
|
||||
pa1->reason_idx = i;
|
||||
}
|
||||
if (pa2)
|
||||
{
|
||||
pa2->valid = 1;
|
||||
pa2->reason = R_realloc;
|
||||
pa2->reason_idx = i;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case __MTB_TYPE_MEMALIGN:
|
||||
acq_ptr (thread, pa2);
|
||||
if (pa2 && pa2->valid)
|
||||
printf ("%ld: pointer %p memalign'd again? %ld:%s\n",
|
||||
i, pa2->ptr, pa2->reason_idx, reasons_str[pa2->reason]);
|
||||
PerThread__add (thread, C_MEMALIGN);
|
||||
PerThread__add_int (thread, pa2 ? pa2->idx : 0);
|
||||
PerThread__add_int (thread, r->size2);
|
||||
PerThread__add_int (thread, r->size);
|
||||
if (pa2)
|
||||
{
|
||||
pa2->valid = 1;
|
||||
pa2->reason = R_memalign;
|
||||
pa2->reason_idx = i;
|
||||
}
|
||||
break;
|
||||
|
||||
case __MTB_TYPE_POSIX_MEMALIGN:
|
||||
/* ptr1 is return value (0 or EINVAL etc)
|
||||
ptr2 is address of allocated memory
|
||||
size is the allocation size
|
||||
size2 is the alignment */
|
||||
PerThread__add (thread, C_MEMALIGN);
|
||||
PerThread__add_int (thread, (r->ptr1 == 0) ? pa2->idx : 0);
|
||||
PerThread__add_int (thread, r->size2);
|
||||
PerThread__add_int (thread, r->size);
|
||||
if (r->ptr1 == 0)
|
||||
{
|
||||
pa2->valid = 1;
|
||||
pa2->reason = R_posix_memalign;
|
||||
pa2->reason_idx = i;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
|
||||
static const char * const month_abbrevs[] = {
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
||||
};
|
||||
|
||||
static struct option longopts[] = {
|
||||
{ "verbose", 0, NULL, 'v' },
|
||||
{ "progress", 0, NULL, 'p' },
|
||||
{ "file-buffers", 0, NULL, 'f' },
|
||||
{ "tmpdir", 1, NULL, 't' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
static void
|
||||
print_help (void)
|
||||
{
|
||||
fprintf (stderr, "Usage: trace2wl [options] <outputfile.wl> <inputfile.mtrace>\n");
|
||||
fprintf (stderr, " -v --verbose print stats about workload\n");
|
||||
fprintf (stderr, " -p --progress show progress info\n");
|
||||
fprintf (stderr, " -f --file-buffers use temporary files to store intermediate buffers\n");
|
||||
fprintf (stderr, " -t <file> --tmpdir=<file> overrides $TMPDIR or /tmp\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int trace_fd, wl_fd;
|
||||
struct stat stbuf;
|
||||
unsigned long i;
|
||||
int opt;
|
||||
const char *tmp = NULL;
|
||||
size_t timer_divisor, old_percent_done=0;
|
||||
time_t start_time, cur_time;
|
||||
int show_progress = 0;
|
||||
|
||||
while ((opt = getopt_long (argc, argv, "vhpft:", longopts, NULL)) != -1)
|
||||
{
|
||||
switch (opt) {
|
||||
case 'v':
|
||||
verbose ++;
|
||||
break;
|
||||
case 'p':
|
||||
show_progress ++;
|
||||
break;
|
||||
case 'f':
|
||||
use_file_buffers ++;
|
||||
break;
|
||||
case 't':
|
||||
tmp = optarg;
|
||||
printf("tmpdir: %s\n", tmp);
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
print_help();
|
||||
}
|
||||
}
|
||||
|
||||
if (use_file_buffers)
|
||||
{
|
||||
if (tmp == NULL)
|
||||
{
|
||||
tmp = getenv("TMPDIR");
|
||||
printf("$TMPDIR: %s", tmp);
|
||||
if (tmp == NULL)
|
||||
{
|
||||
tmp = "/tmp";
|
||||
printf(" (using %s)", tmp);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
tmpdir = (char *) malloc (strlen(tmp) + strlen("/wl__") + 50);
|
||||
sprintf(tmpdir, "%s/wl_%d_", tmp, getpid());
|
||||
tmpdir_len = strlen(tmpdir);
|
||||
}
|
||||
|
||||
per_addr = (Hash *) malloc (sizeof (Hash));
|
||||
Hash__ctor (per_addr);
|
||||
|
||||
per_thread = (Hash *) malloc (sizeof (Hash));
|
||||
Hash__ctor (per_thread);
|
||||
|
||||
if (argc-optind != 2)
|
||||
print_help();
|
||||
|
||||
if (access (argv[optind], F_OK) == 0)
|
||||
{
|
||||
fprintf (stderr, "Error: output file %s already exists, will not overwrite\n", argv[optind]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
trace_fd = open (argv[optind+1], O_RDONLY, 0666);
|
||||
if (trace_fd < 0)
|
||||
{
|
||||
fprintf (stderr, "Can't open %s for reading\n", argv[optind+1]);
|
||||
perror("The error was");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (stat (argv[optind+1], &stbuf) < 0)
|
||||
{
|
||||
fprintf (stderr, "Can't stat %s for reading\n", argv[optind+1]);
|
||||
perror("The error was");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
trace_records =
|
||||
(struct __malloc_trace_buffer_s *)
|
||||
mmap (NULL, stbuf.st_size, PROT_READ, MAP_SHARED, trace_fd, 0);
|
||||
if (trace_records == (void *)(-1))
|
||||
{
|
||||
fprintf (stderr, "Can't map %s for reading\n", argv[optind+1]);
|
||||
perror("The error was");
|
||||
exit(1);
|
||||
}
|
||||
num_trace_records = stbuf.st_size / sizeof(*trace_records);
|
||||
timer_divisor = num_trace_records / 100;
|
||||
time(&start_time);
|
||||
|
||||
for (i = 0; i < num_trace_records; i++)
|
||||
{
|
||||
if (show_progress)
|
||||
{
|
||||
int percent_done = i / timer_divisor;
|
||||
if (percent_done != old_percent_done)
|
||||
{
|
||||
struct tm *tm;
|
||||
old_percent_done = percent_done;
|
||||
time(&cur_time);
|
||||
cur_time = cur_time + (cur_time - start_time) * 100 / percent_done;
|
||||
tm = localtime(&cur_time);
|
||||
printf(" %3d%% done, ETA %3s %2d %2d:%02d \r", percent_done,
|
||||
month_abbrevs[tm->tm_mon], tm->tm_mday,
|
||||
tm->tm_hour, tm->tm_min);
|
||||
fflush (stdout);
|
||||
}
|
||||
}
|
||||
process_one_trace_record (trace_records + i);
|
||||
}
|
||||
if (show_progress)
|
||||
printf(" 100%% done, writing out buffers...\033[K\n");
|
||||
|
||||
PerThread **threads;
|
||||
int n_threads;
|
||||
n_threads = Hash__getvals (per_thread, (void ***)&threads);
|
||||
|
||||
size_t *thread_off = (size_t *) malloc (n_threads * sizeof (size_t));
|
||||
|
||||
if(verbose)
|
||||
printf("%d threads\n", n_threads);
|
||||
for (i = 0; i < n_threads; i ++)
|
||||
{
|
||||
PerThread__add(threads[i], C_DONE);
|
||||
if(verbose)
|
||||
printf("thread: %ld bytes\n", (long)threads[i]->workload.count_total);
|
||||
}
|
||||
|
||||
/* The location of each thread's workload depends on the size of the
|
||||
startup block, but the size of the startup block depends on the
|
||||
size of the thread's location encoding. So, we loop until it
|
||||
stabilizes. */
|
||||
size_t old_len = 1;
|
||||
size_t new_len = 2;
|
||||
Buffer main_loop;
|
||||
Buffer__ctor (&main_loop);
|
||||
while (old_len != new_len)
|
||||
{
|
||||
size_t off = new_len;
|
||||
int i;
|
||||
|
||||
old_len = new_len;
|
||||
Buffer__clear (&main_loop);
|
||||
|
||||
Buffer__add (&main_loop, C_ALLOC_PTRS);
|
||||
Buffer__add_int (&main_loop, addr_count);
|
||||
Buffer__add (&main_loop, C_ALLOC_SYNCS);
|
||||
Buffer__add_int (&main_loop, sync_counter);
|
||||
Buffer__add (&main_loop, C_NTHREADS);
|
||||
Buffer__add_int (&main_loop, n_threads);
|
||||
|
||||
for (i=0; i<n_threads; i++)
|
||||
{
|
||||
thread_off[i] = off;
|
||||
Buffer__add (&main_loop, C_START_THREAD);
|
||||
Buffer__add_int (&main_loop, off);
|
||||
off += threads[i]->workload.count_total;
|
||||
}
|
||||
|
||||
Buffer__add (&main_loop, C_DONE);
|
||||
|
||||
new_len = main_loop.count_total;
|
||||
}
|
||||
|
||||
wl_fd = open (argv[optind], O_CREAT|O_EXCL|O_RDWR, 0666);
|
||||
if (wl_fd < 0)
|
||||
{
|
||||
fprintf (stderr, "Can't open %s for writing\n", argv[optind]);
|
||||
perror("The error was");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Buffer__write (&main_loop, wl_fd);
|
||||
|
||||
for (i=0; i<n_threads; i++)
|
||||
{
|
||||
if (verbose || show_progress)
|
||||
printf("Start thread[%ld] offset 0x%lx\n", i, (long)thread_off[i]);
|
||||
Buffer__write (&(threads[i]->workload), wl_fd);
|
||||
}
|
||||
|
||||
close (wl_fd);
|
||||
|
||||
if (pending_inversions)
|
||||
printf("%d pending inversions remain\n", pending_inversions);
|
||||
if (fixed_inversions)
|
||||
printf("%d inversions fixed\n", fixed_inversions);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
// The trace file looks like an array of struct __malloc_trace_buffer_s
|
||||
#include "mtrace.h"
|
||||
|
||||
typedef long long BIG;
|
||||
|
||||
static BIG
|
||||
get_int (unsigned char **ptr)
|
||||
{
|
||||
BIG rv = 0;
|
||||
while (1)
|
||||
{
|
||||
unsigned char c = *(*ptr)++;
|
||||
rv |= (c & 0x7f);
|
||||
if (c & 0x80)
|
||||
rv <<= 7;
|
||||
else
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
data_looks_like_raw_trace (unsigned char *data, long n_data)
|
||||
{
|
||||
long lim = n_data > 1024 ? 1020 : (n_data - 4);
|
||||
long i;
|
||||
|
||||
// free and malloc calls will have a NULL we can look for
|
||||
for (i=0; i<lim; i++)
|
||||
if (memcmp (data+i, "\0\0\0\0", 4) == 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char * const typenames[] = {
|
||||
"unused",
|
||||
"malloc",
|
||||
"calloc",
|
||||
"free",
|
||||
"realloc",
|
||||
"memalign",
|
||||
"valloc",
|
||||
"pvalloc",
|
||||
"posix_memalign",
|
||||
};
|
||||
|
||||
void
|
||||
dump_raw_trace (unsigned char *data, long n_data)
|
||||
{
|
||||
unsigned char *edata = data + n_data;
|
||||
long head;
|
||||
|
||||
head = n_data / sizeof (struct __malloc_trace_buffer_s);
|
||||
|
||||
printf ("%ld out of %ld events captured (I think)\n", head, head);
|
||||
|
||||
printf ("%8s %8s %8s %16s %16s %16s %16s %16s\n",
|
||||
"threadid", "type", "path", "ptr1", "size", "ptr2", "size2", "size3");
|
||||
|
||||
while (data <= edata - sizeof (struct __malloc_trace_buffer_s))
|
||||
{
|
||||
struct __malloc_trace_buffer_s *t = (struct __malloc_trace_buffer_s *)data;
|
||||
|
||||
switch (t->type)
|
||||
{
|
||||
case __MTB_TYPE_UNUSED:
|
||||
break;
|
||||
default:
|
||||
/* Consider 'memalign' to be the largest API word we want to align
|
||||
on so make the name 8 chars wide at a minimum. */
|
||||
printf ("%08x %8s %c%c%c%c%c%c%c%c%c%c%c%c%c%c %016llx %016llx %016llx %016llx %016llx\n",
|
||||
t->thread,
|
||||
t->type == __MTB_TYPE_MAGIC ? "magic" : typenames[t->type],
|
||||
t->path_thread_cache ? 'T' : '-',
|
||||
t->path_cpu_cache ? 'c' : '-',
|
||||
t->path_cpu_cache2 ? 'C' : '-',
|
||||
t->path_sbrk ? 's' : '-',
|
||||
t->path_mmap ? 'M' : '-',
|
||||
t->path_munmap ? 'U' : '-',
|
||||
t->path_m_f_realloc ? 'R' : '-',
|
||||
t->path_hook ? 'H' : '-',
|
||||
t->path_unsorted_add ? 'U' : '-',
|
||||
t->path_unsorted_remove ? 'u' : '-',
|
||||
t->path_unsorted_empty ? 'E' : '-',
|
||||
t->path_fastbin_add ? 'F' : '-',
|
||||
t->path_fastbin_remove ? 'f' : '-',
|
||||
t->path_malloc_consolidate ? 'C' : '-',
|
||||
(long long unsigned int) (size_t) t->ptr1,
|
||||
(long long unsigned int) t->size,
|
||||
(long long unsigned int) (size_t) t->ptr2,
|
||||
(long long unsigned int) t->size2,
|
||||
(long long unsigned int) t->size3);
|
||||
break;
|
||||
}
|
||||
|
||||
data += sizeof (struct __malloc_trace_buffer_s);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dump_workload (unsigned char *data, long n_data)
|
||||
{
|
||||
unsigned char *orig_data = data;
|
||||
unsigned char *edata = data + n_data;
|
||||
BIG thread_idx = 0;
|
||||
BIG n_ptrs, n_syncs, n_threads, idx, p1, p2, sz;
|
||||
|
||||
while (data < edata)
|
||||
{
|
||||
printf("%016lx: %4lld: ", data - orig_data, thread_idx);
|
||||
switch (*data++)
|
||||
{
|
||||
case C_NOP:
|
||||
break;
|
||||
case C_ALLOC_PTRS:
|
||||
n_ptrs = get_int(&data);
|
||||
printf("AllocPtrs: %lld\n", n_ptrs);
|
||||
break;
|
||||
case C_ALLOC_SYNCS:
|
||||
n_syncs = get_int(&data);
|
||||
printf("AllocSyncs: %lld\n", n_syncs);
|
||||
break;
|
||||
case C_NTHREADS:
|
||||
n_threads = get_int (&data);
|
||||
printf("NThreads: %lld\n", n_threads);
|
||||
break;
|
||||
case C_START_THREAD:
|
||||
idx = get_int (&data);
|
||||
printf("StartThread: 0x%llx\n", idx);
|
||||
break;
|
||||
case C_DONE:
|
||||
printf("Done\n");
|
||||
thread_idx ++;
|
||||
break;
|
||||
|
||||
case C_MALLOC:
|
||||
p2 = get_int (&data);
|
||||
sz = get_int (&data);
|
||||
printf("Malloc (%lld) -> %lld\n", sz, p2);
|
||||
break;
|
||||
|
||||
case C_CALLOC:
|
||||
p2 = get_int (&data);
|
||||
sz = get_int (&data);
|
||||
printf("Calloc (%lld) -> %lld\n", sz, p2);
|
||||
break;
|
||||
|
||||
case C_REALLOC:
|
||||
p2 = get_int (&data);
|
||||
p1 = get_int (&data);
|
||||
sz = get_int (&data);
|
||||
printf("Realloc (%lld, %lld) -> %lld\n", p1, sz, p2);
|
||||
break;
|
||||
|
||||
case C_FREE:
|
||||
p1 = get_int (&data);
|
||||
printf("Free (%lld)\n", p1);
|
||||
break;
|
||||
|
||||
case C_SYNC_W:
|
||||
p1 = get_int(&data);
|
||||
printf("SyncW (%lld)\n", p1);
|
||||
break;
|
||||
|
||||
case C_SYNC_R:
|
||||
p1 = get_int(&data);
|
||||
printf("SyncR (%lld)\n", p1);
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("(unknown:%d)\n", *--data);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int fd;
|
||||
struct stat statb;
|
||||
unsigned char *data;
|
||||
|
||||
fd = open(argv[1], O_RDONLY);
|
||||
if (fd < 0)
|
||||
{
|
||||
fprintf(stderr, "Unable to open %s for reading\n", argv[1]);
|
||||
perror("The error was");
|
||||
exit(1);
|
||||
}
|
||||
fstat (fd, &statb);
|
||||
|
||||
data = (unsigned char *) mmap (NULL, statb.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
|
||||
if (data_looks_like_raw_trace (data, statb.st_size))
|
||||
dump_raw_trace (data, statb.st_size);
|
||||
else
|
||||
dump_workload (data, statb.st_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,731 @@
|
||||
#define _LARGEFILE64_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/resource.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "malloc.h"
|
||||
#include "mtrace.h"
|
||||
|
||||
#if UINTPTR_MAX == 0xffffffffffffffff
|
||||
|
||||
#define ticks_t int64_t
|
||||
/* Setting quick_run to 1 allows the simulator to model
|
||||
only the allocation and deallocation accounting via
|
||||
atomic_rss. The actual allocations are skipped. This
|
||||
mode is useful to verify the workload file. */
|
||||
#define quick_run 0
|
||||
|
||||
static __inline__ ticks_t rdtsc_s(void)
|
||||
{
|
||||
unsigned a, d;
|
||||
asm volatile("cpuid" ::: "%rax", "%rbx", "%rcx", "%rdx");
|
||||
asm volatile("rdtscp" : "=a" (a), "=d" (d));
|
||||
return ((unsigned long long)a) | (((unsigned long long)d) << 32);
|
||||
}
|
||||
|
||||
static __inline__ ticks_t rdtsc_e(void)
|
||||
{
|
||||
unsigned a, d;
|
||||
asm volatile("rdtscp" : "=a" (a), "=d" (d));
|
||||
asm volatile("cpuid" ::: "%rax", "%rbx", "%rcx", "%rdx");
|
||||
return ((unsigned long long)a) | (((unsigned long long)d) << 32);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define ticks_t int32_t
|
||||
|
||||
static __inline__ ticks_t rdtsc_s(void)
|
||||
{
|
||||
unsigned a, d;
|
||||
asm volatile("cpuid" ::: "%ax", "%bx", "%cx", "%dx");
|
||||
asm volatile("rdtsc" : "=a" (a), "=d" (d));
|
||||
return ((unsigned long)a) | (((unsigned long)d) << 16);
|
||||
}
|
||||
|
||||
static __inline__ ticks_t rdtsc_e(void)
|
||||
{
|
||||
unsigned a, d;
|
||||
asm volatile("rdtscp" : "=a" (a), "=d" (d));
|
||||
asm volatile("cpuid" ::: "%ax", "%bx", "%cx", "%dx");
|
||||
return ((unsigned long)a) | (((unsigned long)d) << 16);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static ticks_t diff_timeval (struct timeval e, struct timeval s)
|
||||
{
|
||||
ticks_t usec;
|
||||
if (e.tv_usec < s.tv_usec)
|
||||
usec = (e.tv_usec + 1000000 - s.tv_usec) + (e.tv_sec-1 - s.tv_sec)*1000000;
|
||||
else
|
||||
usec = (e.tv_usec - s.tv_usec) + (e.tv_sec - s.tv_sec)*1000000;
|
||||
return usec;
|
||||
}
|
||||
|
||||
#if 1
|
||||
#define Q1
|
||||
#define Q2
|
||||
#else
|
||||
pthread_mutex_t genmutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
#define Q1 pthread_mutex_lock(&genmutex)
|
||||
#define Q2 pthread_mutex_unlock(&genmutex)
|
||||
#endif
|
||||
|
||||
pthread_mutex_t cmutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
#define NCBUF 10
|
||||
static char cbuf[NCBUF][30];
|
||||
static int ci = 0;
|
||||
|
||||
char *comma(ticks_t x)
|
||||
{
|
||||
char buf[30], *bs, *bd;
|
||||
int l, i, idx;
|
||||
|
||||
pthread_mutex_lock(&cmutex);
|
||||
ci = (ci + 1) % NCBUF;
|
||||
idx = ci;
|
||||
pthread_mutex_unlock(&cmutex);
|
||||
bs = buf;
|
||||
bd = cbuf[idx];
|
||||
|
||||
sprintf(buf, "%lld", (long long int)x);
|
||||
l = strlen(buf);
|
||||
i = l;
|
||||
while (*bs)
|
||||
{
|
||||
*bd++ = *bs++;
|
||||
i--;
|
||||
if (i % 3 == 0 && *bs)
|
||||
*bd++ = ',';
|
||||
}
|
||||
*bd = 0;
|
||||
return cbuf[idx];
|
||||
}
|
||||
|
||||
static volatile void **ptrs;
|
||||
static volatile size_t *sizes;
|
||||
static size_t n_ptrs;
|
||||
static volatile char *syncs;
|
||||
static pthread_mutex_t *mutexes;
|
||||
static pthread_cond_t *conds;
|
||||
static size_t n_syncs;
|
||||
|
||||
static pthread_mutex_t stat_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
ticks_t malloc_time = 0, malloc_count = 0;
|
||||
ticks_t calloc_time = 0, calloc_count = 0;
|
||||
ticks_t realloc_time = 0, realloc_count = 0;
|
||||
ticks_t free_time = 0, free_count = 0;
|
||||
|
||||
size_t ideal_rss = 0;
|
||||
size_t max_ideal_rss = 0;
|
||||
static pthread_mutex_t rss_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
void atomic_rss (ssize_t delta)
|
||||
{
|
||||
pthread_mutex_lock (&rss_mutex);
|
||||
ideal_rss += delta;
|
||||
if (max_ideal_rss < ideal_rss)
|
||||
max_ideal_rss = ideal_rss;
|
||||
pthread_mutex_unlock (&rss_mutex);
|
||||
}
|
||||
|
||||
pthread_mutex_t stop_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
int threads_done = 0;
|
||||
|
||||
//#define dprintf printf
|
||||
#define dprintf(...) (void)1
|
||||
|
||||
//#define mprintf printf
|
||||
//#define MDEBUG 1
|
||||
#define mprintf(...) (void)1
|
||||
|
||||
#define myabort() my_abort_2(thrc, __LINE__)
|
||||
void
|
||||
my_abort_2 (pthread_t thrc, int line)
|
||||
{
|
||||
fprintf(stderr, "Abort thread %p at line %d\n", (void *)thrc, line);
|
||||
abort();
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
/* Wrapper around I/O routines */
|
||||
|
||||
int io_fd;
|
||||
|
||||
#define IOSIZE 65536
|
||||
#define IOMIN 4096
|
||||
|
||||
static pthread_mutex_t io_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
typedef struct {
|
||||
unsigned char buf[IOSIZE];
|
||||
size_t incr;
|
||||
size_t max_incr;
|
||||
size_t buf_base;
|
||||
size_t buf_idx;
|
||||
int saw_eof;
|
||||
} IOPerThreadType;
|
||||
|
||||
IOPerThreadType main_io;
|
||||
IOPerThreadType *thread_io;
|
||||
|
||||
void
|
||||
io_init (IOPerThreadType *io, size_t file_offset, int incr)
|
||||
{
|
||||
if (incr > IOSIZE)
|
||||
incr = IOSIZE;
|
||||
if (incr < IOMIN)
|
||||
incr = IOMIN;
|
||||
|
||||
io->buf_base = file_offset;
|
||||
io->buf_idx = 0;
|
||||
io->incr = incr;
|
||||
|
||||
pthread_mutex_lock (&io_mutex);
|
||||
lseek64 (io_fd, io->buf_base, SEEK_SET);
|
||||
// short read OK, the eof is just to prevent runaways from bad data.
|
||||
if (read (io_fd, io->buf, incr) < 0)
|
||||
io->saw_eof = 1;
|
||||
else
|
||||
io->saw_eof = 0;
|
||||
pthread_mutex_unlock (&io_mutex);
|
||||
}
|
||||
|
||||
unsigned char
|
||||
io_read (IOPerThreadType *io)
|
||||
{
|
||||
if (io->buf_idx >= io->incr)
|
||||
io_init (io, io->buf_base + io->buf_idx, io->incr);
|
||||
if (io->saw_eof)
|
||||
return 0xff;
|
||||
return io->buf [io->buf_idx++];
|
||||
}
|
||||
|
||||
unsigned char
|
||||
io_peek (IOPerThreadType *io)
|
||||
{
|
||||
if (io->buf_idx >= io->incr)
|
||||
io_init (io, io->buf_base + io->buf_idx, io->incr);
|
||||
if (io->saw_eof)
|
||||
return 0xff;
|
||||
return io->buf [io->buf_idx];
|
||||
}
|
||||
|
||||
size_t
|
||||
io_pos (IOPerThreadType *io)
|
||||
{
|
||||
return io->buf_base + io->buf_idx;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
static void
|
||||
wmem (volatile void *ptr, int count)
|
||||
{
|
||||
char *p = (char *)ptr;
|
||||
int i;
|
||||
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
for (i=0; i<count; i++)
|
||||
p[i] = 0x11;
|
||||
}
|
||||
#define xwmem(a,b)
|
||||
|
||||
static size_t get_int (IOPerThreadType *io)
|
||||
{
|
||||
size_t rv = 0;
|
||||
while (1)
|
||||
{
|
||||
unsigned char c = io_read (io);
|
||||
rv |= (c & 0x7f);
|
||||
if (c & 0x80)
|
||||
rv <<= 7;
|
||||
else
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
static void free_wipe (size_t idx)
|
||||
{
|
||||
char *cp = (char *)ptrs[idx];
|
||||
if (cp == NULL)
|
||||
return;
|
||||
size_t sz = sizes[idx];
|
||||
size_t i;
|
||||
for (i=0; i<sz; i++)
|
||||
{
|
||||
if (i % 8 == 1)
|
||||
cp[i] = i / 8;
|
||||
else
|
||||
cp[i] = 0x22;
|
||||
}
|
||||
}
|
||||
|
||||
static void *
|
||||
thread_common (void *my_data_v)
|
||||
{
|
||||
pthread_t thrc = pthread_self ();
|
||||
size_t p1, p2, sz, sz2;
|
||||
IOPerThreadType *io = (IOPerThreadType *)my_data_v;
|
||||
ticks_t my_malloc_time = 0, my_malloc_count = 0;
|
||||
ticks_t my_calloc_time = 0, my_calloc_count = 0;
|
||||
ticks_t my_realloc_time = 0, my_realloc_count = 0;
|
||||
ticks_t my_free_time = 0, my_free_count = 0;
|
||||
ticks_t stime, etime;
|
||||
int thread_idx = io - thread_io;
|
||||
#ifdef MDEBUG
|
||||
volatile void *tmp;
|
||||
#endif
|
||||
|
||||
while (1)
|
||||
{
|
||||
unsigned char this_op = io_peek (io);
|
||||
if (io->saw_eof)
|
||||
myabort();
|
||||
dprintf("op %p:%ld is %d\n", (void *)thrc, io_pos (io), io_peek (io));
|
||||
switch (io_read (io))
|
||||
{
|
||||
case C_NOP:
|
||||
break;
|
||||
|
||||
case C_DONE:
|
||||
dprintf("op %p:%ld DONE\n", (void *)thrc, io_pos (io));
|
||||
pthread_mutex_lock (&stat_mutex);
|
||||
malloc_time += my_malloc_time;
|
||||
calloc_time += my_calloc_time;
|
||||
realloc_time += my_realloc_time;
|
||||
free_time += my_free_time;
|
||||
malloc_count += my_malloc_count;
|
||||
calloc_count += my_calloc_count;
|
||||
realloc_count += my_realloc_count;
|
||||
free_count += my_free_count;
|
||||
threads_done ++;
|
||||
pthread_mutex_unlock (&stat_mutex);
|
||||
pthread_mutex_lock(&stop_mutex);
|
||||
pthread_mutex_unlock(&stop_mutex);
|
||||
return NULL;
|
||||
|
||||
case C_MEMALIGN:
|
||||
p2 = get_int (io);
|
||||
sz2 = get_int (io);
|
||||
sz = get_int (io);
|
||||
dprintf("op %p:%ld %ld = MEMALIGN %ld %ld\n", (void *)thrc, io_pos (io), p2, sz2, sz);
|
||||
/* we can't force memalign to return NULL (fail), so just skip it. */
|
||||
if (p2 == 0)
|
||||
break;
|
||||
if (p2 > n_ptrs)
|
||||
myabort();
|
||||
stime = rdtsc_s();
|
||||
Q1;
|
||||
if (ptrs[p2])
|
||||
{
|
||||
if (!quick_run)
|
||||
free ((void *)ptrs[p2]);
|
||||
atomic_rss (-sizes[p2]);
|
||||
}
|
||||
if (!quick_run)
|
||||
ptrs[p2] = memalign (sz2, sz);
|
||||
else
|
||||
ptrs[p2] = (void *)p2;
|
||||
/* Verify the alignment matches what is expected. */
|
||||
if (((size_t)ptrs[p2] & (sz2 - 1)) != 0)
|
||||
myabort ();
|
||||
sizes[p2] = sz;
|
||||
mprintf("%p = memalign(%lx, %lx)\n", ptrs[p2], sz2, sz);
|
||||
Q2;
|
||||
etime = rdtsc_e();
|
||||
if (ptrs[p2] != NULL)
|
||||
atomic_rss (sz);
|
||||
if (etime < stime)
|
||||
{
|
||||
printf("s: %llx e:%llx d:%llx\n", (long long)stime, (long long)etime, (long long)(etime-stime));
|
||||
}
|
||||
my_malloc_time += etime - stime;
|
||||
my_malloc_count ++;
|
||||
if (!quick_run)
|
||||
wmem(ptrs[p2], sz);
|
||||
break;
|
||||
|
||||
case C_MALLOC:
|
||||
p2 = get_int (io);
|
||||
sz = get_int (io);
|
||||
dprintf("op %p:%ld %ld = MALLOC %ld\n", (void *)thrc, io_pos (io), p2, sz);
|
||||
/* we can't force malloc to return NULL (fail), so just skip it. */
|
||||
if (p2 == 0)
|
||||
break;
|
||||
if (p2 > n_ptrs)
|
||||
myabort();
|
||||
stime = rdtsc_s();
|
||||
Q1;
|
||||
if (ptrs[p2])
|
||||
{
|
||||
if (!quick_run)
|
||||
free ((void *)ptrs[p2]);
|
||||
atomic_rss (-sizes[p2]);
|
||||
}
|
||||
if (!quick_run)
|
||||
ptrs[p2] = malloc (sz);
|
||||
else
|
||||
ptrs[p2] = (void *)p2;
|
||||
sizes[p2] = sz;
|
||||
mprintf("%p = malloc(%lx)\n", ptrs[p2], sz);
|
||||
Q2;
|
||||
etime = rdtsc_e();
|
||||
if (ptrs[p2] != NULL)
|
||||
atomic_rss (sz);
|
||||
if (etime < stime)
|
||||
{
|
||||
printf("s: %llx e:%llx d:%llx\n", (long long)stime, (long long)etime, (long long)(etime-stime));
|
||||
}
|
||||
my_malloc_time += etime - stime;
|
||||
my_malloc_count ++;
|
||||
if (!quick_run)
|
||||
wmem(ptrs[p2], sz);
|
||||
break;
|
||||
|
||||
case C_CALLOC:
|
||||
p2 = get_int (io);
|
||||
sz = get_int (io);
|
||||
dprintf("op %p:%ld %ld = CALLOC %ld\n", (void *)thrc, io_pos (io), p2, sz);
|
||||
/* we can't force calloc to return NULL (fail), so just skip it. */
|
||||
if (p2 == 0)
|
||||
break;
|
||||
if (p2 > n_ptrs)
|
||||
myabort();
|
||||
if (ptrs[p2])
|
||||
{
|
||||
if (!quick_run)
|
||||
free ((void *)ptrs[p2]);
|
||||
atomic_rss (-sizes[p2]);
|
||||
}
|
||||
stime = rdtsc_s();
|
||||
Q1;
|
||||
if (!quick_run)
|
||||
ptrs[p2] = calloc (sz, 1);
|
||||
else
|
||||
ptrs[p2] = (void *)p2;
|
||||
sizes[p2] = sz;
|
||||
mprintf("%p = calloc(%lx)\n", ptrs[p2], sz);
|
||||
Q2;
|
||||
if (ptrs[p2])
|
||||
atomic_rss (sz);
|
||||
my_calloc_time += rdtsc_e() - stime;
|
||||
my_calloc_count ++;
|
||||
if (!quick_run)
|
||||
wmem(ptrs[p2], sz);
|
||||
break;
|
||||
|
||||
case C_REALLOC:
|
||||
p2 = get_int (io);
|
||||
p1 = get_int (io);
|
||||
sz = get_int (io);
|
||||
dprintf("op %p:%ld %ld = REALLOC %ld %ld\n", (void *)thrc, io_pos (io), p2, p1, sz);
|
||||
if (p1 > n_ptrs)
|
||||
myabort();
|
||||
if (p2 > n_ptrs)
|
||||
myabort();
|
||||
/* we can't force realloc to return NULL (fail), so just skip it. */
|
||||
if (p2 == 0)
|
||||
break;
|
||||
|
||||
if (ptrs[p1])
|
||||
atomic_rss (-sizes[p1]);
|
||||
if (!quick_run)
|
||||
free_wipe(p1);
|
||||
stime = rdtsc_s();
|
||||
Q1;
|
||||
#ifdef MDEBUG
|
||||
tmp = ptrs[p1];
|
||||
#endif
|
||||
if (!quick_run)
|
||||
ptrs[p2] = realloc ((void *)ptrs[p1], sz);
|
||||
else
|
||||
ptrs[p2] = (void *)p2;
|
||||
sizes[p2] = sz;
|
||||
mprintf("%p = relloc(%p,%lx)\n", ptrs[p2], tmp,sz);
|
||||
Q2;
|
||||
my_realloc_time += rdtsc_e() - stime;
|
||||
my_realloc_count ++;
|
||||
if (!quick_run)
|
||||
wmem(ptrs[p2], sz);
|
||||
if (p1 != p2)
|
||||
ptrs[p1] = 0;
|
||||
if (ptrs[p2])
|
||||
atomic_rss (sizes[p2]);
|
||||
break;
|
||||
|
||||
case C_FREE:
|
||||
p1 = get_int (io);
|
||||
if (p1 > n_ptrs)
|
||||
myabort();
|
||||
dprintf("op %p:%ld FREE %ld\n", (void *)thrc, io_pos (io), p1);
|
||||
if (!quick_run)
|
||||
free_wipe (p1);
|
||||
if (ptrs[p1])
|
||||
atomic_rss (-sizes[p1]);
|
||||
stime = rdtsc_s();
|
||||
Q1;
|
||||
mprintf("free(%p)\n", ptrs[p1]);
|
||||
if (!quick_run)
|
||||
free ((void *)ptrs[p1]);
|
||||
Q2;
|
||||
my_free_time += rdtsc_e() - stime;
|
||||
my_free_count ++;
|
||||
ptrs[p1] = 0;
|
||||
break;
|
||||
|
||||
case C_SYNC_W:
|
||||
p1 = get_int(io);
|
||||
dprintf("op %p:%ld SYNC_W %ld\n", (void *)thrc, io_pos (io), p1);
|
||||
if (p1 > n_syncs)
|
||||
myabort();
|
||||
pthread_mutex_lock (&mutexes[p1]);
|
||||
syncs[p1] = 1;
|
||||
pthread_cond_signal (&conds[p1]);
|
||||
__sync_synchronize ();
|
||||
pthread_mutex_unlock (&mutexes[p1]);
|
||||
break;
|
||||
|
||||
case C_SYNC_R:
|
||||
p1 = get_int(io);
|
||||
dprintf("op %p:%ld SYNC_R %ld\n", (void *)thrc, io_pos (io), p1);
|
||||
if (p1 > n_syncs)
|
||||
myabort();
|
||||
pthread_mutex_lock (&mutexes[p1]);
|
||||
while (syncs[p1] != 1)
|
||||
{
|
||||
pthread_cond_wait (&conds[p1], &mutexes[p1]);
|
||||
__sync_synchronize ();
|
||||
}
|
||||
pthread_mutex_unlock (&mutexes[p1]);
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("op %d - unsupported, thread %d addr %lu\n",
|
||||
this_op, thread_idx, (long unsigned int)io_pos (io));
|
||||
myabort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void *alloc_mem (size_t amt)
|
||||
{
|
||||
void *rv = mmap (NULL, amt, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||
mlock (rv, amt);
|
||||
memset (rv, 0, amt);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static pthread_t *thread_ids;
|
||||
|
||||
void *
|
||||
my_malloc (const char *msg, int size, IOPerThreadType *io, size_t *psz, size_t count)
|
||||
{
|
||||
void *rv;
|
||||
if (psz)
|
||||
count = *psz = get_int (io);
|
||||
dprintf ("my_malloc for %s size %d * %ld\n", msg, size, count);
|
||||
rv = alloc_mem(size * count);
|
||||
if (!rv)
|
||||
{
|
||||
fprintf(stderr, "calloc(%lu,%lu) failed\n", (long unsigned)size, (long unsigned)*psz);
|
||||
exit(1);
|
||||
}
|
||||
mlock (rv, size * count);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static const char * const scan_names[] = {
|
||||
"UNUSED",
|
||||
"ARENA",
|
||||
"HEAP",
|
||||
"CHUNK_USED",
|
||||
"CHUNK_FREE",
|
||||
"FASTBIN_FREE",
|
||||
"UNSORTED",
|
||||
"TOP",
|
||||
"TCACHE",
|
||||
"USED"
|
||||
};
|
||||
|
||||
void
|
||||
malloc_scan_callback (void *ptr, size_t length, int type)
|
||||
{
|
||||
printf("%s: ptr %p length %llx\n", scan_names[type], ptr, (long long)length);
|
||||
}
|
||||
|
||||
#define MY_ALLOC(T, psz) \
|
||||
(typeof (T)) my_malloc (#T, sizeof(*T), &main_io, psz, 0)
|
||||
#define MY_ALLOCN(T, count) \
|
||||
(typeof (T)) my_malloc (#T, sizeof(*T), &main_io, NULL, count)
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
ticks_t start=0;
|
||||
ticks_t end;
|
||||
ticks_t usec;
|
||||
struct timeval tv_s, tv_e;
|
||||
int thread_idx = 0;
|
||||
int i;
|
||||
size_t n_threads = 0;
|
||||
size_t idx;
|
||||
struct rusage res_start, res_end;
|
||||
int done;
|
||||
size_t guessed_io_size = 4096;
|
||||
struct stat statb;
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s <trace2dat.outfile>\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
io_fd = open(argv[1], O_RDONLY);
|
||||
if (io_fd < 0)
|
||||
{
|
||||
fprintf(stderr, "Unable to open %s for reading\n", argv[1]);
|
||||
perror("The error was");
|
||||
exit(1);
|
||||
}
|
||||
fstat (io_fd, &statb);
|
||||
|
||||
io_init (&main_io, 0, IOMIN);
|
||||
|
||||
pthread_mutex_lock(&stop_mutex);
|
||||
|
||||
done = 0;
|
||||
while (!done)
|
||||
{
|
||||
switch (io_read (&main_io))
|
||||
{
|
||||
case C_NOP:
|
||||
break;
|
||||
case C_ALLOC_PTRS:
|
||||
ptrs = MY_ALLOC (ptrs, &n_ptrs);
|
||||
sizes = alloc_mem(sizeof(sizes[0]) * n_ptrs);
|
||||
ptrs[0] = 0;
|
||||
break;
|
||||
case C_ALLOC_SYNCS:
|
||||
n_syncs = get_int(&main_io);
|
||||
syncs = MY_ALLOCN (syncs, n_syncs);
|
||||
conds = MY_ALLOCN (conds, n_syncs);
|
||||
mutexes = MY_ALLOCN (mutexes, n_syncs);
|
||||
for (idx=0; idx<n_syncs; idx++)
|
||||
{
|
||||
pthread_mutex_init (&mutexes[idx], NULL);
|
||||
pthread_cond_init (&conds[idx], NULL);
|
||||
}
|
||||
break;
|
||||
case C_NTHREADS:
|
||||
thread_ids = MY_ALLOC (thread_ids, &n_threads);
|
||||
thread_io = MY_ALLOCN (thread_io, n_threads);
|
||||
guessed_io_size = ((statb.st_size / n_threads) < (1024*1024)) ? 65536 : 4096;
|
||||
/* The next thing in the workscript is thread creation */
|
||||
getrusage (RUSAGE_SELF, &res_start);
|
||||
gettimeofday (&tv_s, NULL);
|
||||
start = rdtsc_s();
|
||||
break;
|
||||
case C_START_THREAD:
|
||||
idx = get_int (&main_io);
|
||||
io_init (& thread_io[thread_idx], idx, guessed_io_size);
|
||||
pthread_create (&thread_ids[thread_idx], NULL, thread_common, thread_io + thread_idx);
|
||||
dprintf("Starting thread %lld at offset %lu %lx\n", (long long)thread_ids[thread_idx], (unsigned long)idx, (unsigned long)idx);
|
||||
thread_idx ++;
|
||||
break;
|
||||
case C_DONE:
|
||||
do
|
||||
{
|
||||
pthread_mutex_lock (&stat_mutex);
|
||||
i = threads_done;
|
||||
pthread_mutex_unlock (&stat_mutex);
|
||||
} while (i < thread_idx);
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!quick_run)
|
||||
{
|
||||
end = rdtsc_e();
|
||||
gettimeofday (&tv_e, NULL);
|
||||
getrusage (RUSAGE_SELF, &res_end);
|
||||
|
||||
printf("%s cycles\n", comma(end - start));
|
||||
usec = diff_timeval (tv_e, tv_s);
|
||||
printf("%s usec wall time\n", comma(usec));
|
||||
|
||||
usec = diff_timeval (res_end.ru_utime, res_start.ru_utime);
|
||||
printf("%s usec across %d thread%s\n",
|
||||
comma(usec), (int)n_threads, n_threads == 1 ? "" : "s");
|
||||
printf("%s Kb Max RSS (%s -> %s)\n",
|
||||
comma(res_end.ru_maxrss - res_start.ru_maxrss),
|
||||
comma(res_start.ru_maxrss), comma(res_end.ru_maxrss));
|
||||
}
|
||||
printf("%s Kb Max Ideal RSS\n", comma (max_ideal_rss / 1024));
|
||||
|
||||
if (malloc_count == 0) malloc_count ++;
|
||||
if (calloc_count == 0) calloc_count ++;
|
||||
if (realloc_count == 0) realloc_count ++;
|
||||
if (free_count == 0) free_count ++;
|
||||
|
||||
if (!quick_run)
|
||||
{
|
||||
printf("\n");
|
||||
printf("sizeof ticks_t is %lu\n", sizeof(ticks_t));
|
||||
printf("Avg malloc time: %6s in %10s calls\n", comma(malloc_time/malloc_count), comma(malloc_count));
|
||||
printf("Avg calloc time: %6s in %10s calls\n", comma(calloc_time/calloc_count), comma(calloc_count));
|
||||
printf("Avg realloc time: %5s in %10s calls\n", comma(realloc_time/realloc_count), comma(realloc_count));
|
||||
printf("Avg free time: %8s in %10s calls\n", comma(free_time/free_count), comma(free_count));
|
||||
printf("Total call time: %s cycles\n", comma(malloc_time+calloc_time+realloc_time+free_time));
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Free any still-held chunks of memory. */
|
||||
for (idx=0; idx<n_ptrs; idx++)
|
||||
if (ptrs[idx])
|
||||
{
|
||||
free((void *)ptrs[idx]);
|
||||
ptrs[idx] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/* This will fail (crash) for system glibc but that's OK. */
|
||||
__malloc_scan_chunks(malloc_scan_callback);
|
||||
|
||||
malloc_info (0, stdout);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/* ...or report them as used. */
|
||||
for (idx=0; idx<n_ptrs; idx++)
|
||||
if (ptrs[idx])
|
||||
{
|
||||
char *p = (char *)ptrs[idx] - 2*sizeof(size_t);
|
||||
size_t *sp = (size_t *)p;
|
||||
size_t size = sp[1] & ~7;
|
||||
malloc_scan_callback (sp, size, 9);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Now that we've scanned all the per-thread caches, it's safe to
|
||||
let them exit and clean up. */
|
||||
pthread_mutex_unlock(&stop_mutex);
|
||||
|
||||
for (i=0; i<thread_idx; i++)
|
||||
pthread_join (thread_ids[i], NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user