* src/threads/thread.cpp (thread_set_state_*): Don't take the ThreadList
[cacao.git] / contrib / vmlog / vmlogfilter.c
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>     */
4
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.
9  *
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.
14  *
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
18  */
19
20 #include "vmlog.h"
21 #include <string.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <regex.h>
25 #include <getopt.h>
26
27 char *g_idxfname;
28 char *g_strfname;
29 char *g_logfname;
30 int g_idxlen;
31 int g_strlen;
32 int g_nstrings;
33 vmlog_string_entry *g_idxmap;
34 char *g_strmap;
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;
39
40 char *g_cmd = "vmlogfilter";
41
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";
48
49 #define VMLOGDUMP_RINGBUF_SIZE (16 * 1024)
50 #define VMLOGDUMP_PREFETCH 1024
51 #if 0
52 #       define LOG(...) fprintf(stderr, "LOG: " __VA_ARGS__)
53 #else
54 #       define LOG(...)
55 #endif
56
57 #define USAGE \
58         "Usage: %s <options>\n" \
59         "\n" \
60         "Mandatory options include:\n" \
61         "\n" \
62         "\t-p <prefix>      Source prefix.\n" \
63         "\n" \
64         "Optional options include:\n" \
65         "\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" \
71         "\n" 
72
73 static void *xmalloc(int size) {
74         void *ret = malloc(size);
75         if (ret == NULL) {
76                 vmlog_die("Malloc of size %d failed.", size);
77         }
78         return ret;
79 }
80
81 static void usage(int exit_status) {
82         fprintf(stderr, USAGE, g_cmd);
83         exit(exit_status);
84 }
85
86 static void parse_options(int argc, char **argv) {
87         int ret;
88
89         while ((ret = getopt(argc, argv, "i:x:p:t:P:T:h")) != -1) {
90                 switch (ret) {
91                         case 'i':
92                                 opt_filter_include = optarg;
93                                 break;
94                         case 'x':
95                                 opt_filter_exclude = optarg;
96                                 break;
97                         case 't':
98                                 opt_src_threadidx = optarg;
99                                 break;
100                         case 'p':
101                                 opt_src_prefix = optarg;
102                                 if (opt_dest_prefix == NULL) {
103                                         opt_dest_prefix = optarg;
104                                 }
105                         case 'T':
106                                 opt_dest_threadidx = optarg;
107                                 break;
108                         case 'P':
109                                 opt_dest_prefix = optarg;
110                                 break;
111                         case 'h':
112                                 usage(EXIT_SUCCESS);
113                                 break;
114                         default:
115                                 usage(EXIT_FAILURE);
116                                 break;
117                 }
118         }
119
120         if ((opt_src_prefix == NULL) || (opt_dest_prefix == NULL)) {
121                 usage(EXIT_FAILURE);
122         }
123 }
124
125 static void match_strings() {
126         regex_t regex[2];
127         char *filter[2];
128         int default_match[2] = { 1, 0 };
129         int i, err, res;
130         char err_buf[128];
131         char *str = NULL;
132         int str_size = 0;
133         vmlog_string_entry *strent;
134         unsigned char *match;
135
136         /* Initialize */
137
138         filter[0] = opt_filter_include;
139         filter[1] = opt_filter_exclude;
140
141         /* Compile regexes */
142
143         for (i = 0; i < 2; ++i) {
144                 if (filter[i] != NULL) {
145                         err = regcomp(&regex[i], filter[i], REG_EXTENDED | REG_NOSUB);
146                         if (err != 0) {
147                                 regerror(err, &regex[i], err_buf, sizeof(err_buf));
148                                 vmlog_die(
149                                         "Invalid regex %s: %s",
150                                         filter[i], err_buf
151                                 );
152                         }
153                 }
154         }
155
156         /* Allocate array. For each string one byte with flags. */
157
158         g_matches = (unsigned char *)xmalloc(g_nstrings * sizeof(unsigned char));
159
160         /* Traverse strings and match */
161
162         for (strent = g_idxmap, match = g_matches; strent != g_idxmap + g_nstrings; ++strent, ++match) {
163                 *match = 0;
164
165                 if ((strent->len + 1) > str_size) {
166                         str_size = strent->len + 1;
167                         free(str);
168                         str = (char *)xmalloc(str_size);
169                 }
170                 memcpy(str, g_strmap + strent->ofs, strent->len);
171                 str[strent->len] = '\0';
172
173                 for (i = 0; i < 2; ++i) {
174                         if (filter[i] != NULL) {
175                                 res = regexec(&regex[i], str, 0, NULL, 0);
176                                 if (res == 0) {
177                                         *match |= (1 << i);
178                                         LOG("String `%s' matches regex `%d (%s)'\n", str, i, filter[i]);
179                                 }
180                         } else {
181                                 *match |= (default_match[i] << i);
182                         }
183                 }
184         }
185 }
186
187 /* see src/vm/jit/show.c in the cacao source tree for documentation on the below. */
188
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]
200
201 int g_ctr[2] = { 0, 0 };
202
203 static int test_enter(unsigned char match_flags) {
204
205         int force_show = 0;
206
207         if (STATE_IS_INITIAL()) {
208                 if (EVENT_INCLUDE()) {
209                         TRANSITION_NEXT_INCLUDE();
210                 }
211         } else if (STATE_IS_INCLUDE()) {
212                 if (EVENT_EXCLUDE()) {
213                         TRANSITION_NEXT_EXCLUDE();
214                         /* just entered exclude, show this method */
215                         force_show = 1;
216                 } else if (EVENT_INCLUDE()) {
217                         TRANSITION_NEXT_INCLUDE();
218                 }
219         } else if (STATE_IS_EXCLUDE()) {
220                 if (EVENT_EXCLUDE()) {
221                         TRANSITION_NEXT_EXCLUDE();
222                 }
223         }
224
225         return STATE_IS_INCLUDE() || force_show;
226 }
227
228 static int test_exit(unsigned char match_flags) {
229
230         int force_show = 0;
231
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;
237                 }
238         } else if (STATE_IS_EXCLUDE()) {
239                 if (EVENT_EXCLUDE()) {
240                         TRANSITION_PREV_EXCLUDE();
241                 }
242         }
243
244         return STATE_IS_INCLUDE() || force_show;
245 }
246
247 static int test_other() {
248         return STATE_IS_INCLUDE();
249 }
250
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);
258                                         g_thread_log->seq++;
259                                 }
260                                 break;
261                         case VMLOG_TAG_LEAVE:
262                                 if (test_exit(g_matches[logent->index])) {
263                                         vmlog_thread_log_append(g_thread_log, logent);
264                                         g_thread_log->seq++;
265                                 }
266                                 break;
267                         default:
268                                 if (test_other()) {
269                                         vmlog_thread_log_append(g_thread_log, logent);
270                                         g_thread_log->seq++;
271                                 }
272                                 break;
273                 }
274         }
275 }
276
277 int main(int argc,char **argv) {
278
279         if (argc) {
280                 vmlog_set_progname(argv[0]);
281                 g_cmd = argv[0];
282         }
283
284         parse_options(argc, argv);
285
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),
290                 ".", 1, 
291                 opt_src_threadidx, strlen(opt_src_threadidx),
292                 ".log", 4,
293                 NULL
294         );
295
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);
300
301         g_log = vmlog_log_new(opt_dest_prefix, 0);
302         g_thread_log = vmlog_thread_log_new(
303                 g_log, 
304                 (void *)atoi(opt_dest_threadidx), 
305                 atoi(opt_dest_threadidx) 
306         );
307
308         match_strings();        
309         do_filter();
310
311         vmlog_thread_log_free(g_thread_log);
312         vmlog_log_free(g_log);
313         
314         vmlog_ringbuf_free(g_ringbuf);
315         g_ringbuf = NULL;
316         vmlog_file_munmap(g_strmap,g_strlen);
317         vmlog_file_munmap(g_idxmap,g_idxlen);
318
319         return 0;
320 }
321