/*  mpdprof.c -- summarize MPD_TRACE info to create program profile  */

#include <ctype.h>
#include <stdio.h>
#include "../gen.h"
#include "../util.h"

#define MAXLINES 10000		/* max lines in a source file */

struct evcount {		/* struct for counting events on a line */
    struct evcount *next;
    char *event;
    long count;
};

struct mpdfile {		/* struct for each source file */
    struct mpdfile *next;
    char *name;
    struct evcount *ev[MAXLINES+1];
};

struct mpdfile *getfile ();
struct evcount *getevent ();

char *tname;			/* trace file name, if any */
FILE *tfile;			/* trace file */
int aflag;			/* -a (annotate listing) flag */
struct mpdfile *flist;		/* list of mentioned files */



/*  main program  */

main (argc, argv)
int argc;
char *argv[];
{
    char buf[200], fname[200], proc[200], event[200];
    int lno;
    struct mpdfile *f;
    struct evcount *e;

    options (argc, argv);			/* process options */
    xprefix = argv[0];				/* save program name */
    if (tname)
	tfile = mustopen (tname, "r");		/* open input file */
    else
	tfile = stdin;

    while (fgets (buf, sizeof (buf), tfile))	/* for each line: */
	if (sscanf (buf, "%s %d %s %s", fname, &lno, proc, event) == 4) {
	    if (lno < 0 || lno > MAXLINES)
		continue;
	    /* this is a valid trace event */
	    fname [strlen (fname) - 1] = '\0';	/* chop comma */
	    f = getfile (fname);		/* get list for src file */
	    e = getevent (f, lno, event);	/* get event counter */
	    e->count++;				/* incr count */
	}

    for (f = flist; f != NULL; f = f->next)
	report (f);				/* report each source file */

    exit (0);
    /*NOTREACHED*/
}



/*  options (argc, argv) -- process command options  */

options (argc, argv)
int argc;
char *argv[];
{
    int c;
    extern int optind;
    extern char *optarg;

    /* parse command line options. */
#define USAGE "usage: mpdprof [-a] [file]"
    while ((c = getopt (argc, argv, "a")) != EOF)
	switch (c)  {
	    case 'a':
		aflag++;
		break;
	    default:
		mexit (USAGE);
	}
    if (argc - optind > 1)
	mexit (USAGE);
    if (optind < argc)
	tname = argv[optind];
}



/*  getfile (filename) -- find mpdfile struct for the given file  */

struct mpdfile *getfile (fname)
char *fname;
{
    struct mpdfile *f, **p;

    p = &flist;
    while ((f = *p) != NULL) {
	if (strcmp (f->name, fname) == 0)
	    return f;
	p = &f->next;
	}
    /*
     * Need to create a new struct.
     * Install at end of list to preserve order.
     */
    f = (struct mpdfile *) alloc (sizeof (struct mpdfile));
    memset ((char *) f, 0, sizeof (struct mpdfile));
    f->name = salloc (fname);
    *p = f;
    return f;
}



/*  getevent (f, lno, event) -- find evcount struct for the given event */

struct evcount *
getevent (f, lno, event)
struct mpdfile *f;
int lno;
char *event;
{
    struct evcount *e, **p;

    p = &f->ev[lno];
    while ((e = *p) != NULL) {
	if (strcmp (e->event, event) == 0)
	    return e;
	p = &e->next;
	}
    /*
     * Need to create a new struct.
     * Install at end of list to preserve order.
     */
    e = (struct evcount *) alloc (sizeof (struct evcount));
    e->event = salloc (event);
    e->count = 0;
    e->next = 0;
    *p = e;
    return e;
}



/*  report(f) -- produce report for file f  */

report (f)
struct mpdfile *f;
{
    FILE *fp;
    int lno;
    struct evcount *e;

    if (aflag) {
	if (fp = fopen (f->name, "r")) {
	    annotate (fp, f);
	    return;
	} else
	    perror (f->name);
	    /* and fall through to non-annotated report */
    }

    /* report summary */
    for (lno = 1; lno <= MAXLINES; lno++)
	if ((e = f->ev[lno]) != NULL) {
	    printf ("%s,%4d ", f->name, lno);
	    putcounts (e);
	}
    if (f->next != NULL)
	putchar ('\n');
}



/*  annotate (fp, f) -- produce annotated listing for file f  */

annotate (fp, f)
FILE *fp;
struct mpdfile *f;
{
    int lno, c;
    char buf[500], *p;
    struct evcount *e;

    printf ("#====================\n");
    printf ("#  %s\n", f->name);
    printf ("#====================\n");
    for (lno = 1; lno <= MAXLINES; lno++) {
	if (!fgets (buf, sizeof (buf), fp))
	    break;
	fputs (buf, stdout);
	e = f->ev[lno];
	if (e != NULL) {
	    for (p = buf; isspace (c = *p++) && c != '\n'; )
		putchar (c);
	    putchar ('#');
	    putcounts (e);
	    }
	}
    for (; lno <= MAXLINES; lno++)
	if ((e = f->ev[lno]) != NULL) {
	    printf ("(beyond EOF) %s,%4d ", f->name, lno);
	    putcounts (e);
	}
    if (f->next != NULL)
	putchar ('\n');
}



/*  putcounts (e) -- output counts beginning with e, and terminate with \n  */

putcounts (e)
struct evcount *e;
{
    while (e != NULL) {
	printf ("  %s:%ld", e->event, e->count);
	e = e->next;
	}
    putchar ('\n');
}
