Line data Source code
1 : //
2 : // mhdump: midas history explorer
3 : //
4 : // Author: Konstantin Olchanski, 20-NOV-2007
5 : //
6 : // Compile:
7 : // g++ -o mhdump.o -c mhdump.cxx -O2 -g -Wall -Wuninitialized
8 : // g++ -o mhdump -g -O2 mhdump.o
9 : //
10 : // $Id$
11 : //
12 :
13 : #undef NDEBUG // midas required assert() to be always enabled
14 :
15 : #include <stdio.h>
16 : #include <stdlib.h>
17 : #include <stdint.h>
18 : #include <string.h>
19 : #include <assert.h>
20 : #include <errno.h>
21 :
22 : #include <string>
23 : #include <map>
24 : #include <vector>
25 : #include <ctime>
26 :
27 : ////////////////////////////////////////
28 : // Definitions extracted from midas.h //
29 : ////////////////////////////////////////
30 :
31 : #define DWORD uint32_t
32 :
33 : #define NAME_LENGTH 32 /**< length of names, mult.of 8! */
34 :
35 : typedef struct {
36 : DWORD record_type; /* RT_DATA or RT_DEF */
37 : DWORD event_id;
38 : DWORD time;
39 : DWORD def_offset; /* place of definition */
40 : DWORD data_size; /* data following this header in bytes */
41 : } HIST_RECORD;
42 :
43 : typedef struct {
44 : char name[NAME_LENGTH]; /**< - */
45 : DWORD type; /**< - */
46 : DWORD n_data; /**< - */
47 : } TAG;
48 :
49 : ////////////////////////////////////////
50 : // Definitions extracted from midas.c //
51 : ////////////////////////////////////////
52 :
53 : /********************************************************************/
54 : /* data type sizes */
55 : int tid_size[] = {
56 : 0, /* tid == 0 not defined */
57 : 1, /* TID_BYTE unsigned byte 0 255 */
58 : 1, /* TID_SBYTE signed byte -128 127 */
59 : 1, /* TID_CHAR single character 0 255 */
60 : 2, /* TID_WORD two bytes 0 65535 */
61 : 2, /* TID_SHORT signed word -32768 32767 */
62 : 4, /* TID_DWORD four bytes 0 2^32-1 */
63 : 4, /* TID_INT signed dword -2^31 2^31-1 */
64 : 4, /* TID_BOOL four bytes bool 0 1 */
65 : 4, /* TID_FLOAT 4 Byte float format */
66 : 8, /* TID_DOUBLE 8 Byte float format */
67 : 1, /* TID_BITFIELD 8 Bits Bitfield 00000000 11111111 */
68 : 0, /* TID_STRING zero terminated string */
69 : 0, /* TID_ARRAY variable length array of unkown type */
70 : 0, /* TID_STRUCT C structure */
71 : 0, /* TID_KEY key in online database */
72 : 0, /* TID_LINK link in online database */
73 : 8, /* TID_INT64 8 bytes int -2^63 2^63-1 */
74 : 8 /* TID_UINT64 8 bytes unsigned int 0 2^64-1 */
75 : };
76 :
77 : /* data type names */
78 : const char *tid_name[] = {
79 : "NULL",
80 : "BYTE",
81 : "SBYTE",
82 : "CHAR",
83 : "UINT16",
84 : "INT16",
85 : "UINT32",
86 : "INT32",
87 : "BOOL",
88 : "FLOAT",
89 : "DOUBLE",
90 : "BITFIELD",
91 : "STRING",
92 : "ARRAY",
93 : "STRUCT",
94 : "KEY",
95 : "LINK",
96 : "INT64",
97 : "UINT64"
98 : };
99 :
100 : ////////////////////////////////////////
101 :
102 : struct Tag
103 : {
104 : int event_id;
105 : std::string name;
106 : int offset;
107 : int arraySize;
108 : int typeSize;
109 : int typeCode;
110 : };
111 :
112 : struct Event
113 : {
114 : bool printAllTags;
115 : int size;
116 : std::map<std::string,Tag*> tags;
117 : std::vector<std::string> tagNames;
118 : std::vector<int> tagIndexes;
119 : };
120 :
121 : std::map<int,Event*> gTags;
122 :
123 : bool doPrintTags = true;
124 : bool doPrintNames = true;
125 : bool doPrintData = true;
126 : bool doAll = false;
127 :
128 0 : int readHstFile(FILE*f)
129 : {
130 0 : bool doRead = true;
131 :
132 0 : assert(f!=NULL);
133 :
134 : while (1)
135 : {
136 : HIST_RECORD rec;
137 :
138 0 : if (doRead)
139 : {
140 0 : int rd = fread(&rec, sizeof(rec), 1, f);
141 0 : if (!rd)
142 0 : break;
143 : }
144 :
145 0 : doRead = true;
146 : #if 0
147 : printf("HIST_RECORD:\n");
148 : printf(" Record type: 0x%x\n", rec.record_type);
149 : printf(" Event ID: %d\n", rec.event_id);
150 : printf(" Time: %d\n", rec.time);
151 : printf(" Offset: 0x%x\n", rec.def_offset);
152 : printf(" Size: 0x%x\n", rec.data_size);
153 : #endif
154 :
155 0 : switch (rec.record_type)
156 : {
157 0 : default:
158 0 : printf("Unexpected record type: 0x%08x, trying to recover by skipping bad data.\n", rec.record_type);
159 : while (1)
160 : {
161 0 : int c = fgetc(f);
162 : //printf("read 0x%02x\n", c);
163 0 : if (c==EOF)
164 0 : return 0;
165 0 : if (c!=0x48)
166 0 : continue;
167 :
168 0 : c = fgetc(f);
169 0 : if (c==EOF)
170 0 : return 0;
171 0 : if (c!=0x53)
172 0 : continue;
173 :
174 0 : c = fgetc(f);
175 0 : if (c==EOF)
176 0 : return 0;
177 0 : if (c!=0x44)
178 0 : continue;
179 :
180 0 : printf("Maybe recovered - see what looks like valid history record header.\n");
181 :
182 0 : ((char*)(&rec))[0] = 0x48;
183 0 : ((char*)(&rec))[1] = 0x53;
184 0 : ((char*)(&rec))[2] = 0x44;
185 :
186 0 : int rd = fread(((char*)(&rec))+3, sizeof(rec)-3, 1, f);
187 0 : if (!rd)
188 0 : return 0;
189 :
190 0 : doRead = false;
191 0 : break;
192 0 : }
193 0 : break;
194 :
195 0 : case 0x46445348: // RT_DEF:
196 : {
197 : char event_name[NAME_LENGTH];
198 0 : int rd = fread(event_name, 1, NAME_LENGTH, f);
199 0 : assert(rd == NAME_LENGTH);
200 :
201 0 : int size = rec.data_size;
202 0 : int ntags = size/sizeof(TAG);
203 :
204 0 : if (doPrintTags)
205 0 : printf("Event %d, \"%s\", size %d, %d tags.\n", rec.event_id, event_name, size, ntags);
206 :
207 0 : assert(size > 0);
208 0 : assert(size < 1*1024*1024);
209 0 : assert(size == ntags*(int)sizeof(TAG));
210 :
211 0 : TAG *tags = new TAG[ntags];
212 0 : rd = fread(tags, 1, size, f);
213 0 : assert(rd == size);
214 :
215 0 : Event* e = gTags[rec.event_id];
216 0 : if (!e)
217 : {
218 0 : gTags[rec.event_id] = new Event;
219 0 : e = gTags[rec.event_id];
220 0 : e->printAllTags = false;
221 0 : if (doAll)
222 0 : e->printAllTags = true;
223 : }
224 : else
225 : {
226 0 : if (e->printAllTags) {
227 0 : e->tagNames.clear();
228 0 : e->tagIndexes.clear();
229 : }
230 : }
231 :
232 0 : e->size = 0;
233 :
234 0 : int offset = 0;
235 :
236 0 : for (int itag=0; itag<ntags; itag++)
237 : {
238 0 : int tsize = tid_size[tags[itag].type];
239 :
240 0 : if (tsize == 0)
241 0 : tsize = 1;
242 :
243 0 : int size = tags[itag].n_data * tsize;
244 :
245 0 : if (tags[itag].type == 12) { // TID_STRING
246 : //if (size == 1)
247 : //size = 32; // kludge old broken history files
248 0 : fprintf(stderr, "Error: Event %d, \"%s\", has a tag \"%s\" of type TID_STRING, which is forbidden and cannot be decoded, all data after this tag will be gibberish\n", rec.event_id, event_name, tags[itag].name);
249 0 : size = 0;
250 : }
251 :
252 0 : if (offset%tsize != 0)
253 0 : offset += tsize-offset%tsize;
254 :
255 0 : assert(offset%tsize == 0);
256 :
257 0 : Tag* t = new Tag;
258 0 : t->event_id = rec.event_id;
259 0 : t->name = tags[itag].name;
260 0 : t->offset = offset;
261 0 : t->arraySize = tags[itag].n_data;
262 0 : t->typeSize = tid_size[tags[itag].type];
263 0 : t->typeCode = tags[itag].type;
264 :
265 0 : e->tags[t->name] = t;
266 :
267 0 : if (e->printAllTags)
268 : {
269 0 : e->tagNames.push_back(t->name);
270 0 : e->tagIndexes.push_back(-1);
271 : }
272 :
273 0 : if (doPrintTags)
274 0 : printf(" Tag %d: \"%s\"[%d], type \"%s\" (%d), type size %d, offset %d+%d\n", itag, tags[itag].name, tags[itag].n_data, tid_name[tags[itag].type], tags[itag].type, tid_size[tags[itag].type], offset, size);
275 :
276 0 : offset += size;
277 : }
278 :
279 0 : e->size = offset;
280 :
281 : //if (doPrintTags)
282 : //printf(" Event size %d\n", e->size);
283 :
284 0 : delete[] tags;
285 0 : break;
286 : }
287 :
288 0 : case 0x41445348: // RT_DATA:
289 : {
290 0 : int size = rec.data_size;
291 :
292 : if (0)
293 : printf("Data record, size %d.\n", size);
294 :
295 0 : if (size <= 1 || size > 1*1024*1024)
296 : {
297 0 : fprintf(stderr, "Error: Invalid data record: event %d, size %d is invalid\n", rec.event_id, rec.data_size);
298 0 : continue;
299 : }
300 :
301 0 : char *buf = new char[size];
302 0 : int rd = fread(buf, 1, size, f);
303 0 : assert(rd == size);
304 :
305 0 : time_t t = (time_t)rec.time;
306 :
307 0 : Event* e = gTags[rec.event_id];
308 0 : if (e && doPrintData)
309 : {
310 0 : if (size != e->size)
311 : {
312 0 : fprintf(stderr, "Error: event %d, size mismatch should be %d, got %d bytes\n", rec.event_id, e->size, size);
313 : }
314 :
315 : //printf("event %d, time %s", rec.event_id, ctime(&t));
316 :
317 0 : int n = e->tagNames.size();
318 :
319 0 : if (n>0)
320 0 : printf("%d %d ", rec.event_id, rec.time);
321 :
322 0 : for (int i=0; i<n; i++)
323 : {
324 0 : Tag*t = e->tags[e->tagNames[i]];
325 0 : int index = e->tagIndexes[i];
326 :
327 : //printf(" dump %s[%d] (%p)\n", e->tagNames[i].c_str(), e->tagIndexes[i], t);
328 :
329 0 : if (t)
330 : {
331 0 : int offset = t->offset;
332 0 : void* ptr = (void*)(buf+offset);
333 :
334 0 : if (doPrintNames)
335 : {
336 0 : if (index < 0)
337 0 : printf(" %s=", t->name.c_str());
338 : else
339 0 : printf(" %s[%d]=", t->name.c_str(), index);
340 : }
341 :
342 0 : for (int j=0; j<t->arraySize; j++)
343 : {
344 0 : if (index >= 0)
345 0 : j = index;
346 :
347 0 : switch (t->typeCode)
348 : {
349 0 : default:
350 0 : printf("unknownType%d ",t->typeCode);
351 0 : break;
352 0 : case 1: /* BYTE */
353 0 : printf("%u ",((uint8_t*)ptr)[j]);
354 0 : break;
355 0 : case 2: /* SBYTE */
356 0 : printf("%d ",((int8_t*)ptr)[j]);
357 0 : break;
358 0 : case 3: /* CHAR */
359 0 : printf("\'%c\' ",((char*)ptr)[j]);
360 0 : break;
361 0 : case 4: /* WORD */
362 0 : printf("%u ",((uint16_t*)ptr)[j]);
363 0 : break;
364 0 : case 5: /* SHORT */
365 0 : printf("%d ",((int16_t*)ptr)[j]);
366 0 : break;
367 0 : case 6: /* DWORD */
368 0 : printf("%u ",((uint32_t*)ptr)[j]);
369 0 : break;
370 0 : case 7: /* INT */
371 0 : printf("%d ",((int32_t*)ptr)[j]);
372 0 : break;
373 0 : case 8: /* BOOL */
374 0 : printf("%u ",((uint32_t*)ptr)[j]);
375 0 : break;
376 0 : case 9: /* FLOAT */
377 0 : printf("%.8g ",((float*)ptr)[j]);
378 0 : break;
379 0 : case 10: /* DOUBLE */
380 0 : printf("%.16g ",((double*)ptr)[j]);
381 0 : break;
382 : //case 12: /* STRING */
383 : //printf("%s ",&((char*)ptr)[j]);
384 : //break;
385 : }
386 :
387 0 : if (index >= 0)
388 0 : break;
389 : }
390 : }
391 : }
392 :
393 0 : if (n>0)
394 0 : printf(" %s", ctime(&t));
395 : }
396 :
397 0 : delete[] buf;
398 0 : break;
399 : }
400 : }
401 0 : }
402 :
403 0 : return 0;
404 : }
405 :
406 0 : int readHst(const char* name)
407 : {
408 0 : FILE* f = fopen(name,"r");
409 0 : if (!f)
410 : {
411 0 : fprintf(stderr,"Error: Cannot open \'%s\', errno %d (%s)\n", name, errno, strerror(errno));
412 0 : exit(1);
413 : }
414 :
415 0 : readHstFile(f);
416 0 : fclose(f);
417 0 : return 0;
418 : }
419 :
420 0 : std::string readString(FILE* f)
421 : {
422 : char buf[1024];
423 0 : memset(buf, 0, sizeof(buf));
424 0 : const char* s = fgets(buf, sizeof(buf)-1, f);
425 0 : if (!s)
426 0 : return "";
427 : // NUL-teminated by memset(0)
428 0 : size_t len = strlen(buf);
429 0 : if (len > 0) {
430 : // remove trailing newline
431 0 : if (buf[len-1] == '\n')
432 0 : buf[len-1] = 0;
433 : }
434 0 : return buf;
435 : }
436 :
437 0 : std::string tagValue(const char* tag, const std::string& s)
438 : {
439 0 : size_t len = strlen(tag);
440 0 : if (strncmp(s.c_str(), tag, len) == 0) {
441 0 : const char* sptr = s.c_str() + len;
442 0 : while (sptr[0] == ' ')
443 0 : sptr++;
444 0 : return sptr;
445 : }
446 0 : return "";
447 : }
448 :
449 0 : int readMhfFileV2(const char* filename, FILE*f)
450 : {
451 0 : std::string event_name = tagValue("event_name:", readString(f));
452 0 : std::string time = tagValue("time:", readString(f));
453 0 : printf("event name: [%s], time [%s]\n", event_name.c_str(), time.c_str());
454 0 : std::string s;
455 : while (1) {
456 0 : s = readString(f);
457 0 : if (strncmp(s.c_str(), "tag:", 4) != 0)
458 0 : break;
459 0 : printf("tag: %s\n", s.c_str());
460 : }
461 0 : std::string s_record_size = tagValue("record_size:", s);
462 0 : std::string s_data_offset = tagValue("data_offset:", readString(f));
463 0 : size_t record_size = atoi(s_record_size.c_str());
464 0 : size_t data_offset = atoi(s_data_offset.c_str());
465 : //record_size += 4; // 4 bytes of timestamp
466 0 : printf("record size: %zu, data offset: %zu\n", record_size, data_offset);
467 0 : int status = fseek(f, data_offset, SEEK_SET);
468 0 : if (status != 0) {
469 0 : fprintf(stderr, "%s: cannot seek to %zu, fseek() returned %d, errno %d (%s)\n", filename, data_offset, status, errno, strerror(errno));
470 0 : return -1;
471 : }
472 0 : char buf[record_size];
473 0 : int count = 0;
474 0 : uint32_t last_time = atoi(time.c_str());
475 0 : while (!feof(f)) {
476 0 : size_t rd = fread(buf, 1, record_size, f);
477 : //printf("read %zu\n", rd);
478 0 : if (rd == 0) {
479 : // EOF
480 0 : break;
481 0 : } else if (rd != record_size) {
482 : // short read
483 0 : fprintf(stderr, "%s: short read at the end of file, last data record is truncated from %zu to %zu bytes\n", filename, record_size, rd);
484 0 : break;
485 : }
486 0 : const uint32_t t = *(uint32_t*)&buf[0];
487 0 : printf("record %d, time %lu, incr %lu\n", count, (long unsigned int)t, (long unsigned int)(t-last_time));
488 0 : count++;
489 0 : if (t == last_time) {
490 0 : printf("duplicate time %lu -> %lu\n", (long unsigned int)last_time, (long unsigned int)t);
491 0 : } else if (t < last_time) {
492 0 : printf("non-monotonic time %lu -> %lu\n", (long unsigned int)last_time, (long unsigned int)t);
493 : }
494 0 : last_time = t;
495 : }
496 0 : fprintf(stderr, "%s: read %d records\n", filename, count);
497 0 : return 0;
498 0 : }
499 :
500 0 : int readMhfFile(const char* filename, FILE*f)
501 : {
502 0 : std::string version = readString(f);
503 0 : if (version == "version: 2.0") {
504 0 : return readMhfFileV2(filename, f);
505 : }
506 :
507 0 : fprintf(stderr, "%s: unexpected file version: %s\n", filename, version.c_str());
508 0 : return -1;
509 0 : }
510 :
511 0 : int readMhf(const char* name)
512 : {
513 0 : FILE* f = fopen(name,"r");
514 0 : if (!f) {
515 0 : fprintf(stderr,"Error: Cannot open \'%s\', errno %d (%s)\n", name, errno, strerror(errno));
516 0 : exit(1);
517 : }
518 :
519 0 : readMhfFile(name, f);
520 0 : fclose(f);
521 0 : return 0;
522 : }
523 :
524 0 : void help()
525 : {
526 0 : fprintf(stderr,"Usage: mhdump [-h] [-L] [-n] [-t] [-E event_id] [-T tag_name] file1.hst file2.hst ...\n");
527 0 : fprintf(stderr,"Usage: mhdump [-L] [-n] [-t] [-T tag_name] mhf_file1.dat mhf_file2.dat ...\n");
528 0 : fprintf(stderr,"\n");
529 0 : fprintf(stderr,"Switches:\n");
530 0 : fprintf(stderr," -h --- print this help message\n");
531 0 : fprintf(stderr," -L --- list tag definitions only\n");
532 0 : fprintf(stderr," -t --- omit tag definitions\n");
533 0 : fprintf(stderr," -n --- omit variable names\n");
534 0 : fprintf(stderr,"\n");
535 0 : fprintf(stderr,"Examples:\n");
536 0 : fprintf(stderr," To list all existing tags: mhdump -L file1.hst file2.hst ...\n");
537 0 : fprintf(stderr," To show data for all events, all tags: mhdump file1.hst file2.hst ...\n");
538 0 : fprintf(stderr," To show all data for event 0: mhdump -E 0 file1.hst file2.hst ...\n");
539 0 : fprintf(stderr," To show data for event 0, tag \"State\": mhdump -n -E 0 -T State file1.hst file2.hst ...\n");
540 0 : fprintf(stderr," To show data for event 3, tag \"MCRT\", array index 5: mhdump -n -E 3 -T MCRT[5] file1.hst file2.hst ...\n");
541 0 : exit(1);
542 : }
543 :
544 0 : int main(int argc,char*argv[])
545 : {
546 0 : int event_id = -1;
547 :
548 0 : if (argc <= 1)
549 0 : help(); // DOES NOT RETURN
550 :
551 0 : for (int iarg=1; iarg<argc; iarg++)
552 0 : if (strcmp(argv[iarg], "-h")==0)
553 : {
554 0 : help(); // DOES NOT RETURN
555 : }
556 0 : else if (strcmp(argv[iarg], "-E")==0)
557 : {
558 0 : iarg++;
559 0 : event_id = atoi(argv[iarg]);
560 0 : if (!gTags[event_id])
561 0 : gTags[event_id] = new Event;
562 0 : gTags[event_id]->printAllTags = true;
563 : }
564 0 : else if (strcmp(argv[iarg], "-T")==0)
565 : {
566 0 : iarg++;
567 0 : char *s = strchr(argv[iarg],'[');
568 0 : int index = -1;
569 0 : if (s)
570 : {
571 0 : index = atoi(s+1);
572 0 : *s = 0;
573 : }
574 :
575 0 : std::string name = argv[iarg];
576 :
577 0 : Event* e = gTags[event_id];
578 :
579 0 : if ((event_id<0) || !e)
580 : {
581 0 : fprintf(stderr,"Error: expected \"-E event_id\" before \"-T ...\"\n");
582 0 : exit(1);
583 : }
584 :
585 0 : e->printAllTags = false;
586 0 : e->tagNames.push_back(name);
587 0 : e->tagIndexes.push_back(index);
588 0 : }
589 0 : else if (strcmp(argv[iarg], "-t")==0)
590 0 : doPrintTags = false;
591 0 : else if (strcmp(argv[iarg], "-L")==0)
592 : {
593 0 : doPrintTags = true;
594 0 : doPrintData = false;
595 : }
596 0 : else if (strcmp(argv[iarg], "-A")==0)
597 0 : doAll = true;
598 0 : else if (strcmp(argv[iarg], "-n")==0)
599 0 : doPrintNames = false;
600 0 : else if (strncmp(argv[iarg], "mhf_", 4) == 0 || strstr(argv[iarg], "/mhf_")) {
601 : // read mhf_xxx.dat files
602 0 : if (gTags.size() == 0)
603 0 : doAll = true;
604 0 : readMhf(argv[iarg]);
605 : } else {
606 : // read xxx.hst files
607 0 : if (gTags.size() == 0)
608 0 : doAll = true;
609 0 : readHst(argv[iarg]);
610 : }
611 :
612 : // make leak sanitizer happy, delete everything we allocated.
613 0 : for (auto& ei: gTags) {
614 0 : if (ei.second) {
615 0 : for (auto& ti: ei.second->tags) {
616 0 : if (ti.second) {
617 0 : delete ti.second;
618 : }
619 : }
620 0 : ei.second->tags.clear();
621 0 : delete ei.second;
622 : }
623 : }
624 0 : gTags.clear();
625 :
626 0 : return 0;
627 : }
628 :
629 : /* emacs
630 : * Local Variables:
631 : * tab-width: 8
632 : * c-basic-offset: 3
633 : * indent-tabs-mode: nil
634 : * End:
635 : */
|