struct {
FILE * fp; // trace file
size_t total, max; // current and maximum allocated memory
size_t overhead, maxoverhead; // current and maximum profiling overhead
size_t nr; // current number of records
size_t startrss, maxrss; // starting and maximum system ressource usage
char * fname; // trace file name
} pmtrace;
typedef struct {
char * func, * file;
size_t max, total;
int line, id;
} pmfunc;
typedef struct {
size_t id, size;
} pmdata;
static pmfunc * pmfuncs = NULL;
static int pmfuncn = 0;
static int pmfunc_index (const char * func, const char * file, int line)
{
pmfunc * p = pmfuncs;
for (int i = 0; i < pmfuncn; i++, p++)
if (p->line == line && !strcmp(func, p->func) && !strcmp(file, p->file))
return p->id;
pmfuncn++;
pmfuncs = (pmfunc *) sysrealloc (pmfuncs, pmfuncn*sizeof(pmfunc));
p = &pmfuncs[pmfuncn - 1];
memset (p, 0, sizeof(pmfunc));
p->func = systrdup(func);
p->file = systrdup(file);
p->line = line;
p->id = pmfuncn;
if (pmtrace.fp)
fprintf (pmtrace.fp, "@ %d %s %s %d\n", pmfuncn, func, file, line);
return pmfuncn;
}
static void pmfunc_trace (pmfunc * f, char c)
{
if (pmtrace.fp)
fprintf (pmtrace.fp, "%c %d %ld %ld %ld",
c, f->id, pmtrace.nr, pmtrace.total, f->total);
@if _GNU_SOURCE
if (pmtrace.nr % 1 == 0) {
struct rusage usage;
getrusage (RUSAGE_SELF, &usage);
if (pmtrace.fp)
fprintf (pmtrace.fp, " %ld", usage.ru_maxrss*1024);
if (!pmtrace.nr)
pmtrace.startrss = usage.ru_maxrss;
if (usage.ru_maxrss > pmtrace.maxrss)
pmtrace.maxrss = usage.ru_maxrss;
}
@endif
if (pmtrace.fp)
fputc ('\n', pmtrace.fp);
pmtrace.nr++;
}
static void * pmfunc_alloc (pmdata * d, size_t size,
const char * func, const char * file, int line,
char c)
{
assert (d != NULL);
OMP (omp critical)
{
d->id = pmfunc_index(func, file, line);
d->size = size;
pmfunc * f = &pmfuncs[d->id - 1];
f->total += size;
if (f->total > f->max)
f->max = f->total;
pmtrace.total += size;
pmtrace.overhead += sizeof(pmdata);
if (pmtrace.total > pmtrace.max) {
pmtrace.max = pmtrace.total;
pmtrace.maxoverhead = pmtrace.overhead;
}
pmfunc_trace (f, c);
}
return ((char *)d) + sizeof(pmdata);
}
static void * pmfunc_free (void * ptr, char c)
{
if (!ptr)
return ptr;
pmdata * d = (pmdata *) (((char *)ptr) - sizeof(pmdata));
if (d->id < 1 || d->id > pmfuncn) {
fputs ("*** MTRACE: ERROR!: corrupted free()", stderr);
if (d->size == 0)
fputs (", possible double free()", stderr);
else
fputs (", not traced?", stderr);
fputs (", aborting...\n", stderr);
abort();
return ptr;
}
else
OMP (omp critical)
{
pmfunc * f = &pmfuncs[d->id - 1];
if (f->total < d->size) {
fprintf (stderr, "*** MTRACE: ERROR!: %ld < %ld: corrupted free()?\n",
f->total, d->size);
abort();
}
else
f->total -= d->size;
if (pmtrace.total < d->size) {
fprintf (stderr, "*** MTRACE: ERROR!: %ld < %ld: corrupted free()?\n",
pmtrace.total, d->size);
abort();
}
else {
pmtrace.total -= d->size;
pmtrace.overhead -= sizeof(pmdata);
}
d->id = 0;
d->size = 0;
pmfunc_trace (f, c);
}
return d;
}
static void * pmalloc (size_t size,
const char * func, const char * file, int line)
{
return pmfunc_alloc ((pmdata *) sysmalloc (sizeof(pmdata) + size),
size, func, file, line, '+');
}
static void * pcalloc (size_t nmemb, size_t size,
const char * func, const char * file, int line)
{
void * p = pmalloc (nmemb*size, func, file, line);
return memset (p, 0, nmemb*size);
}
static void * prealloc (void * ptr, size_t size,
const char * func, const char * file, int line)
{
return pmfunc_alloc ((pmdata *) sysrealloc (pmfunc_free(ptr, '<'),
sizeof(pmdata) + size),
size, func, file, line, '>');
}
static void pfree (void * ptr,
const char * func, const char * file, int line)
{
sysfree (pmfunc_free (ptr, '-'));
}
static char * pstrdup (const char * s,
const char * func, const char * file, int line)
{
char * d = (char *) pmalloc (strlen(s) + 1, func, file, line);
return strcpy (d, s);
}
#if MTRACE < 3
static int pmaxsort (const void * a, const void * b) {
const pmfunc * p1 = a, * p2 = b;
return p1->max < p2->max;
}
#endif
static int ptotalsort (const void * a, const void * b) {
const pmfunc * p1 = (const pmfunc *) a, * p2 = (const pmfunc *) b;
return p1->total < p2->total;
}
static void pmfuncs_free()
{
pmfunc * p = pmfuncs;
for (int i = 0; i < pmfuncn; i++, p++) {
sysfree (p->func);
sysfree (p->file);
}
sysfree (pmfuncs);
}
void pmuntrace (void)
{
#if MTRACE < 3
fprintf (stderr,
"*** MTRACE: max resident set size: %10ld bytes\n"
"*** MTRACE: max traced memory size: %10ld bytes"
" (tracing overhead %.1g%%)\n"
"%10s %20s %s\n",
pmtrace.maxrss*1024,
pmtrace.max, pmtrace.maxoverhead*100./pmtrace.max,
"max bytes", "function", "file");
qsort (pmfuncs, pmfuncn, sizeof(pmfunc), pmaxsort);
pmfunc * p = pmfuncs;
for (int i = 0; i < pmfuncn && p->max > 0; i++, p++)
fprintf (stderr, "%10ld %20s %s:%d\n",
p->max, p->func, p->file, p->line);
if (pmtrace.fp) {
char * fname = pmtrace.fname, * s;
while ((s = strchr(fname,'/')))
fname = s + 1;
fputs ("load(\"`echo $BASILISK`/mtrace.plot\")\n", pmtrace.fp);
fprintf (pmtrace.fp,
"plot '%s' u 3:($6-%g) w l t 'ru_maxrss - %.3g',"
"total(\"%s\") w l t 'total'",
fname,
pmtrace.startrss*1024.,
pmtrace.startrss*1024.,
fname);
pmfunc * p = pmfuncs;
for (int i = 0; i < pmfuncn && p->max > 0.01*pmtrace.max; i++, p++)
fprintf (pmtrace.fp,
",func(\"%s\",%d) w l t '%s'",
fname, p->id, p->func);
fputc ('\n', pmtrace.fp);
fprintf (stderr,
"*** MTRACE: To get a graph use: tail -n 2 %s | gnuplot -persist\n",
fname);
fclose (pmtrace.fp);
pmtrace.fp = NULL;
sysfree (pmtrace.fname);
}
#endif // MTRACE < 3
if (pmtrace.total > 0) {
qsort (pmfuncs, pmfuncn, sizeof(pmfunc), ptotalsort);
pmfunc * p = pmfuncs;
for (int i = 0; i < pmfuncn && p->total > 0; i++, p++)
fprintf (stderr, "%s:%d: error: %ld bytes leaked here\n",
p->file, p->line, p->total);
pmfuncs_free();
exit(1);
}
else {
#if MTRACE < 3
fputs ("*** MTRACE: No memory leaks\n", stderr);
#endif
pmfuncs_free();
}
}