blob: ae7d6cdf074c3b5bcf7975751e2176d05a93f29e [file] [log] [blame]
/* This file is part of hp2ps, a graph drawer for memory profiles.
Copyright (C) 2002 The University Court of the University of Glasgow.
This program is governed by the license contained in the file LICENSE. */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Main.h"
#include "Defines.h"
#include "Error.h"
#include "HpFile.h"
#include "Utilities.h"
#ifndef atof
double atof PROTO((const char *));
#endif
/* own stuff already included */
#define N_MARKS 50 /* start size of the mark table */
#define N_SAMPLES 500 /* start size of the sample table */
char *theident;
char *thestring;
int theinteger;
floatish thefloatish;
int g_ch; /* last character read */
token thetok; /* last token */
int linenum; /* current line number */
int endfile; /* true at end of file */
static boolish gotjob = 0; /* "JOB" read */
static boolish gotdate = 0; /* "DATE" read */
static boolish gotvalueunit = 0; /* "VALUE_UNIT" read */
static boolish gotsampleunit = 0; /* "SAMPLE_UNIT" read */
static boolish insample = 0; /* true when in sample */
static floatish lastsample; /* the last sample time */
static void GetHpLine PROTO((FILE *)); /* forward */
static void GetHpTok PROTO((FILE *)); /* forward */
static struct entry *GetEntry PROTO((char *)); /* forward */
static void MakeIdentTable PROTO((void)); /* forward */
char *jobstring;
char *datestring;
char *sampleunitstring;
char *valueunitstring;
floatish *samplemap; /* sample intervals */
floatish *markmap; /* sample marks */
/*
* An extremely simple parser. The input is organised into lines of
* the form
*
* JOB s -- job identifier string
* DATE s -- date string
* SAMPLE_UNIT s -- sample unit eg "seconds"
* VALUE_UNIT s -- value unit eg "bytes"
* MARK i -- sample mark
* BEGIN_SAMPLE i -- start of ith sample
* identifier i -- there are i identifiers in this sample
* END_SAMPLE i -- end of ith sample
*
*/
void
GetHpFile(infp)
FILE *infp;
{
nsamples = 0;
nmarks = 0;
nidents = 0;
g_ch = ' ';
endfile = 0;
linenum = 1;
lastsample = 0.0;
GetHpTok(infp);
while (endfile == 0) {
GetHpLine(infp);
}
if (!gotjob) {
Error("%s: JOB missing", hpfile);
}
if (!gotdate) {
Error("%s: DATE missing", hpfile);
}
if (!gotvalueunit) {
Error("%s: VALUE_UNIT missing", hpfile);
}
if (!gotsampleunit) {
Error("%s: SAMPLE_UNIT missing", hpfile);
}
if (nsamples == 0) {
Error("%s: contains no samples", hpfile);
}
MakeIdentTable();
fclose(hpfp);
}
/*
* Read the next line from the input, check the syntax, and perform
* the appropriate action.
*/
static void
GetHpLine(infp)
FILE* infp;
{
static intish nmarkmax = 0, nsamplemax = 0;
switch (thetok) {
case JOB_TOK:
GetHpTok(infp);
if (thetok != STRING_TOK) {
Error("%s, line %d: string must follow JOB", hpfile, linenum);
}
jobstring = thestring;
gotjob = 1;
GetHpTok(infp);
break;
case DATE_TOK:
GetHpTok(infp);
if (thetok != STRING_TOK) {
Error("%s, line %d: string must follow DATE", hpfile, linenum);
}
datestring = thestring;
gotdate = 1;
GetHpTok(infp);
break;
case SAMPLE_UNIT_TOK:
GetHpTok(infp);
if (thetok != STRING_TOK) {
Error("%s, line %d: string must follow SAMPLE_UNIT", hpfile,
linenum);
}
sampleunitstring = thestring;
gotsampleunit = 1;
GetHpTok(infp);
break;
case VALUE_UNIT_TOK:
GetHpTok(infp);
if (thetok != STRING_TOK) {
Error("%s, line %d: string must follow VALUE_UNIT", hpfile,
linenum);
}
valueunitstring = thestring;
gotvalueunit = 1;
GetHpTok(infp);
break;
case MARK_TOK:
GetHpTok(infp);
if (thetok != FLOAT_TOK) {
Error("%s, line %d, floating point number must follow MARK",
hpfile, linenum);
}
if (insample) {
Error("%s, line %d, MARK occurs within sample", hpfile, linenum);
}
if (nmarks >= nmarkmax) {
if (!markmap) {
nmarkmax = N_MARKS;
markmap = (floatish*) xmalloc(nmarkmax * sizeof(floatish));
} else {
nmarkmax *= 2;
markmap = (floatish*) xrealloc(markmap, nmarkmax * sizeof(floatish));
}
}
markmap[ nmarks++ ] = thefloatish;
GetHpTok(infp);
break;
case BEGIN_SAMPLE_TOK:
insample = 1;
GetHpTok(infp);
if (thetok != FLOAT_TOK) {
Error("%s, line %d, floating point number must follow BEGIN_SAMPLE", hpfile, linenum);
}
if (thefloatish < lastsample) {
Error("%s, line %d, samples out of sequence", hpfile, linenum);
} else {
lastsample = thefloatish;
}
if (nsamples >= nsamplemax) {
if (!samplemap) {
nsamplemax = N_SAMPLES;
samplemap = (floatish*) xmalloc(nsamplemax * sizeof(floatish));
} else {
nsamplemax *= 2;
samplemap = (floatish*) xrealloc(samplemap,
nsamplemax * sizeof(floatish));
}
}
samplemap[ nsamples ] = thefloatish;
GetHpTok(infp);
break;
case END_SAMPLE_TOK:
insample = 0;
GetHpTok(infp);
if (thetok != FLOAT_TOK) {
Error("%s, line %d: floating point number must follow END_SAMPLE",
hpfile, linenum);
}
nsamples++;
GetHpTok(infp);
break;
case IDENTIFIER_TOK:
GetHpTok(infp);
if (thetok != INTEGER_TOK) {
Error("%s, line %d: integer must follow identifier", hpfile,
linenum);
}
StoreSample(GetEntry(theident), nsamples, (floatish) theinteger);
GetHpTok(infp);
break;
case EOF_TOK:
endfile = 1;
break;
default:
Error("%s, line %d: %s unexpected", hpfile, linenum,
TokenToString(thetok));
break;
}
}
char *
TokenToString(t)
token t;
{
switch (t) {
case EOF_TOK: return "EOF";
case INTEGER_TOK: return "integer";
case FLOAT_TOK: return "floating point number";
case IDENTIFIER_TOK: return "identifier";
case STRING_TOK: return "string";
case BEGIN_SAMPLE_TOK: return "BEGIN_SAMPLE";
case END_SAMPLE_TOK: return "END_SAMPLE";
case JOB_TOK: return "JOB";
case DATE_TOK: return "DATE";
case SAMPLE_UNIT_TOK: return "SAMPLE_UNIT";
case VALUE_UNIT_TOK: return "VALUE_UNIT";
case MARK_TOK: return "MARK";
case X_RANGE_TOK: return "X_RANGE";
case Y_RANGE_TOK: return "Y_RANGE";
case ORDER_TOK: return "ORDER";
case SHADE_TOK: return "SHADE";
default: return "(strange token)";
}
}
/*
* Read the next token from the input and assign its value
* to the global variable "thetok". In the case of numbers,
* the corresponding value is also assigned to "theinteger"
* or "thefloatish" as appropriate; in the case of identifiers
* it is assigned to "theident".
*/
static void
GetHpTok(infp)
FILE* infp;
{
while (isspace(g_ch)) { /* skip whitespace */
if (g_ch == '\n') linenum++;
g_ch = getc(infp);
}
if (g_ch == EOF) {
thetok = EOF_TOK;
return;
}
if (isdigit(g_ch)) {
thetok = GetNumber(infp);
return;
} else if (g_ch == '\"') {
GetString(infp);
thetok = STRING_TOK;
return;
} else if (IsIdChar(g_ch)) {
ASSERT(! (isdigit(g_ch))); /* g_ch can't be a digit here */
GetIdent(infp);
if (!isupper(theident[0])) {
thetok = IDENTIFIER_TOK;
} else if (strcmp(theident, "BEGIN_SAMPLE") == 0) {
thetok = BEGIN_SAMPLE_TOK;
} else if (strcmp(theident, "END_SAMPLE") == 0) {
thetok = END_SAMPLE_TOK;
} else if (strcmp(theident, "JOB") == 0) {
thetok = JOB_TOK;
} else if (strcmp(theident, "DATE") == 0) {
thetok = DATE_TOK;
} else if (strcmp(theident, "SAMPLE_UNIT") == 0) {
thetok = SAMPLE_UNIT_TOK;
} else if (strcmp(theident, "VALUE_UNIT") == 0) {
thetok = VALUE_UNIT_TOK;
} else if (strcmp(theident, "MARK") == 0) {
thetok = MARK_TOK;
} else {
thetok = IDENTIFIER_TOK;
}
return;
} else {
Error("%s, line %d: strange character (%c)", hpfile, linenum, g_ch);
}
}
/*
* Read a sequence of digits and convert the result to an integer
* or floating point value (assigned to the "theinteger" or
* "thefloatish").
*/
static char numberstring[ NUMBER_LENGTH ];
token
GetNumber(infp)
FILE* infp;
{
int i;
int containsdot;
ASSERT(isdigit(ch)); /* we must have a digit to start with */
containsdot = 0;
for (i = 0; i < NUMBER_LENGTH && (isdigit(g_ch) || g_ch == '.'); i++) {
numberstring[ i ] = g_ch;
containsdot |= (g_ch == '.');
g_ch = getc(infp);
}
ASSERT(i < NUMBER_LENGTH); /* did not overflow */
numberstring[ i ] = '\0';
if (containsdot) {
thefloatish = (floatish) atof(numberstring);
return FLOAT_TOK;
} else {
theinteger = atoi(numberstring);
return INTEGER_TOK;
}
}
/*
* Read a sequence of identifier characters and assign the result
* to the string "theident".
*/
void
GetIdent(infp)
FILE *infp;
{
unsigned int i;
char idbuffer[5000];
for (i = 0; i < (sizeof idbuffer)-1 && IsIdChar(g_ch); i++) {
idbuffer[ i ] = g_ch;
g_ch = getc(infp);
}
idbuffer[ i ] = '\0';
if (theident)
free(theident);
theident = copystring(idbuffer);
}
/*
* Read a sequence of characters that make up a string and
* assign the result to "thestring".
*/
void
GetString(infp)
FILE *infp;
{
unsigned int i;
char stringbuffer[5000];
ASSERT(ch == '\"');
g_ch = getc(infp); /* skip the '\"' that begins the string */
for (i = 0; i < (sizeof stringbuffer)-1 && g_ch != '\"'; i++) {
stringbuffer[ i ] = g_ch;
g_ch = getc(infp);
}
stringbuffer[i] = '\0';
thestring = copystring(stringbuffer);
ASSERT(g_ch == '\"');
g_ch = getc(infp); /* skip the '\"' that terminates the string */
}
boolish
IsIdChar(ch)
int ch;
{
return (!isspace(ch));
}
/*
* The information associated with each identifier is stored
* in a linked list of chunks. The table below allows the list
* of chunks to be retrieved given an identifier name.
*/
#define N_HASH 513
static struct entry* hashtable[ N_HASH ];
static intish
Hash(s)
char *s;
{
int r;
for (r = 0; *s; s++) {
r = r + r + r + *s;
}
if (r < 0) r = -r;
return r % N_HASH;
}
/*
* Get space for a new chunk. Initialise it, and return a pointer
* to the new chunk.
*/
static struct chunk*
MakeChunk()
{
struct chunk* ch;
struct datapoint* d;
ch = (struct chunk*) xmalloc( sizeof(struct chunk) );
d = (struct datapoint*) xmalloc (sizeof(struct datapoint) * N_CHUNK);
ch->nd = 0;
ch->d = d;
ch->next = 0;
return ch;
}
/*
* Get space for a new entry. Initialise it, and return a pointer
* to the new entry.
*/
struct entry *
MakeEntry(name)
char *name;
{
struct entry* e;
e = (struct entry *) xmalloc(sizeof(struct entry));
e->chk = MakeChunk();
e->name = copystring(name);
return e;
}
/*
* Get the entry associated with "name", creating a new entry if
* necessary.
*/
static struct entry *
GetEntry(name)
char* name;
{
intish h;
struct entry* e;
h = Hash(name);
for (e = hashtable[ h ]; e; e = e->next) {
if (strcmp(e->name, name) == 0) {
break;
}
}
if (e) {
return (e);
} else {
nidents++;
e = MakeEntry(name);
e->next = hashtable[ h ];
hashtable[ h ] = e;
return (e);
}
}
/*
* Store information from a sample.
*/
void
StoreSample(en, bucket, value)
struct entry* en; intish bucket; floatish value;
{
struct chunk* chk;
for (chk = en->chk; chk->next != 0; chk = chk->next)
;
if (chk->nd < N_CHUNK) {
chk->d[ chk->nd ].bucket = bucket;
chk->d[ chk->nd ].value = value;
chk->nd += 1;
} else {
struct chunk* t;
t = chk->next = MakeChunk();
t->d[ 0 ].bucket = bucket;
t->d[ 0 ].value = value;
t->nd += 1;
}
}
struct entry** identtable;
/*
* The hash table is useful while reading the input, but it
* becomes a liability thereafter. The code below converts
* it to a more easily processed table.
*/
static void
MakeIdentTable()
{
intish i;
intish j;
struct entry* e;
nidents = 0;
for (i = 0; i < N_HASH; i++) {
for (e = hashtable[ i ]; e; e = e->next) {
nidents++;
}
}
identtable = (struct entry**) xmalloc(nidents * sizeof(struct entry*));
j = 0;
for (i = 0; i < N_HASH; i++) {
for (e = hashtable[ i ]; e; e = e->next, j++) {
identtable[ j ] = e;
}
}
}