1 /* vmlog - high-speed logging for free VMs */
2 /* Copyright (C) 2006 Edwin Steiner <edwin.steiner@gmx.net> */
3 /* 2007 Peter Molnar <peter.molnar@wm.sk> */
5 /* This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
33 vmlog_string_entry *g_idxmap;
35 unsigned char *g_matches;
36 vmlog_ringbuf *g_ringbuf = NULL;
37 vmlog_log *g_log = NULL;
38 vmlog_thread_log *g_thread_log = NULL;
40 char *g_cmd = "vmlogfilter";
42 char *opt_src_prefix = NULL;
43 char *opt_dest_prefix = NULL;
44 char *opt_filter_include = NULL;
45 char *opt_filter_exclude = NULL;
46 char *opt_src_threadidx = "0";
47 char *opt_dest_threadidx = "100";
49 #define VMLOGDUMP_RINGBUF_SIZE (16 * 1024)
50 #define VMLOGDUMP_PREFETCH 1024
52 # define LOG(...) fprintf(stderr, "LOG: " __VA_ARGS__)
58 "Usage: %s <options>\n" \
60 "Mandatory options include:\n" \
62 "\t-p <prefix> Source prefix.\n" \
64 "Optional options include:\n" \
66 "\t-t <threadidx> Source thread index. Defaults to 0.\n" \
67 "\t-P <prefix> Destination prefix, defaults to source prefix.\n" \
68 "\t-T <threadidx> Destination thread index. Defaults to 100.\n" \
69 "\t-i <regex> Filter include.\n" \
70 "\t-x <regex> Filter exclude.\n" \
73 static void *xmalloc(int size) {
74 void *ret = malloc(size);
76 vmlog_die("Malloc of size %d failed.", size);
81 static void usage(int exit_status) {
82 fprintf(stderr, USAGE, g_cmd);
86 static void parse_options(int argc, char **argv) {
89 while ((ret = getopt(argc, argv, "i:x:p:t:P:T:h")) != -1) {
92 opt_filter_include = optarg;
95 opt_filter_exclude = optarg;
98 opt_src_threadidx = optarg;
101 opt_src_prefix = optarg;
102 if (opt_dest_prefix == NULL) {
103 opt_dest_prefix = optarg;
106 opt_dest_threadidx = optarg;
109 opt_dest_prefix = optarg;
120 if ((opt_src_prefix == NULL) || (opt_dest_prefix == NULL)) {
125 static void match_strings() {
128 int default_match[2] = { 1, 0 };
133 vmlog_string_entry *strent;
134 unsigned char *match;
138 filter[0] = opt_filter_include;
139 filter[1] = opt_filter_exclude;
141 /* Compile regexes */
143 for (i = 0; i < 2; ++i) {
144 if (filter[i] != NULL) {
145 err = regcomp(®ex[i], filter[i], REG_EXTENDED | REG_NOSUB);
147 regerror(err, ®ex[i], err_buf, sizeof(err_buf));
149 "Invalid regex %s: %s",
156 /* Allocate array. For each string one byte with flags. */
158 g_matches = (unsigned char *)xmalloc(g_nstrings * sizeof(unsigned char));
160 /* Traverse strings and match */
162 for (strent = g_idxmap, match = g_matches; strent != g_idxmap + g_nstrings; ++strent, ++match) {
165 if ((strent->len + 1) > str_size) {
166 str_size = strent->len + 1;
168 str = (char *)xmalloc(str_size);
170 memcpy(str, g_strmap + strent->ofs, strent->len);
171 str[strent->len] = '\0';
173 for (i = 0; i < 2; ++i) {
174 if (filter[i] != NULL) {
175 res = regexec(®ex[i], str, 0, NULL, 0);
178 LOG("String `%s' matches regex `%d (%s)'\n", str, i, filter[i]);
181 *match |= (default_match[i] << i);
187 /* see src/vm/jit/show.c in the cacao source tree for documentation on the below. */
189 #define MATCHFLAGS match_flags
190 #define FILTERVERBOSECALLCTR g_ctr
191 #define STATE_IS_INITIAL() ((FILTERVERBOSECALLCTR[0] == 0) && (FILTERVERBOSECALLCTR[1] == 0))
192 #define STATE_IS_INCLUDE() ((FILTERVERBOSECALLCTR[0] > 0) && (FILTERVERBOSECALLCTR[1] == 0))
193 #define STATE_IS_EXCLUDE() (FILTERVERBOSECALLCTR[1] > 0)
194 #define EVENT_INCLUDE() (MATCHFLAGS & (1 << 0))
195 #define EVENT_EXCLUDE() (MATCHFLAGS & (1 << 1))
196 #define TRANSITION_NEXT_INCLUDE() ++FILTERVERBOSECALLCTR[0]
197 #define TRANSITION_PREV_INCLUDE() --FILTERVERBOSECALLCTR[0]
198 #define TRANSITION_NEXT_EXCLUDE() ++FILTERVERBOSECALLCTR[1]
199 #define TRANSITION_PREV_EXCLUDE() --FILTERVERBOSECALLCTR[1]
201 int g_ctr[2] = { 0, 0 };
203 static int test_enter(unsigned char match_flags) {
207 if (STATE_IS_INITIAL()) {
208 if (EVENT_INCLUDE()) {
209 TRANSITION_NEXT_INCLUDE();
211 } else if (STATE_IS_INCLUDE()) {
212 if (EVENT_EXCLUDE()) {
213 TRANSITION_NEXT_EXCLUDE();
214 /* just entered exclude, show this method */
216 } else if (EVENT_INCLUDE()) {
217 TRANSITION_NEXT_INCLUDE();
219 } else if (STATE_IS_EXCLUDE()) {
220 if (EVENT_EXCLUDE()) {
221 TRANSITION_NEXT_EXCLUDE();
225 return STATE_IS_INCLUDE() || force_show;
228 static int test_exit(unsigned char match_flags) {
232 if (STATE_IS_INCLUDE()) {
233 if (EVENT_INCLUDE()) {
234 TRANSITION_PREV_INCLUDE();
235 /* just entered initial, show this method */
236 if (STATE_IS_INITIAL()) force_show = 1;
238 } else if (STATE_IS_EXCLUDE()) {
239 if (EVENT_EXCLUDE()) {
240 TRANSITION_PREV_EXCLUDE();
244 return STATE_IS_INCLUDE() || force_show;
247 static int test_other() {
248 return STATE_IS_INCLUDE();
251 static void do_filter() {
252 vmlog_log_entry *logent;
253 while ((logent = vmlog_ringbuf_next(g_ringbuf, VMLOGDUMP_PREFETCH)) != NULL) {
254 switch (logent->tag) {
255 case VMLOG_TAG_ENTER:
256 if (test_enter(g_matches[logent->index])) {
257 vmlog_thread_log_append(g_thread_log, logent);
261 case VMLOG_TAG_LEAVE:
262 if (test_exit(g_matches[logent->index])) {
263 vmlog_thread_log_append(g_thread_log, logent);
269 vmlog_thread_log_append(g_thread_log, logent);
277 int main(int argc,char **argv) {
280 vmlog_set_progname(argv[0]);
284 parse_options(argc, argv);
286 g_idxfname = vmlog_concat3(opt_src_prefix,"",".idx",NULL);
287 g_strfname = vmlog_concat3(opt_src_prefix,"",".str",NULL);
288 g_logfname = vmlog_concat4len(
289 opt_src_prefix, strlen(opt_src_prefix),
291 opt_src_threadidx, strlen(opt_src_threadidx),
296 g_ringbuf = vmlog_ringbuf_new(g_logfname, VMLOGDUMP_RINGBUF_SIZE);
297 g_idxmap = (vmlog_string_entry *) vmlog_file_mmap(g_idxfname,&g_idxlen);
298 g_nstrings = g_idxlen / sizeof(vmlog_string_entry);
299 g_strmap = (char *) vmlog_file_mmap(g_strfname,&g_strlen);
301 g_log = vmlog_log_new(opt_dest_prefix, 0);
302 g_thread_log = vmlog_thread_log_new(
304 (void *)atoi(opt_dest_threadidx),
305 atoi(opt_dest_threadidx)
311 vmlog_thread_log_free(g_thread_log);
312 vmlog_log_free(g_log);
314 vmlog_ringbuf_free(g_ringbuf);
316 vmlog_file_munmap(g_strmap,g_strlen);
317 vmlog_file_munmap(g_idxmap,g_idxlen);