* configure.ac: New switch for disabling -O2 (--disable-optimizations).
[cacao.git] / contrib / vmlog / vmlogdump.c
1 /* vmlog - high-speed logging for free VMs                  */
2 /* Copyright (C) 2006 Edwin Steiner <edwin.steiner@gmx.net> */
3
4 /* This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19 #include "vmlog.h"
20 #include <string.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <stdarg.h>
24 #include <assert.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <getopt.h>
29 #include <sys/stat.h>
30 #include <sys/mman.h>
31
32 #define VMLOGDUMP_RINGBUF_SIZE 1024*16
33 #define VMLOGDUMP_PREFETCH 1024
34
35 /*** options *********************************************************/
36
37 char *opt_prefix;
38 int opt_threadidx = 0;
39 int opt_dumpstrings = 0;
40 int opt_countcalls = 0;
41 int opt_show_zero_counts = 0;
42 int opt_open_frames = 0;
43 int opt_prefix_open_frames = 0;
44 int opt_show_depth = 1;
45 int opt_show_seqns = 0;
46 int opt_indent = 4;
47 int opt_reldepth = 0;
48 int opt_strip_desc = 0;
49 int opt_maxdepth = INT_MAX;
50
51 char *opt_tag = NULL;
52 char *opt_string = NULL;
53 vmlog_log_entry opt_needle;
54
55 vmlog_seq_t opt_range_start = 0;
56 vmlog_seq_t opt_range_end = VMLOG_SEQ_MAX;
57
58 /*** global variables ************************************************/
59
60 char *g_idxfname;
61 char *g_strfname;
62 char *g_logfname;
63 int g_logfd;
64 int g_idxlen;
65 int g_strlen;
66 int g_nstrings;
67 char *g_indentation;
68
69 vmlog_string_entry *g_idxmap;
70 char *g_strmap;
71
72 vmlog_ringbuf *g_ringbuf = NULL;
73
74 int *g_counters = NULL;
75
76 /*** usage ***********************************************************/
77
78 const char *usage =
79 "Usage: vmlogdump [options] prefix\n"
80 "\n"
81 "    Options:\n"
82 "        -c      count call frequency of methods\n"
83 "        -D      don't show depth numbers\n"
84 "           -z   show methods with zero call count\n"
85 "        -gT:S   select entries with given Tag and String\n"
86 "        -h      display this help message\n"
87 "        -iNUM   indentation width\n"
88 "        -lNUM   limit max depth to NUM\n"
89 "        -n      show sequence numbers / string indices\n"
90 "        -o      show open frames\n"
91 "        -O      show open frames before dump\n"
92 "        -P      strip method parameter and return types\n"
93 "        -rN:M   limit output to a range of sequence numbers\n"
94 "        -R      show depth relative to minimum depth in range\n"
95 "        -s      dump strings\n"
96 "        -tNUM   use log of thread NUM (default=0)\n"
97 "\n";
98
99 /*********************************************************************/
100
101 static void parse_tag_and_string(const char *arg)
102 {
103         const char *sep;
104         int len;
105
106         sep = strchr(arg,':');
107         if (!sep)
108                 vmlog_die_usage(usage,1);
109
110         len = sep - arg;
111         VMLOG_XZNEW_ARRAY(opt_tag,char,len+1);
112         memcpy(opt_tag,arg,len);
113
114         opt_needle.tag = vmlog_tag_from_name(opt_tag,len);
115         if (opt_needle.tag == -1) {
116                 vmlog_die("unknown tag identifier: %s",opt_tag);
117         }
118         
119         len = strlen(arg) - len - 1;
120         VMLOG_XZNEW_ARRAY(opt_string,char,len+1);
121         memcpy(opt_string,sep+1,len);
122 }
123
124 void parse_command_line(int argc,char **argv)
125 {
126         int r;
127
128         while (1) {
129                 r = getopt(argc,argv,"cDg:hi:l:noOPr:Rst:z");
130                 if (r == -1)
131                         break;
132                 switch (r) {
133                         case 'c':
134                                 opt_countcalls = 1;
135                                 break;
136
137                         case 'D':
138                                 opt_show_depth = 0;
139                                 break;
140
141                         case 'g':
142                                 parse_tag_and_string(optarg);
143                                 break;
144
145                         case 'h':
146                                 vmlog_die_usage(usage,0);
147
148                         case 'i':
149                                 opt_indent = atoi(optarg);
150                                 if (opt_indent < 0)
151                                         vmlog_die_usage(usage,1);
152                                 break;
153
154                         case 'l':
155                                 opt_maxdepth = atoi(optarg);
156                                 if (opt_maxdepth < 0)
157                                         vmlog_die_usage(usage,1);
158                                 break;
159
160                         case 'n':
161                                 opt_show_seqns = 1;
162                                 break;
163
164                         case 'o':
165                                 opt_open_frames = 1;
166                                 break;
167
168                         case 'O':
169                                 opt_prefix_open_frames = 1;
170                                 break;
171
172                         case 'P':
173                                 opt_strip_desc = 1;
174                                 break;
175
176                         case 'r':
177                                 if (!vmlog_opt_parse_range(optarg,&opt_range_start,&opt_range_end))
178                                         vmlog_die("invalid range: %s",optarg);
179                                 break;
180
181                         case 'R':
182                                 opt_reldepth = 1;
183                                 break;
184                                 
185                         case 's':
186                                 opt_dumpstrings = 1;
187                                 break;
188                                 
189                         case 't':
190                                 opt_threadidx = atoi(optarg);
191                                 break;
192
193                         case 'z':
194                                 opt_show_zero_counts = 1;
195                                 break;
196                                 
197                         case '?': vmlog_die_usage(usage,1);
198                 }
199         }
200
201         if (argc - optind < 1)
202                 vmlog_die_usage(usage,1);
203         
204         opt_prefix = argv[optind++];
205
206         g_indentation = VMLOG_NEW_ARRAY(char,opt_indent+1);
207         memset(g_indentation,' ',opt_indent);
208         g_indentation[opt_indent] = 0;
209 }
210
211 static void fprint_string(FILE *file,int index)
212 {
213         char *str;
214         vmlog_string_entry *strent;
215         char *buf;
216         int len;
217
218         strent = g_idxmap + index;
219         str = g_strmap + strent->ofs;
220         len = strent->len;
221
222         if (opt_strip_desc) {
223                 char *desc;
224
225                 desc = (char*) memchr(str,'(',len);
226                 if (desc)
227                         len = desc - str;
228         }
229
230         buf = VMLOG_NEW_ARRAY(char,len+1);
231
232         memcpy(buf,str,len);
233         buf[len] = 0;
234
235         fputs(buf,file);
236         
237         VMLOG_FREE_ARRAY(char,len+1,buf);
238 }
239
240 static void dump_log_entry(vmlog_log_entry *logent,int depth,vmlog_seq_t seq)
241 {
242         int i;
243
244         if (opt_show_depth) {
245                 if (opt_reldepth)
246                         fputc('R',stdout);
247                 fprintf(stdout,"%d",depth);
248                 fputc(':',stdout);
249                 if (depth < 100) {
250                         fputc(' ',stdout);
251                         if (depth < 10)
252                                 fputc(' ',stdout);
253                 }
254         }
255         if (opt_show_seqns) {
256                 fprintf(stdout,"(" VMLOG_SEQ_FMT_W ")",seq);
257         }
258         for (i=0; i<depth; ++i)
259                 fputs(g_indentation,stdout);
260         switch(logent->tag) {
261                 case VMLOG_TAG_ENTER: fputs("enter ",stdout); break;
262                 case VMLOG_TAG_LEAVE: fputs("leave ",stdout); break;
263                 case VMLOG_TAG_THROW: fputs("throw ",stdout); break;
264                 case VMLOG_TAG_CATCH: fputs("catch ",stdout); break;
265                 case VMLOG_TAG_UNWND: fputs("unwnd ",stdout); break;
266                 case VMLOG_TAG_SIGNL: fputs("signl ",stdout); break;
267                 case VMLOG_TAG_UNROL: fputs("unrol ",stdout); break;
268                 case VMLOG_TAG_REROL: fputs("rerol ",stdout); break;
269                 default: fputs("<UNKNOWN TAG> ",stdout);
270         }
271         fprint_string(stdout,logent->index);
272         fputs("\n",stdout);
273 }
274
275 static void dump_log_entries(void)
276 {
277         vmlog_log_entry *logent;
278         int depth = 0;
279         int mindepth = INT_MAX;
280         vmlog_seq_t seq;
281
282         if (opt_reldepth) {
283                 vmlog_ringbuf_seek(g_ringbuf,opt_range_start);
284                 while ((g_ringbuf->seq <= opt_range_end)
285                         && (logent = vmlog_ringbuf_next(g_ringbuf,VMLOGDUMP_PREFETCH)))
286                 {
287                         int depthchange = vmlog_tag_definitions[logent->tag].depth;
288
289                         depth += depthchange;
290                         if (depthchange < 0) {
291                                 if (depth < mindepth)
292                                         mindepth = depth;
293                         }
294                 }
295
296                 vmlog_ringbuf_seek(g_ringbuf,opt_range_start);
297                 if (mindepth != INT_MAX)
298                         depth = -mindepth;
299                 else
300                         depth = 0;
301         }
302
303         while ((g_ringbuf->seq <= opt_range_end)
304                 && (logent = vmlog_ringbuf_next(g_ringbuf,VMLOGDUMP_PREFETCH)))
305         {
306                 int depthchange = vmlog_tag_definitions[logent->tag].depth;
307
308                 if (depthchange < 0) {
309                         depth += depthchange;
310                         if (depth < mindepth)
311                                 mindepth = depth;
312                 }
313
314                 seq = g_ringbuf->seq - 1;
315
316                 if ((!opt_string || (logent->tag == opt_needle.tag && logent->index == opt_needle.index))
317                         && (seq >= opt_range_start)
318                         && (depth <= opt_maxdepth))
319                 {
320                         dump_log_entry(logent,depth,seq);
321                 }
322
323                 if (depthchange > 0)
324                         depth += depthchange;
325         }
326 }
327
328 static void dump_strings(void)
329 {
330         int i;
331
332         for (i=0; i<g_nstrings; ++i) {
333                 if (opt_show_seqns) {
334                         fprintf(stdout,"%8d: ",i);
335                 }
336                 fprint_string(stdout,i);
337                 fputc('\n',stdout);
338         }
339 }
340
341 static int compare_by_counter(const void *a,const void *b)
342 {
343         return g_counters[*(int*)b] - g_counters[*(int*)a];
344 }
345
346 static void count_calls(void)
347 {
348         int *indices;
349         int i;
350         int index;
351         vmlog_log_entry *logent;
352
353         g_counters = VMLOG_NEW_ARRAY(int,g_nstrings);
354         assert(g_counters);
355         memset(g_counters,0,sizeof(int)*g_nstrings);
356
357         while (1) {
358                 logent = vmlog_ringbuf_next(g_ringbuf,VMLOGDUMP_PREFETCH);
359                 if (!logent)
360                         break;
361
362                 if (logent->tag == VMLOG_TAG_ENTER) {
363                         g_counters[logent->index]++;
364                 }
365         }
366
367         indices = VMLOG_NEW_ARRAY(int,g_nstrings);
368         assert(indices);
369         for (i=0; i<g_nstrings; ++i)
370                 indices[i] = i;
371
372         qsort(indices,g_nstrings,sizeof(int),compare_by_counter);
373
374         for (i=0; i<g_nstrings; ++i) {
375                 index = indices[i];
376                 if (!g_counters[index] && !opt_show_zero_counts)
377                         break;
378                 fprintf(stdout,"%12d ",g_counters[index]);
379                 fprint_string(stdout,index);
380                 fputc('\n',stdout);
381         }
382         
383         VMLOG_FREE_ARRAY(int,g_nstrings,indices);
384         VMLOG_FREE_ARRAY(int,g_nstrings,g_counters);
385 }
386
387 static void dump_open_frames(void)
388 {
389         vmlog_thread_log *tlog;
390         vmlog_log_entry *logent;
391         vmlog_frame *frame;
392         vmlog_seq_t start;
393         vmlog_seq_t end;
394         int i;
395         int maxdepth = 0;
396
397         tlog = vmlog_thread_log_new(NULL,NULL,0);
398
399         if (opt_prefix_open_frames) {
400                 start = 0;
401                 end = opt_range_start - 1;
402         }
403         else {
404                 start = opt_range_start;
405                 end = opt_range_end;
406         }
407
408         if (start > 0)
409                 vmlog_ringbuf_seek(g_ringbuf,start);
410
411         while (g_ringbuf->seq <= end 
412                 && (logent = vmlog_ringbuf_next(g_ringbuf,VMLOGDUMP_PREFETCH)))
413         {
414                 int depthchange = vmlog_tag_definitions[logent->tag].depth;
415                 if (depthchange < 0) {
416                         if (!vmlog_thread_log_leave(tlog,logent->index,g_ringbuf->seq - 1))
417                                 vmlog_warn("unmatched leave");
418                 }
419                 else if (depthchange > 0) {
420                         vmlog_thread_log_enter(tlog,logent->index,g_ringbuf->seq - 1);
421                         if (tlog->depth > maxdepth)
422                                 maxdepth = tlog->depth;
423                 }
424         }
425
426         if (tlog->depth < 0) {
427                 vmlog_warn("thread ends with negative call frame depth: %i",tlog->depth);
428                 vmlog_warn("frame buffer will be dumped, expect bogus results:");
429                 tlog->depth = tlog->framescap;
430                 if (tlog->depth > maxdepth)
431                         tlog->depth = maxdepth;
432         }
433
434         for (i=0; i<tlog->depth; ++i) {
435                 frame = tlog->frames + i;
436                 fprintf(stdout,"%3d: (" VMLOG_SEQ_FMT_W ") ",i,frame->seq);
437                 fprint_string(stdout,frame->index);
438                 fputc('\n',stdout);
439         }
440
441         vmlog_thread_log_free(tlog);
442 }
443
444 int main(int argc,char **argv)
445 {
446         char buf[20];
447         int need_logfile = 1;
448         int need_vml = 0;
449         vmlog_log *vml;
450         
451         if (argc)
452                 vmlog_set_progname(argv[0]);
453         parse_command_line(argc,argv);
454
455         sprintf(buf,"%d",opt_threadidx);
456         g_idxfname = vmlog_concat3(opt_prefix,"",".idx",NULL);
457         g_strfname = vmlog_concat3(opt_prefix,"",".str",NULL);
458         g_logfname = vmlog_concat4len(opt_prefix,strlen(opt_prefix),
459                         ".",1,buf,strlen(buf),".log",4,NULL);
460
461         if (opt_dumpstrings)
462                 need_logfile = 0;
463
464         if (opt_string)
465                 need_vml = 1;
466
467         if (need_logfile) {
468                 g_ringbuf = vmlog_ringbuf_new(g_logfname,VMLOGDUMP_RINGBUF_SIZE);
469         }
470
471         g_idxmap = (vmlog_string_entry *) vmlog_file_mmap(g_idxfname,&g_idxlen);
472         g_nstrings = g_idxlen / sizeof(vmlog_string_entry);
473         g_strmap = (char *) vmlog_file_mmap(g_strfname,&g_strlen);
474
475         if (need_vml) {
476                 vml = vmlog_log_new(NULL,0);
477                 vmlog_load_stringhash(vml,opt_prefix);
478                 if (opt_string) {
479                         opt_needle.index = vmlog_get_string_index(vml,opt_string,strlen(opt_string));
480                         fprintf(stdout,"needle '%s:%s' tag=%d index=%d\n",
481                                         opt_tag,opt_string,opt_needle.tag,opt_needle.index);
482                 }
483         }
484         
485         if (opt_countcalls) {
486                 count_calls();
487         }
488         else if (opt_dumpstrings) {
489                 dump_strings();
490         }
491         else if (opt_open_frames) {
492                 dump_open_frames();
493         }
494         else {
495                 if (opt_prefix_open_frames) {
496                         dump_open_frames();
497                         fputs("---\n",stdout);
498                 }
499                 dump_log_entries();
500         }
501
502         vmlog_ringbuf_free(g_ringbuf);
503         g_ringbuf = NULL;
504         munmap(g_strmap,g_strlen);
505         munmap(g_idxmap,g_idxlen);
506
507         return 0;
508 }
509
510 /* vim: noet ts=8 sw=8
511  */
512