src/grid/mtrace.h

    Memory profiling

    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();
      }
    }