/* eventcnv.c: Simple event log converter
* Copyright (c) 2001 Ravenbrook Limited. See end of file for license.
*
* $Id: //info.ravenbrook.com/project/mps/version/1.107/code/eventcnv.c#1 $
*/
#include "config.h"
/* override variety setting for EVENT */
#define EVENT
#include "eventcom.h"
#include "eventpro.h"
#include "mpmtypes.h"
#include <stddef.h> /* for size_t */
#include <stdio.h> /* for printf */
#include <stdarg.h> /* for va_list */
#include <stdlib.h> /* for EXIT_FAILURE */
#include <assert.h> /* for assert */
#include <string.h> /* for strcmp */
#include <math.h> /* for sqrt */
#include "mpstd.h"
#ifdef MPS_OS_SU
#include "ossu.h"
#endif
typedef unsigned int uint;
typedef unsigned long ulong;
static Word eventTime; /* current event time */
/* event counters */
typedef unsigned long eventCountArray[EventCodeMAX+1];
static unsigned long bucketEventCount[EventCodeMAX+1];
static unsigned long totalEventCount[EventCodeMAX+1];
static char *prog; /* program name */
/* command-line arguments */
static Bool verbose = FALSE;
/* style: '\0' for human-readable, 'L' for Lisp, 'C' for CDF. */
static char style = '\0';
static Bool reportEvents = FALSE;
static Bool eventEnabled[EventCodeMAX+1];
static Bool partialLog = FALSE;
static Word bucketSize = 0;
/* error -- error signalling */
static void error(const char *format, ...)
{
va_list args;
fflush(stdout); /* sync */
fprintf(stderr, "%s: @%lu ", prog, (ulong)eventTime);
va_start(args, format);
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
va_end(args);
exit(EXIT_FAILURE);
}
/* usage -- usage message */
static void usage(void)
{
fprintf(stderr,
"Usage: %s [-f logfile] [-p] [-v] [-e events] [-b size]"
" [-S[LC]] [-?]\nSee guide.mps.telemetry for instructions.",
prog);
}
/* usageError -- explain usage and error */
static void usageError(void)
{
usage();
error("Bad usage");
}
/* parseEventSpec -- parses an event spec
*
* The spec is of the form: <name>[(+|-)<name>]...
* The first name can be 'all'.
*/
static void parseEventSpec(const char *arg)
{
size_t arglen;
EventCode i;
const char *end;
char name[EventNameMAX+1];
Bool enabled = TRUE;
end = arg + strlen(arg);
for(i = 0; i <= EventCodeMAX; ++i)
eventEnabled[i] = FALSE;
do {
arglen = strcspn(arg, "+-");
strncpy(name, arg, arglen); name[arglen] = '\0';
if (strcmp(name, "all") == 0) {
for(i = 0; i <= EventCodeMAX; ++i)
eventEnabled[i] = EventCodeIsValid(i);
} else
eventEnabled[EventName2Code(name)] = enabled;
enabled = (arg[arglen] == '+'); arg += arglen + 1;
} while (arg < end);
}
/* parseArgs -- parse command line arguments, return log file name */
static char *parseArgs(int argc, char *argv[])
{
char *name = "mpsio.log";
int i = 1;
if (argc >= 1)
prog = argv[0];
else
prog = "unknown";
while (i < argc) { /* consider argument i */
if (argv[i][0] == '-') { /* it's an option argument */
switch (argv[i][1]) {
case 'f': /* file name */
++ i;
if (i == argc)
usageError();
else
name = argv[i];
break;
case 'p': /* partial log */
partialLog = TRUE;
break;
case 'v': /* verbosity */
verbose = TRUE;
break;
case 'e': { /* event statistics */
reportEvents = TRUE;
++ i;
if (i == argc)
usageError();
else
parseEventSpec(argv[i]);
} break;
case 'b': { /* bucket size */
++ i;
if (i == argc)
usageError();
else {
int n;
n = sscanf(argv[i], "%lu", &bucketSize);
if (n != 1) usageError();
}
} break;
case 'S': /* style */
style = argv[i][2]; /* '\0' for human-readable, 'L' for Lisp, */
break; /* 'C' for CDF. */
case '?': case 'h': /* help */
usage();
break;
default:
usageError();
}
} /* if option */
++ i;
}
return name;
}
/* processEvent -- process event */
static void processEvent(EventProc proc, Event event, Word etime)
{
Res res;
res = EventRecord(proc, event, etime);
if (res != ResOK)
error("Can't record event: error %d.", res);
switch(event->any.code) {
default:
break;
}
}
/* Printing routines */
/* printStr -- print an EventString */
static void printStr(EventString str, Bool quotes)
{
size_t i, len;
if (quotes) putchar('"');
len = str->len;
for (i = 0; i < len; ++ i) {
char c = str->str[i];
if (quotes && (c == '"' || c == '\\')) putchar('\\');
putchar(c);
}
if (quotes) putchar('"');
}
/* printAddr -- print an Addr or its label */
static void printAddr(EventProc proc, Addr addr)
{
Word label;
label = AddrLabel(proc, addr);
if (label != 0 && addr != 0) {
/* We assume labelling zero is meant to record a point in time */
EventString sym = LabelText(proc, label);
if (sym != NULL) {
putchar(' ');
printStr(sym, (style == 'C'));
} else {
printf((style == '\0') ? " sym%05lX" : " \"sym %lX\"",
(ulong)label);
}
} else
printf((style != 'C') ? " %08lX" : " %lu", (ulong)addr);
}
/* reportEventResults -- report event counts from a count array */
static void reportEventResults(eventCountArray eventCounts)
{
EventCode i;
unsigned long total = 0;
for(i = 0; i <= EventCodeMAX; ++i) {
total += eventCounts[i];
if (eventEnabled[i])
switch (style) {
case '\0':
printf(" %5lu", eventCounts[i]);
break;
case 'L':
printf(" %lX", eventCounts[i]);
break;
case 'C':
printf(", %lu", eventCounts[i]);
break;
}
}
switch (style) {
case '\0':
printf(" %5lu\n", total);
break;
case 'L':
printf(" %lX)\n", total);
break;
case 'C':
printf(", %lu\n", total);
break;
}
}
/* reportBucketResults -- report results of the current bucket */
static void reportBucketResults(Word bucketLimit)
{
switch (style) {
case '\0':
printf("%8lu:", (ulong)bucketLimit);
break;
case 'L':
printf("(%lX", (ulong)bucketLimit);
break;
case 'C':
printf("%lu", (ulong)bucketLimit);
break;
}
if (reportEvents) {
reportEventResults(bucketEventCount);
}
}
/* clearBucket -- clear bucket */
static void clearBucket(void)
{
EventCode i;
for(i = 0; i <= EventCodeMAX; ++i)
bucketEventCount[i] = 0;
}
/* readLog -- read and parse log
*
* This is the heart of eventcnv: It reads an event log using EventRead.
* It updates the counters. If verbose is true, it looks up the format,
* parses the arguments, and prints a representation of the event. Each
* argument is printed using printArg (see RELATION, below), except for
* some event types that are handled specially.
*/
static void printArg(EventProc proc,
void *arg, char argType, char *styleConv)
{
switch (argType) {
case 'A': {
if (style != 'L') {
if (style == 'C') putchar(',');
printAddr(proc, *(Addr *)arg);
} else
printf(styleConv, (ulong)*(Addr *)arg);
} break;
case 'P': {
printf(styleConv, (ulong)*(void **)arg);
} break;
case 'U': {
printf(styleConv, (ulong)*(unsigned *)arg);
} break;
case 'W': {
printf(styleConv, (ulong)*(Word *)arg);
} break;
case 'D': {
switch (style) {
case '\0':
printf(" %#8.3g", *(double *)arg); break;
case 'C':
printf(", %.10G", *(double *)arg); break;
case 'L':
printf(" %#.10G", *(double *)arg); break;
}
} break;
case 'S': {
if (style == 'C') putchar(',');
putchar(' ');
printStr((EventStringStruct *)arg, (style == 'C' || style == 'L'));
} break;
default: error("Can't print format >%c<", argType);
}
}
#define RELATION(name, code, always, kind, format) \
case code: { \
printArg(proc, EVENT_##format##_FIELD_PTR(event, i), \
eventFormat[i], styleConv); \
} break;
static void readLog(EventProc proc)
{
EventCode c;
Word bucketLimit = bucketSize;
char *styleConv = NULL; /* suppress uninit warning */
/* Print event count header. */
if (reportEvents) {
if (style == '\0') {
printf(" bucket:");
for(c = 0; c <= EventCodeMAX; ++c)
if (eventEnabled[c])
printf(" %04X", (unsigned)c);
printf(" all\n");
}
}
/* Init event counts. */
for(c = 0; c <= EventCodeMAX; ++c)
totalEventCount[c] = 0;
clearBucket();
/* Init style. */
switch (style) {
case '\0':
styleConv = " %8lX"; break;
case 'C':
styleConv = ", %lu"; break;
case 'L':
styleConv = " %lX"; break;
default:
error("Unknown style code '%c'", style);
}
while (TRUE) { /* loop for each event */
char *eventFormat;
int argCount, i;
Event event;
EventCode code;
Res res;
/* Read and parse event. */
res = EventRead(&event, proc);
if (res == ResFAIL) break; /* eof */
if (res != ResOK) error("Truncated log");
eventTime = event->any.clock;
code = EventGetCode(event);
/* Output bucket, if necessary, and update counters */
if (bucketSize != 0 && eventTime >= bucketLimit) {
reportBucketResults(bucketLimit-1);
clearBucket();
do {
bucketLimit += bucketSize;
} while (eventTime >= bucketLimit);
}
if (reportEvents) {
++bucketEventCount[code];
++totalEventCount[code];
}
/* Output event. */
if (verbose) {
eventFormat = EventCode2Format(code);
argCount = strlen(eventFormat);
if (eventFormat[0] == '0') argCount = 0;
if (style == 'L') putchar('(');
switch (style) {
case '\0': case 'L': {
printf("%-19s", EventCode2Name(code));
} break;
case 'C':
printf("%u", (unsigned)code);
break;
}
switch (style) {
case '\0':
printf(" %8lu", (ulong)eventTime); break;
case 'C':
printf(", %lu", (ulong)eventTime); break;
case 'L':
printf(" %lX", (ulong)eventTime); break;
}
switch (event->any.code) {
case EventLabel: {
switch (style) {
case '\0': case 'C': {
EventString sym = LabelText(proc, event->aw.w1);
printf((style == '\0') ? " %08lX " : ", %lu, ",
(ulong)event->aw.a0);
if (sym != NULL) {
printStr(sym, (style == 'C'));
} else {
printf((style == '\0') ? "sym %05lX" : "sym %lX\"",
(ulong)event->aw.w1);
}
} break;
case 'L': {
printf(" %lX %lX", (ulong)event->aw.a0, (ulong)event->aw.w1);
} break;
}
} break;
case EventMeterValues: {
switch (style) {
case '\0': {
if (event->pddwww.w3 == 0) {
printf(" %08lX 0 N/A N/A N/A N/A",
(ulong)event->pddwww.p0);
} else {
double mean = event->pddwww.d1 / (double)event->pddwww.w3;
/* .stddev: stddev = sqrt(meanSquared - mean^2), but see */
/* <code/meter.c#limitation.variance>. */
double stddev = sqrt(fabs(event->pddwww.d2
- (mean * mean)));
printf(" %08lX %8u %8u %8u %#8.3g %#8.3g",
(ulong)event->pddwww.p0, (uint)event->pddwww.w3,
(uint)event->pddwww.w4, (uint)event->pddwww.w5,
mean, stddev);
}
printAddr(proc, (Addr)event->pddwww.p0);
} break;
case 'C': {
putchar(',');
printAddr(proc, (Addr)event->pddwww.p0);
printf(", %.10G, %.10G, %u, %u, %u",
event->pddwww.d1, event->pddwww.d2,
(uint)event->pddwww.w3, (uint)event->pddwww.w4,
(uint)event->pddwww.w5);
} break;
case 'L': {
printf(" %lX %#.10G %#.10G %X %X %X", (ulong)event->pddwww.p0,
event->pddwww.d1, event->pddwww.d2,
(uint)event->pddwww.w3, (uint)event->pddwww.w4,
(uint)event->pddwww.w5);
} break;
}
} break;
case EventPoolInit: { /* pool, arena, class */
printf(styleConv, (ulong)event->ppp.p0);
printf(styleConv, (ulong)event->ppp.p1);
/* class is a Pointer, but we label them, so call printAddr */
if (style != 'L') {
if (style == 'C') putchar(',');
printAddr(proc, (Addr)event->ppp.p2);
} else
printf(styleConv, (ulong)event->ppp.p2);
} break;
default:
for (i = 0; i < argCount; ++i) {
switch(code) {
#include "eventdef.h"
#undef RELATION
}
}
}
if (style == 'L') putchar(')');
putchar('\n');
fflush(stdout);
}
processEvent(proc, event, eventTime);
EventDestroy(proc, event);
} /* while(!feof(input)) */
/* report last bucket (partial) */
if (bucketSize != 0) {
reportBucketResults(eventTime);
}
if (reportEvents) {
/* report totals */
switch (style) {
case '\0': {
printf("\n run:");
} break;
case 'L': {
printf("(t");
} break;
case 'C': {
printf("%lu", eventTime+1);
} break;
}
reportEventResults(totalEventCount);
/* explain event codes */
if (style == '\0') {
printf("\n");
for(c = 0; c <= EventCodeMAX; ++c)
if (eventEnabled[c])
printf(" %04X %s\n", (unsigned)c, EventCode2Name(c));
if (bucketSize == 0)
printf("\nevent clock stopped at %lu\n", (ulong)eventTime);
}
}
}
/* logReader -- reader function for a file log */
static FILE *input;
static Res logReader(void *file, void *p, size_t len)
{
size_t n;
n = fread(p, 1, len, (FILE *)file);
return (n < len) ? (feof((FILE *)file) ? ResFAIL : ResIO) : ResOK;
}
/* CHECKCONV -- check t2 can be cast to t1 without loss */
#define CHECKCONV(t1, t2) \
(sizeof(t1) >= sizeof(t2))
/* main */
int main(int argc, char *argv[])
{
char *filename;
EventProc proc;
Res res;
#if !defined(MPS_OS_FR)
/* GCC -ansi -pedantic -Werror on FreeBSD will fail here
* with the warning "statement with no effect". */
assert(CHECKCONV(ulong, Word));
assert(CHECKCONV(ulong, Addr));
assert(CHECKCONV(ulong, void *));
assert(CHECKCONV(unsigned, EventCode));
assert(CHECKCONV(Addr, void *)); /* for labelled pointers */
#endif
filename = parseArgs(argc, argv);
if (strcmp(filename, "-") == 0)
input = stdin;
else {
input = fopen(filename, "rb");
if (input == NULL)
error("unable to open \"%s\"\n", filename);
}
res = EventProcCreate(&proc, partialLog, logReader, (void *)input);
if (res != ResOK)
error("Can't init EventProc module: error %d.", res);
readLog(proc);
EventProcDestroy(proc);
return EXIT_SUCCESS;
}
/* C. COPYRIGHT AND LICENSE
*
* Copyright (C) 2001-2002 Ravenbrook Limited <http://www.ravenbrook.com/>.
* All rights reserved. This is an open source license. Contact
* Ravenbrook for commercial licensing options.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Redistributions in any form must be accompanied by information on how
* to obtain complete source code for this software and any accompanying
* software that uses this software. The source code must either be
* included in the distribution or be available for no more than the cost
* of distribution plus a nominal fee, and must be freely redistributable
* under reasonable conditions. For an executable file, complete source
* code means the source code for all modules it contains. It does not
* include source code for modules or files that typically accompany the
* major components of the operating system on which the executable file
* runs.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/