/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* ZenProfiler
* Copyright (C) 2001-2007 Jeffrey Stedfast
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __ZENPROFILER_H__
#define __ZENPROFILER_H__
#ifdef ENABLE_ZENPROFILER
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#ifdef ENABLE_THREADS
#include <pthread.h>
#endif
#ifndef ENABLE_ZENTIMER
#ifdef ENABLE_ZENPROFILER
#define ENABLE_ZENTIMER
#endif
#endif /* ENABLE_ZENTIMER */
#include "zentimer.h"
#ifdef __cplusplus
extern "C" {
#pragma }
#endif /* __cplusplus */
typedef struct _zfunc_t {
struct _zfunc_t *next;
uint32_t ncalls;
ztime_t total;
char *name;
} zfunc_t;
static struct {
GHashTable *hash;
zfunc_t *functions, *tail;
uint32_t nfuncs;
ztimer_t ztimer;
FILE *log;
} zprof = { NULL, NULL, (zfunc_t *) &zprof.functions, 0, ZTIMER_INITIALIZER, NULL };
#ifdef ENABLE_THREADS
static pthread_mutex_t zlock = PTHREAD_MUTEX_INITIALIZER;
#define ZENPROFILER_LOCK() pthread_mutex_lock (&zlock)
#define ZENPROFILER_UNLOCK() pthread_mutex_unlock (&zlock)
#else
#define ZENPROFILER_LOCK()
#define ZENPROFILER_UNLOCK()
#endif /* ENABLE_THREADS */
static char *
zfuncname (const char *func)
{
register const const char *inptr = func;
const char *name;
while (*inptr == ' ' || *inptr == '\t')
inptr++;
name = inptr;
while (*inptr && *inptr != ' ' && *inptr != '(')
inptr++;
return g_strndup (name, inptr - name);
}
/**
* ZenProfilerInit:
* @logfile: File to save profiling information (or %NULL to log to stderr)
*
* Initializes the Zen Profiler.
**/
static void
ZenProfilerInit (const char *logfile)
{
zprof.hash = g_hash_table_new (g_str_hash, g_str_equal);
if (logfile)
zprof.log = fopen (logfile, "wt");
ZenTimerStart (&zprof.ztimer);
}
static void
zprofile (char *name, ztimer_t *ztimer)
{
zfunc_t *zfunc;
ztime_t delta;
char *key;
ZENPROFILER_LOCK();
if (!g_hash_table_lookup_extended (zprof.hash, name, (gpointer *) &key, (gpointer *) &zfunc)) {
zfunc = g_new (zfunc_t, 1);
zfunc->next = NULL;
zfunc->name = name;
zfunc->ncalls = 0;
zfunc->total.sec = 0;
zfunc->total.usec = 0;
zprof.tail->next = zfunc;
zprof.tail = zfunc;
g_hash_table_insert (zprof.hash, zfunc->name, zfunc);
zprof.nfuncs++;
} else
g_free (name);
ztime_delta (&ztimer->start, &ztimer->stop, &delta);
ztime_add (&zfunc->total, &delta);
zfunc->ncalls++;
ZENPROFILER_UNLOCK();
}
/**
* ZenProfilerLazy:
* @func: Function call to profile.
*
* Profiles the provided function call. This is basically the lazy way out.
*
* Sample usage: ZenProfilerLazy(printf ("how long does this take??\n"));
**/
#define ZenProfilerLazy(func) G_STMT_START { \
ztimer_t ztimer; \
\
ZenTimerStart (&ztimer); \
func; \
ZenTimerStop (&ztimer); \
\
zprofile (zfuncname (#func), &ztimer); \
} G_STMT_END
/**
* ZenProfilerStart:
* @ztimerp: pointer to a ztimer_t
*
* Initializes @zstart.
**/
#define ZenProfilerStart(ztimerp) ZenTimerStart (ztimerp)
/**
* ZenProfilerStop:
* @ztimerp: pointer to a ztimer_t
*
* Initializes @zstop.
**/
#define ZenProfilerStop(ztimerp) ZenTimerStop (ztimerp)
/**
* ZenProfilerReport:
* @ztimerp: pointer to a ztimer_t
*
* Cache the results for later reporting.
**/
#define ZenProfilerReport(ztimerp) zprofile (g_strdup (G_GNUC_PRETTY_FUNCTION), ztimerp)
/**
* ZenProfiler_return:
* @ztimer: a ztimer_t
*
* Stop the timer, report the results, and then return from the function.
*
* Note: equivalent to: ZenProfilerStop(@ztimer); ZenProfilerReport(@ztimer); return;
**/
#define ZenProfiler_return(ztimerp) G_STMT_START { \
ZenProfilerStop (ztimerp); \
ZenProfilerReport (ztimerp); \
return; \
} G_STMT_END
/**
* ZenProfiler_return_val:
* @ztimer: a ztimer_t
*
* Same as ZenProfiler_return() but returns the specified value.
**/
#define ZenProfiler_return_val(ztimerp, retval) G_STMT_START { \
ZenProfilerStop (ztimerp); \
ZenProfilerReport (ztimerp); \
return retval; \
} G_STMT_END
static void
zen_log (FILE *log, zfunc_t *zfunc, uint32_t usec)
{
char percent[7], total[20], calls[20], average[20];
uint32_t us, avgus;
double pcnt = 0.0;
us = (zfunc->total.sec * ZTIME_USEC_PER_SEC) + zfunc->total.usec;
pcnt = ((double) us) / ((double) usec) * 100.0;
avgus = us / zfunc->ncalls;
sprintf (percent, "%2.2f", pcnt);
sprintf (calls, "%u", zfunc->ncalls);
sprintf (total, "%u.%03u", zfunc->total.sec, zfunc->total.usec / 1000);
sprintf (average, "%u.%03u", avgus / ZTIME_USEC_PER_SEC, (avgus % ZTIME_USEC_PER_SEC) / 1000);
fprintf (log, "%6s %9s %7s %7s %7s %7s %s\n",
percent, total, "n/a", calls, "n/a", average, zfunc->name);
}
static int
zfunccmp (const void *v1, const void *v2)
{
const zfunc_t *func1 = (const zfunc_t *) v1;
const zfunc_t *func2 = (const zfunc_t *) v2;
uint32_t usec1, usec2;
usec1 = (func1->total.sec * ZTIME_USEC_PER_SEC) + func1->total.usec;
usec2 = (func2->total.sec * ZTIME_USEC_PER_SEC) + func2->total.usec;
return usec2 - usec1;
}
/**
* ZenProfilerShutdown:
*
* Write out our profiling info and cleanup the mess we made.
**/
static void
ZenProfilerShutdown (void)
{
FILE *log = zprof.log ? zprof.log : stderr;
uint32_t usec, i;
zfunc_t *zfunc;
ztime_t delta;
void **pdata;
ZenTimerStop (&zprof.ztimer);
ztime_delta (&zprof.ztimer.start, &zprof.ztimer.stop, &delta);
fprintf (log, "ZenProfiler\n\n");
fprintf (log, "Flat profile:\n\n");
fprintf (log, " %% cumulative self self total\n");
fprintf (log, " time seconds seconds calls s/call s/call name\n");
fprintf (log, "------ --------- ------- ------- ----------- ----------- ----\n");
pdata = g_malloc (sizeof (void *) * zprof.nfuncs);
for (i = 0, zfunc = zprof.functions; i < zprof.nfuncs; i++, zfunc = zfunc->next)
pdata[i] = zfunc;
qsort (pdata, zprof.nfuncs, sizeof (void *), zfunccmp);
usec = (delta.sec * ZTIME_USEC_PER_SEC) + delta.usec;
for (i = 0; i < zprof.nfuncs; i++) {
zfunc = pdata[i];
zen_log (log, zfunc, usec);
g_free (zfunc->name);
g_free (zfunc);
}
g_hash_table_destroy (zprof.hash);
g_free (pdata);
fprintf (log, "\n %% the percentage of the total running time of the\n");
fprintf (log, "time program used by this function.\n\n");
fprintf (log, "cumulative a running sum of the number of seconds accounted\n");
fprintf (log, " seconds for by this function and those listed above it.\n\n");
fprintf (log, " self the number of seconds accounted for by this\n");
fprintf (log, "seconds function alone. This is the major sort for this\n");
fprintf (log, " listing.\n\n");
fprintf (log, "calls the number of times this function was invoked, if\n");
fprintf (log, " this function is profiled, else blank.\n\n");
fprintf (log, " self the average number of seconds spent in this\n");
fprintf (log, "s/call function per call, if this function is profiled,\n");
fprintf (log, " else blank.\n\n");
fprintf (log, " total the average number of seconds spent in this\n");
fprintf (log, " s/call function and its descendents per call, if this \n");
fprintf (log, " function is profiled, else blank.\n\n");
fprintf (log, "name the name of the function.\n\n");
if (zprof.log)
fclose (zprof.log);
}
#ifdef __cplusplus
}
#endif /* __cplusplus */
#else /* ENABLE_ZENPROFILER */
#define ZenProfilerInit(logfile)
#define ZenProfilerLazy(func) func
#define ZenProfilerStart(ztimerp)
#define ZenProfilerStop(ztimerp)
#define ZenProfilerReport(ztimerp)
#define ZenProfiler_return(ztimerp) return
#define ZenProfiler_return_val(ztimerp, retval) return (retval)
#define ZenProfilerShutdown()
#endif /* ENABLE_ZENPROFILER */
#define Z(func) ZenProfilerLazy(func)
#endif /* __ZENPROFILER_H__ */
syntax highlighted by Code2HTML, v. 0.9.1