1 /* vmlog - high-speed logging for free VMs */
2 /* Copyright (C) 2006 Edwin Steiner <edwin.steiner@gmx.net> */
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.
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.
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
32 #define VMLOGDUMP_RINGBUF_SIZE 1024*16
33 #define VMLOGDUMP_PREFETCH 1024
35 /*** options *********************************************************/
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;
48 int opt_strip_desc = 0;
49 int opt_maxdepth = INT_MAX;
52 char *opt_string = NULL;
53 vmlog_log_entry opt_needle;
55 vmlog_seq_t opt_range_start = 0;
56 vmlog_seq_t opt_range_end = VMLOG_SEQ_MAX;
58 /*** global variables ************************************************/
69 vmlog_string_entry *g_idxmap;
72 vmlog_ringbuf *g_ringbuf = NULL;
74 int *g_counters = NULL;
76 /*** usage ***********************************************************/
79 "Usage: vmlogdump [options] prefix\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"
96 " -tNUM use log of thread NUM (default=0)\n"
99 /*********************************************************************/
101 static void parse_tag_and_string(const char *arg)
106 sep = strchr(arg,':');
108 vmlog_die_usage(usage,1);
111 VMLOG_XZNEW_ARRAY(opt_tag,char,len+1);
112 memcpy(opt_tag,arg,len);
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);
119 len = strlen(arg) - len - 1;
120 VMLOG_XZNEW_ARRAY(opt_string,char,len+1);
121 memcpy(opt_string,sep+1,len);
124 void parse_command_line(int argc,char **argv)
129 r = getopt(argc,argv,"cDg:hi:l:noOPr:Rst:z");
142 parse_tag_and_string(optarg);
146 vmlog_die_usage(usage,0);
149 opt_indent = atoi(optarg);
151 vmlog_die_usage(usage,1);
155 opt_maxdepth = atoi(optarg);
156 if (opt_maxdepth < 0)
157 vmlog_die_usage(usage,1);
169 opt_prefix_open_frames = 1;
177 if (!vmlog_opt_parse_range(optarg,&opt_range_start,&opt_range_end))
178 vmlog_die("invalid range: %s",optarg);
190 opt_threadidx = atoi(optarg);
194 opt_show_zero_counts = 1;
197 case '?': vmlog_die_usage(usage,1);
201 if (argc - optind < 1)
202 vmlog_die_usage(usage,1);
204 opt_prefix = argv[optind++];
206 g_indentation = VMLOG_NEW_ARRAY(char,opt_indent+1);
207 memset(g_indentation,' ',opt_indent);
208 g_indentation[opt_indent] = 0;
211 static void fprint_string(FILE *file,int index)
214 vmlog_string_entry *strent;
218 strent = g_idxmap + index;
219 str = g_strmap + strent->ofs;
222 if (opt_strip_desc) {
225 desc = (char*) memchr(str,'(',len);
230 buf = VMLOG_NEW_ARRAY(char,len+1);
237 VMLOG_FREE_ARRAY(char,len+1,buf);
240 static void dump_log_entry(vmlog_log_entry *logent,int depth,vmlog_seq_t seq)
244 if (opt_show_depth) {
247 fprintf(stdout,"%d",depth);
255 if (opt_show_seqns) {
256 fprintf(stdout,"(" VMLOG_SEQ_FMT_W ")",seq);
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);
271 fprint_string(stdout,logent->index);
275 static void dump_log_entries(void)
277 vmlog_log_entry *logent;
279 int mindepth = INT_MAX;
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)))
287 int depthchange = vmlog_tag_definitions[logent->tag].depth;
289 depth += depthchange;
290 if (depthchange < 0) {
291 if (depth < mindepth)
296 vmlog_ringbuf_seek(g_ringbuf,opt_range_start);
297 if (mindepth != INT_MAX)
303 while ((g_ringbuf->seq <= opt_range_end)
304 && (logent = vmlog_ringbuf_next(g_ringbuf,VMLOGDUMP_PREFETCH)))
306 int depthchange = vmlog_tag_definitions[logent->tag].depth;
308 if (depthchange < 0) {
309 depth += depthchange;
310 if (depth < mindepth)
314 seq = g_ringbuf->seq - 1;
316 if ((!opt_string || (logent->tag == opt_needle.tag && logent->index == opt_needle.index))
317 && (seq >= opt_range_start)
318 && (depth <= opt_maxdepth))
320 dump_log_entry(logent,depth,seq);
324 depth += depthchange;
328 static void dump_strings(void)
332 for (i=0; i<g_nstrings; ++i) {
333 if (opt_show_seqns) {
334 fprintf(stdout,"%8d: ",i);
336 fprint_string(stdout,i);
341 static int compare_by_counter(const void *a,const void *b)
343 return g_counters[*(int*)b] - g_counters[*(int*)a];
346 static void count_calls(void)
351 vmlog_log_entry *logent;
353 g_counters = VMLOG_NEW_ARRAY(int,g_nstrings);
355 memset(g_counters,0,sizeof(int)*g_nstrings);
358 logent = vmlog_ringbuf_next(g_ringbuf,VMLOGDUMP_PREFETCH);
362 if (logent->tag == VMLOG_TAG_ENTER) {
363 g_counters[logent->index]++;
367 indices = VMLOG_NEW_ARRAY(int,g_nstrings);
369 for (i=0; i<g_nstrings; ++i)
372 qsort(indices,g_nstrings,sizeof(int),compare_by_counter);
374 for (i=0; i<g_nstrings; ++i) {
376 if (!g_counters[index] && !opt_show_zero_counts)
378 fprintf(stdout,"%12d ",g_counters[index]);
379 fprint_string(stdout,index);
383 VMLOG_FREE_ARRAY(int,g_nstrings,indices);
384 VMLOG_FREE_ARRAY(int,g_nstrings,g_counters);
387 static void dump_open_frames(void)
389 vmlog_thread_log *tlog;
390 vmlog_log_entry *logent;
397 tlog = vmlog_thread_log_new(NULL,NULL,0);
399 if (opt_prefix_open_frames) {
401 end = opt_range_start - 1;
404 start = opt_range_start;
409 vmlog_ringbuf_seek(g_ringbuf,start);
411 while (g_ringbuf->seq <= end
412 && (logent = vmlog_ringbuf_next(g_ringbuf,VMLOGDUMP_PREFETCH)))
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");
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;
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;
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);
441 vmlog_thread_log_free(tlog);
444 int main(int argc,char **argv)
447 int need_logfile = 1;
452 vmlog_set_progname(argv[0]);
453 parse_command_line(argc,argv);
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);
468 g_ringbuf = vmlog_ringbuf_new(g_logfname,VMLOGDUMP_RINGBUF_SIZE);
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);
476 vml = vmlog_log_new(NULL,0);
477 vmlog_load_stringhash(vml,opt_prefix);
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);
485 if (opt_countcalls) {
488 else if (opt_dumpstrings) {
491 else if (opt_open_frames) {
495 if (opt_prefix_open_frames) {
497 fputs("---\n",stdout);
502 vmlog_ringbuf_free(g_ringbuf);
504 munmap(g_strmap,g_strlen);
505 munmap(g_idxmap,g_idxlen);
510 /* vim: noet ts=8 sw=8