blob: ae7d6cdf074c3b5bcf7975751e2176d05a93f29e [file] [log] [blame]
nethercote5912c812004-02-15 15:38:08 +00001/* This file is part of hp2ps, a graph drawer for memory profiles.
2 Copyright (C) 2002 The University Court of the University of Glasgow.
3 This program is governed by the license contained in the file LICENSE. */
4
nethercotec9f36922004-02-14 16:40:02 +00005#include <ctype.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include "Main.h"
10#include "Defines.h"
11#include "Error.h"
12#include "HpFile.h"
13#include "Utilities.h"
14
15#ifndef atof
16double atof PROTO((const char *));
17#endif
18
19/* own stuff already included */
20
21#define N_MARKS 50 /* start size of the mark table */
22#define N_SAMPLES 500 /* start size of the sample table */
23
24char *theident;
25char *thestring;
26int theinteger;
27floatish thefloatish;
28int g_ch; /* last character read */
29token thetok; /* last token */
30int linenum; /* current line number */
31int endfile; /* true at end of file */
32
33static boolish gotjob = 0; /* "JOB" read */
34static boolish gotdate = 0; /* "DATE" read */
35static boolish gotvalueunit = 0; /* "VALUE_UNIT" read */
36static boolish gotsampleunit = 0; /* "SAMPLE_UNIT" read */
37static boolish insample = 0; /* true when in sample */
38
39static floatish lastsample; /* the last sample time */
40
41static void GetHpLine PROTO((FILE *)); /* forward */
42static void GetHpTok PROTO((FILE *)); /* forward */
43
44static struct entry *GetEntry PROTO((char *)); /* forward */
45
46static void MakeIdentTable PROTO((void)); /* forward */
47
48char *jobstring;
49char *datestring;
50
51char *sampleunitstring;
52char *valueunitstring;
53
54floatish *samplemap; /* sample intervals */
55floatish *markmap; /* sample marks */
56
57/*
58 * An extremely simple parser. The input is organised into lines of
59 * the form
60 *
61 * JOB s -- job identifier string
62 * DATE s -- date string
63 * SAMPLE_UNIT s -- sample unit eg "seconds"
64 * VALUE_UNIT s -- value unit eg "bytes"
65 * MARK i -- sample mark
66 * BEGIN_SAMPLE i -- start of ith sample
67 * identifier i -- there are i identifiers in this sample
68 * END_SAMPLE i -- end of ith sample
69 *
70 */
71
72void
73GetHpFile(infp)
74 FILE *infp;
75{
76 nsamples = 0;
77 nmarks = 0;
78 nidents = 0;
79
80 g_ch = ' ';
81 endfile = 0;
82 linenum = 1;
83 lastsample = 0.0;
84
85 GetHpTok(infp);
86
87 while (endfile == 0) {
88 GetHpLine(infp);
89 }
90
91 if (!gotjob) {
92 Error("%s: JOB missing", hpfile);
93 }
94
95 if (!gotdate) {
96 Error("%s: DATE missing", hpfile);
97 }
98
99 if (!gotvalueunit) {
100 Error("%s: VALUE_UNIT missing", hpfile);
101 }
102
103 if (!gotsampleunit) {
104 Error("%s: SAMPLE_UNIT missing", hpfile);
105 }
106
107 if (nsamples == 0) {
108 Error("%s: contains no samples", hpfile);
109 }
110
111
112 MakeIdentTable();
113
114 fclose(hpfp);
115}
116
117
118/*
119 * Read the next line from the input, check the syntax, and perform
120 * the appropriate action.
121 */
122
123static void
124GetHpLine(infp)
125 FILE* infp;
126{
127 static intish nmarkmax = 0, nsamplemax = 0;
128
129 switch (thetok) {
130 case JOB_TOK:
131 GetHpTok(infp);
132 if (thetok != STRING_TOK) {
133 Error("%s, line %d: string must follow JOB", hpfile, linenum);
134 }
135 jobstring = thestring;
136 gotjob = 1;
137 GetHpTok(infp);
138 break;
139
140 case DATE_TOK:
141 GetHpTok(infp);
142 if (thetok != STRING_TOK) {
143 Error("%s, line %d: string must follow DATE", hpfile, linenum);
144 }
145 datestring = thestring;
146 gotdate = 1;
147 GetHpTok(infp);
148 break;
149
150 case SAMPLE_UNIT_TOK:
151 GetHpTok(infp);
152 if (thetok != STRING_TOK) {
153 Error("%s, line %d: string must follow SAMPLE_UNIT", hpfile,
154 linenum);
155 }
156 sampleunitstring = thestring;
157 gotsampleunit = 1;
158 GetHpTok(infp);
159 break;
160
161 case VALUE_UNIT_TOK:
162 GetHpTok(infp);
163 if (thetok != STRING_TOK) {
164 Error("%s, line %d: string must follow VALUE_UNIT", hpfile,
165 linenum);
166 }
167 valueunitstring = thestring;
168 gotvalueunit = 1;
169 GetHpTok(infp);
170 break;
171
172 case MARK_TOK:
173 GetHpTok(infp);
174 if (thetok != FLOAT_TOK) {
175 Error("%s, line %d, floating point number must follow MARK",
176 hpfile, linenum);
177 }
178 if (insample) {
179 Error("%s, line %d, MARK occurs within sample", hpfile, linenum);
180 }
181 if (nmarks >= nmarkmax) {
182 if (!markmap) {
183 nmarkmax = N_MARKS;
184 markmap = (floatish*) xmalloc(nmarkmax * sizeof(floatish));
185 } else {
186 nmarkmax *= 2;
187 markmap = (floatish*) xrealloc(markmap, nmarkmax * sizeof(floatish));
188 }
189 }
190 markmap[ nmarks++ ] = thefloatish;
191 GetHpTok(infp);
192 break;
193
194 case BEGIN_SAMPLE_TOK:
195 insample = 1;
196 GetHpTok(infp);
197 if (thetok != FLOAT_TOK) {
198 Error("%s, line %d, floating point number must follow BEGIN_SAMPLE", hpfile, linenum);
199 }
200 if (thefloatish < lastsample) {
201 Error("%s, line %d, samples out of sequence", hpfile, linenum);
202 } else {
203 lastsample = thefloatish;
204 }
205 if (nsamples >= nsamplemax) {
206 if (!samplemap) {
207 nsamplemax = N_SAMPLES;
208 samplemap = (floatish*) xmalloc(nsamplemax * sizeof(floatish));
209 } else {
210 nsamplemax *= 2;
211 samplemap = (floatish*) xrealloc(samplemap,
212 nsamplemax * sizeof(floatish));
213 }
214 }
215 samplemap[ nsamples ] = thefloatish;
216 GetHpTok(infp);
217 break;
218
219 case END_SAMPLE_TOK:
220 insample = 0;
221 GetHpTok(infp);
222 if (thetok != FLOAT_TOK) {
223 Error("%s, line %d: floating point number must follow END_SAMPLE",
224 hpfile, linenum);
225 }
226 nsamples++;
227 GetHpTok(infp);
228 break;
229
230 case IDENTIFIER_TOK:
231 GetHpTok(infp);
232 if (thetok != INTEGER_TOK) {
233 Error("%s, line %d: integer must follow identifier", hpfile,
234 linenum);
235 }
236 StoreSample(GetEntry(theident), nsamples, (floatish) theinteger);
237 GetHpTok(infp);
238 break;
239
240 case EOF_TOK:
241 endfile = 1;
242 break;
243
244 default:
245 Error("%s, line %d: %s unexpected", hpfile, linenum,
246 TokenToString(thetok));
247 break;
248 }
249}
250
251
252char *
253TokenToString(t)
254 token t;
255{
256 switch (t) {
257 case EOF_TOK: return "EOF";
258 case INTEGER_TOK: return "integer";
259 case FLOAT_TOK: return "floating point number";
260 case IDENTIFIER_TOK: return "identifier";
261 case STRING_TOK: return "string";
262 case BEGIN_SAMPLE_TOK: return "BEGIN_SAMPLE";
263 case END_SAMPLE_TOK: return "END_SAMPLE";
264 case JOB_TOK: return "JOB";
265 case DATE_TOK: return "DATE";
266 case SAMPLE_UNIT_TOK: return "SAMPLE_UNIT";
267 case VALUE_UNIT_TOK: return "VALUE_UNIT";
268 case MARK_TOK: return "MARK";
269
270 case X_RANGE_TOK: return "X_RANGE";
271 case Y_RANGE_TOK: return "Y_RANGE";
272 case ORDER_TOK: return "ORDER";
273 case SHADE_TOK: return "SHADE";
274 default: return "(strange token)";
275 }
276}
277
278/*
279 * Read the next token from the input and assign its value
280 * to the global variable "thetok". In the case of numbers,
281 * the corresponding value is also assigned to "theinteger"
282 * or "thefloatish" as appropriate; in the case of identifiers
283 * it is assigned to "theident".
284 */
285
286static void
287GetHpTok(infp)
288 FILE* infp;
289{
290
291 while (isspace(g_ch)) { /* skip whitespace */
292 if (g_ch == '\n') linenum++;
293 g_ch = getc(infp);
294 }
295
296 if (g_ch == EOF) {
297 thetok = EOF_TOK;
298 return;
299 }
300
301 if (isdigit(g_ch)) {
302 thetok = GetNumber(infp);
303 return;
304 } else if (g_ch == '\"') {
305 GetString(infp);
306 thetok = STRING_TOK;
307 return;
308 } else if (IsIdChar(g_ch)) {
309 ASSERT(! (isdigit(g_ch))); /* g_ch can't be a digit here */
310 GetIdent(infp);
311 if (!isupper(theident[0])) {
312 thetok = IDENTIFIER_TOK;
313 } else if (strcmp(theident, "BEGIN_SAMPLE") == 0) {
314 thetok = BEGIN_SAMPLE_TOK;
315 } else if (strcmp(theident, "END_SAMPLE") == 0) {
316 thetok = END_SAMPLE_TOK;
317 } else if (strcmp(theident, "JOB") == 0) {
318 thetok = JOB_TOK;
319 } else if (strcmp(theident, "DATE") == 0) {
320 thetok = DATE_TOK;
321 } else if (strcmp(theident, "SAMPLE_UNIT") == 0) {
322 thetok = SAMPLE_UNIT_TOK;
323 } else if (strcmp(theident, "VALUE_UNIT") == 0) {
324 thetok = VALUE_UNIT_TOK;
325 } else if (strcmp(theident, "MARK") == 0) {
326 thetok = MARK_TOK;
327 } else {
328 thetok = IDENTIFIER_TOK;
329 }
330 return;
331 } else {
332 Error("%s, line %d: strange character (%c)", hpfile, linenum, g_ch);
333 }
334}
335
336
337/*
338 * Read a sequence of digits and convert the result to an integer
339 * or floating point value (assigned to the "theinteger" or
340 * "thefloatish").
341 */
342
njna2887842005-05-25 21:26:35 +0000343static char numberstring[ NUMBER_LENGTH ];
nethercotec9f36922004-02-14 16:40:02 +0000344
345token
346GetNumber(infp)
347 FILE* infp;
348{
349 int i;
350 int containsdot;
351
352 ASSERT(isdigit(ch)); /* we must have a digit to start with */
353
354 containsdot = 0;
355
356 for (i = 0; i < NUMBER_LENGTH && (isdigit(g_ch) || g_ch == '.'); i++) {
357 numberstring[ i ] = g_ch;
358 containsdot |= (g_ch == '.');
359 g_ch = getc(infp);
360 }
361
362 ASSERT(i < NUMBER_LENGTH); /* did not overflow */
363
364 numberstring[ i ] = '\0';
365
366 if (containsdot) {
367 thefloatish = (floatish) atof(numberstring);
368 return FLOAT_TOK;
369 } else {
370 theinteger = atoi(numberstring);
371 return INTEGER_TOK;
372 }
373}
374
375/*
376 * Read a sequence of identifier characters and assign the result
377 * to the string "theident".
378 */
379
380void
381GetIdent(infp)
382 FILE *infp;
383{
384 unsigned int i;
385 char idbuffer[5000];
386
387 for (i = 0; i < (sizeof idbuffer)-1 && IsIdChar(g_ch); i++) {
388 idbuffer[ i ] = g_ch;
389 g_ch = getc(infp);
390 }
391
392 idbuffer[ i ] = '\0';
393
394 if (theident)
395 free(theident);
396
397 theident = copystring(idbuffer);
398}
399
400
401/*
402 * Read a sequence of characters that make up a string and
403 * assign the result to "thestring".
404 */
405
406void
407GetString(infp)
408 FILE *infp;
409{
410 unsigned int i;
411 char stringbuffer[5000];
412
413 ASSERT(ch == '\"');
414
415 g_ch = getc(infp); /* skip the '\"' that begins the string */
416
417 for (i = 0; i < (sizeof stringbuffer)-1 && g_ch != '\"'; i++) {
418 stringbuffer[ i ] = g_ch;
419 g_ch = getc(infp);
420 }
421
422 stringbuffer[i] = '\0';
423 thestring = copystring(stringbuffer);
424
425 ASSERT(g_ch == '\"');
426
427 g_ch = getc(infp); /* skip the '\"' that terminates the string */
428}
429
430boolish
431IsIdChar(ch)
432 int ch;
433{
434 return (!isspace(ch));
435}
436
437
438/*
439 * The information associated with each identifier is stored
440 * in a linked list of chunks. The table below allows the list
441 * of chunks to be retrieved given an identifier name.
442 */
443
444#define N_HASH 513
445
446static struct entry* hashtable[ N_HASH ];
447
448static intish
449Hash(s)
450 char *s;
451{
452 int r;
453
454 for (r = 0; *s; s++) {
455 r = r + r + r + *s;
456 }
457
458 if (r < 0) r = -r;
459
460 return r % N_HASH;
461}
462
463/*
464 * Get space for a new chunk. Initialise it, and return a pointer
465 * to the new chunk.
466 */
467
468static struct chunk*
469MakeChunk()
470{
471 struct chunk* ch;
472 struct datapoint* d;
473
474 ch = (struct chunk*) xmalloc( sizeof(struct chunk) );
475
476 d = (struct datapoint*) xmalloc (sizeof(struct datapoint) * N_CHUNK);
477
478 ch->nd = 0;
479 ch->d = d;
480 ch->next = 0;
481 return ch;
482}
483
484
485/*
486 * Get space for a new entry. Initialise it, and return a pointer
487 * to the new entry.
488 */
489
490struct entry *
491MakeEntry(name)
492 char *name;
493{
494 struct entry* e;
495
496 e = (struct entry *) xmalloc(sizeof(struct entry));
497 e->chk = MakeChunk();
498 e->name = copystring(name);
499 return e;
500}
501
502/*
503 * Get the entry associated with "name", creating a new entry if
504 * necessary.
505 */
506
507static struct entry *
508GetEntry(name)
509 char* name;
510{
511 intish h;
512 struct entry* e;
513
514 h = Hash(name);
515
516 for (e = hashtable[ h ]; e; e = e->next) {
517 if (strcmp(e->name, name) == 0) {
518 break;
519 }
520 }
521
522 if (e) {
523 return (e);
524 } else {
525 nidents++;
526 e = MakeEntry(name);
527 e->next = hashtable[ h ];
528 hashtable[ h ] = e;
529 return (e);
530 }
531}
532
533
534/*
535 * Store information from a sample.
536 */
537
538void
539StoreSample(en, bucket, value)
540 struct entry* en; intish bucket; floatish value;
541{
542 struct chunk* chk;
543
544 for (chk = en->chk; chk->next != 0; chk = chk->next)
545 ;
546
547 if (chk->nd < N_CHUNK) {
548 chk->d[ chk->nd ].bucket = bucket;
549 chk->d[ chk->nd ].value = value;
550 chk->nd += 1;
551 } else {
552 struct chunk* t;
553 t = chk->next = MakeChunk();
554 t->d[ 0 ].bucket = bucket;
555 t->d[ 0 ].value = value;
556 t->nd += 1;
557 }
558}
559
560
561struct entry** identtable;
562
563/*
564 * The hash table is useful while reading the input, but it
565 * becomes a liability thereafter. The code below converts
566 * it to a more easily processed table.
567 */
568
569static void
570MakeIdentTable()
571{
572 intish i;
573 intish j;
574 struct entry* e;
575
576 nidents = 0;
577 for (i = 0; i < N_HASH; i++) {
578 for (e = hashtable[ i ]; e; e = e->next) {
579 nidents++;
580 }
581 }
582
583 identtable = (struct entry**) xmalloc(nidents * sizeof(struct entry*));
584 j = 0;
585
586 for (i = 0; i < N_HASH; i++) {
587 for (e = hashtable[ i ]; e; e = e->next, j++) {
588 identtable[ j ] = e;
589 }
590 }
591}