* contrib/vmlog: Committed vmlog 0.0.5.
[cacao.git] / contrib / vmlog / vmlog.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 <sys/stat.h>
29 #include <sys/mman.h>
30
31 #include <jni.h>
32
33 /*** default macros **************************************************/
34
35 #ifndef VMLOG_LOCK
36 #define VMLOG_LOCK(vml)
37 #define VMLOG_UNLOCK(vml)
38 #endif
39
40 /* #define VMLOG_ENDIAN_CONVERT_WRITE */
41 #define VMLOG_HOST_LITTLE_ENDIAN
42
43 /*** constants *******************************************************/
44
45 /* currently vmlog does no rehashing, so these should be quite big */
46 #define VMLOG_INITIAL_STRING_HASH_SIZE       50000 /* XXX debug */
47 #define VMLOG_INITIAL_THREAD_HASH_SIZE       8 /* XXX debug */
48
49 /* initial size of the frame buffer - this is doubled each time    */
50 /* the frame buffer has to grow                                    */
51 #define VMLOG_INITIAL_FRAMES_CAPACITY        1 /* XXX debug */
52
53 /*** types ***********************************************************/
54
55 /* we declare this here because defining _LARGEFILE64_SOURCE works */
56 /* only if we are the first ones to include the headers, which may */
57 /* not be the case if vmlog.c is used as an include file.          */
58
59 #ifndef _LARGEFILE64_SOURCE
60 typedef long long off64_t;
61 off64_t lseek64(int fd, off64_t offset, int whence);
62 #endif
63
64 /*** tag definitions *************************************************/
65
66 /* CAUTION: these must are indexed by the VMLOG_TAG_... constants! */
67 vmlog_tag_definition vmlog_tag_definitions[] = {
68         { "enter", "enter", +1 },
69         { "leave", "leave", -1 },
70         { "throw", "throw",  0 },
71         { "catch", "catch",  0 },
72         { "unwnd", "unwnd", -1 },
73         { "signl", "signl",  0 },
74         { "unrol", "unrol", -1 },
75         { "rerol", "rerol", +1 },
76         { NULL   , NULL   ,  0 }
77 };
78
79 /*** global variables ************************************************/
80
81 static char *vmlog_progname = "vmlog";
82
83 /*** prototypes ******************************************************/
84
85 static void *vmlog_memdup(const void *m,int len);
86
87 /*** error reporting *************************************************/
88
89 void vmlog_set_progname(const char *progname)
90 {
91         if (!progname) {
92                 progname = "vmlog (progname == NULL)";
93         }
94
95         vmlog_progname = vmlog_memdup(progname,strlen(progname)+1);
96 }
97
98 void vmlog_die(const char *fmt,...)
99 {
100         va_list ap;
101
102         fputs(vmlog_progname,stderr);
103         fputs(": error: ",stderr);
104         va_start(ap,fmt);
105         vfprintf(stderr,fmt,ap);
106         va_end(ap);
107         fputc('\n',stderr);
108         exit(1);
109 }
110
111 void vmlog_warn(const char *fmt,...)
112 {
113         va_list ap;
114
115         fputs(vmlog_progname,stderr);
116         fputs(": warning: ",stderr);
117         va_start(ap,fmt);
118         vfprintf(stderr,fmt,ap);
119         va_end(ap);
120         fputc('\n',stderr);
121 }
122
123 void vmlog_die_usage(const char *usage,int error)
124 {
125         assert(usage);
126         
127         fputs(usage,(error) ? stderr : stdout);
128         exit((error) ? 1 : 0);
129 }
130
131 /*** utility functions ***********************************************/
132
133 static void *vmlog_memdup(const void *data,int len)
134 {
135         char *p;
136
137         p = VMLOG_NEW_ARRAY(char,len);
138         assert(p);
139         memcpy(p,data,len);
140
141         return p;
142 }
143
144 static void *vmlog_strdup(const void *data,int len)
145 {
146         char *p;
147
148         p = VMLOG_NEW_ARRAY(char,len+1);
149         assert(p);
150         memcpy(p,data,len);
151         p[len] = 0;
152
153         return p;
154 }
155
156 char *vmlog_concat4len(const char *a,int alen,const char *b,int blen,
157                        const char *c,int clen,const char *d,int dlen,
158                        int *plen)
159 {
160         int len;
161         char *p;
162         char *pp;
163
164         assert(a);
165         assert(b);
166         assert(c);
167         assert(d);
168
169         len = alen + blen + clen + dlen;
170         if (plen)
171                 *plen = len;
172
173         p = VMLOG_NEW_ARRAY(char,len+1);
174         pp = p;
175         memcpy(pp,a,alen); pp += alen;
176         memcpy(pp,b,blen); pp += blen;
177         memcpy(pp,c,clen); pp += clen;
178         memcpy(pp,d,dlen); pp += dlen;
179         *pp = 0;
180
181         return p;
182 }
183
184 char *vmlog_concat3(const char *a,const char *b,const char *c,int *plen)
185 {
186         int len,lena,lenb,lenc;
187         char *p;
188         char *pp;
189
190         assert(a);
191         assert(b);
192         assert(c);
193
194         lena = strlen(a);
195         lenb = strlen(b);
196         lenc = strlen(c);
197
198         len = lena + lenb + lenc;
199         if (plen)
200                 *plen = len;
201
202         p = VMLOG_NEW_ARRAY(char,len+1);
203         pp = p;
204         memcpy(pp,a,lena); pp += lena;
205         memcpy(pp,b,lenb); pp += lenb;
206         memcpy(pp,c,lenc); pp += lenc;
207         *pp = 0;
208
209         return p;
210 }
211
212 /*** file ops ********************************************************/
213
214 void vmlog_file_open(vmlog_file *file,const char *fname,vmlog_fmode fmode)
215 {
216         int r;
217         struct stat st;
218         int flags = 0;
219         
220         assert(file);
221
222         switch (fmode) {
223                 case vmlogRead:
224                         flags = O_RDONLY;
225                         break;
226
227                 case vmlogAppend:
228                         flags = O_WRONLY | O_APPEND | O_CREAT;
229                         break;
230
231                 case vmlogTruncateAppend:
232                         flags = O_WRONLY | O_APPEND | O_CREAT | O_TRUNC;
233                         break;
234
235                 default:
236                         vmlog_die("unknown fmode for opening file: %s: %d",
237                                 fname,fmode);
238         }
239
240         r = open(fname,flags,0644);
241         if (r == -1) {
242                 vmlog_die("could not open file: %s: %s",fname,strerror(errno));
243         }
244         file->fd = r;
245         file->fnamelen = strlen(fname);
246         file->fname = vmlog_memdup(fname,file->fnamelen+1);
247
248         r = fstat(file->fd,&st);
249         if (r == -1) {
250                 vmlog_die("could not stat file: %s: %s",fname,strerror(errno));
251         }
252         file->ofs = (fmode == vmlogRead) ? 0 : st.st_size;
253 }
254
255 void vmlog_file_close(vmlog_file *file)
256 {
257         assert(file);
258
259         if (file->fd == -1)
260                 return;
261
262         close(file->fd);
263         file->fd = -1;
264         VMLOG_FREE_ARRAY(char,file->fnamelen+1,file->fname);
265         file->fname = NULL;
266         file->fnamelen = 0;
267 }
268
269 void vmlog_file_append(vmlog_file *file,const void *data,int len)
270 {
271         int r;
272
273         assert(len >= 0);
274         if (!len)
275                 return;
276         assert(data);
277         
278         do {
279                 r = write(file->fd,data,len);
280         } while (r == -1 && errno == EINTR);
281
282         if (r == -1) {
283                 vmlog_die("could not write to file: %s: %s",file->fname,strerror(errno));
284         }
285
286         if (r != len) {
287                 vmlog_die("could not write all data to file: %s",file->fname);
288         }
289
290         file->ofs += len;
291 }
292
293 void vmlog_file_stat(vmlog_file *file)
294 {
295         int r;
296         struct stat st;
297         
298         r = fstat(file->fd,&st);
299         if (r == -1)
300                 vmlog_die("could not stat file: %s: %s",file->fname,strerror(errno));
301
302         file->size = st.st_size;
303 }
304
305 void * vmlog_file_mmap(const char *fname,int *plen)
306 {
307         int fd;
308         int r;
309         struct stat st;
310         void *m;
311
312         fd = open(fname,O_RDONLY);
313         if (fd == -1)
314                 vmlog_die("could not open file: %s: %s",fname,strerror(errno));
315         
316         r = fstat(fd,&st);
317         if (r == -1)
318                 vmlog_die("could not stat file: %s: %s",fname,strerror(errno));
319
320         if (plen)
321                 *plen = st.st_size;
322         
323         if (st.st_size) {
324                 m = mmap(NULL,st.st_size,PROT_READ,MAP_PRIVATE,fd,0);
325                 if (m == MAP_FAILED) {
326                         vmlog_die("could not mmap file: %s: %s",fname,strerror(errno));
327                 }
328         }
329         else {
330                 /* fake a pointer */
331                 m = VMLOG_NEW(char);
332         }
333
334         close(fd);
335
336         return m;
337 }
338
339 void vmlog_file_munmap(void *m,int len)
340 {
341         int r;
342         
343         if (len) {
344                 r = munmap(m,len);
345                 if (r != 0)
346                         vmlog_warn("could not munmap file: %s",strerror(errno));
347         }
348         else {
349                 VMLOG_FREE(char,m);
350         }
351 }
352
353 void vmlog_file_seek(vmlog_file *file,vmlog_fofs_t ofs)
354 {
355         off64_t r;
356
357         r = lseek64(file->fd,ofs,SEEK_SET);
358         if (r == (off64_t)-1)
359                 vmlog_die("could not seek position in file: %s: %s",
360                         file->fname,strerror(errno));
361         file->ofs = ofs;
362 }
363
364 /*** string storage **************************************************/
365
366 static void vmlog_add_string(vmlog_log *vml,const char *data,int len)
367 {
368         vmlog_string_entry strent;
369 #if defined(VMLOG_ENDIAN_CONVERT_WRITE)
370         vmlog_fofs_t tmp;
371 #endif
372         
373         assert(vml);
374
375         if (vml->strfile.fd == -1)
376                 return;
377         if (vml->idxfile.fd == -1)
378                 return;
379
380         strent.ofs = vml->strfile.ofs;
381         strent.len = len;
382
383 #if defined(VMLOG_ENDIAN_CONVERT_WRITE)
384 #if defined(VMLOG_HOST_LITTLE_ENDIAN)
385         tmp = ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[0]) << 56)
386             | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[1]) << 48)
387             | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[2]) << 40)
388             | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[3]) << 32)
389             | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[4]) << 24)
390             | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[5]) << 16)
391             | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[6]) <<  8)
392             | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[7]) <<  0);
393         strent.ofs = tmp;
394         tmp = ((vmlog_fofs_t)(((unsigned char*)&strent.len)[0]) << 24)
395             | ((vmlog_fofs_t)(((unsigned char*)&strent.len)[1]) << 16)
396             | ((vmlog_fofs_t)(((unsigned char*)&strent.len)[2]) <<  8)
397             | ((vmlog_fofs_t)(((unsigned char*)&strent.len)[3]) <<  0);
398         strent.len = tmp;
399 #else
400         tmp = ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[7]) << 56)
401             | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[6]) << 48)
402             | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[5]) << 40)
403             | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[4]) << 32)
404             | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[3]) << 24)
405             | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[2]) << 16)
406             | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[1]) <<  8)
407             | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[0]) <<  0);
408         strent.ofs = tmp;
409         tmp = ((vmlog_fofs_t)(((unsigned char*)&strent.len)[3]) << 24)
410             | ((vmlog_fofs_t)(((unsigned char*)&strent.len)[2]) << 16)
411             | ((vmlog_fofs_t)(((unsigned char*)&strent.len)[1]) <<  8)
412             | ((vmlog_fofs_t)(((unsigned char*)&strent.len)[0]) <<  0);
413         strent.len = tmp;
414 #endif
415 #endif /* defined(VMLOG_ENDIAN_CONVERT_WRITE) */
416         
417         vmlog_file_append(&(vml->strfile),data,len);
418         vmlog_file_append(&(vml->idxfile),&strent,sizeof(vmlog_string_entry));
419 }
420
421 /*** index functions *************************************************/
422
423 static int vmlog_is_ignored(vmlog_log *vml,int index)
424 {
425         return (index < vml->ignorelistlen);
426 }
427
428 /*** thread log functions ********************************************/
429
430 static void vmlog_thread_log_alloc_logbuf(vmlog_thread_log *tlog,int cap)
431 {
432         assert(tlog);
433         assert(cap >= 0);
434
435         if (cap) {
436                 VMLOG_XZNEW_ARRAY(tlog->logbuf,vmlog_log_entry,cap);
437         }
438         else {
439                 tlog->logbuf = NULL;
440         }
441         tlog->logbufptr = tlog->logbuf;
442         tlog->logbufend = tlog->logbuf + cap;
443         tlog->logbufcap = cap;
444 }
445
446 static void vmlog_thread_log_flush(vmlog_thread_log *tlog)
447 {
448         assert(tlog);
449         assert(tlog->logbuf);
450
451         vmlog_file_append(&(tlog->logfile),tlog->logbuf,
452                         (tlog->logbufptr - tlog->logbuf) * sizeof(vmlog_log_entry));
453
454         tlog->logbufptr = tlog->logbuf;
455 }
456
457 static void vmlog_thread_log_realloc_frames(vmlog_thread_log *tlog,int cap)
458 {
459         vmlog_frame *oldframes;
460
461         assert(tlog);
462         assert(cap >= tlog->depth);
463
464         oldframes = tlog->frames;
465
466         if (cap) {
467                 VMLOG_XZNEW_ARRAY(tlog->frames,vmlog_frame,cap);
468         }
469         else {
470                 tlog->frames = NULL;
471         }
472         
473         if (oldframes) {
474                 if (tlog->frames) {
475                         memcpy(tlog->frames,oldframes,sizeof(vmlog_frame) * tlog->depth);
476                 }
477                 VMLOG_FREE_ARRAY(vmlog_frame,tlog->framescap,oldframes);
478         }
479         tlog->framescap = cap;
480 }
481
482 static void vmlog_thread_log_append(vmlog_thread_log *tlog,vmlog_log_entry *logent)
483 {
484 #if defined(VMLOG_ENDIAN_CONVERT_WRITE)
485         unsigned int tmp;
486
487 #if defined(VMLOG_HOST_LITTLE_ENDIAN)
488         tmp = ((unsigned int)(((unsigned char*)logent)[3]) <<  0)
489             | ((unsigned int)(((unsigned char*)logent)[2]) <<  8)
490             | ((unsigned int)(((unsigned char*)logent)[1]) << 16);
491 #else
492         tmp = ((unsigned int)(((unsigned char*)logent)[1]) <<  0)
493             | ((unsigned int)(((unsigned char*)logent)[2]) <<  8)
494             | ((unsigned int)(((unsigned char*)logent)[3]) << 16);
495 #endif
496         logent->index = tmp;
497 #endif /* defined(VMLOG_ENDIAN_CONVERT_WRITE) */
498         if (tlog->logbufptr) {
499                 if (tlog->logbufptr == tlog->logbufend) {
500                         vmlog_thread_log_flush(tlog);
501                 }
502                 *tlog->logbufptr++ = *logent;
503         }
504         else {
505                 vmlog_file_append(&(tlog->logfile),logent,sizeof(vmlog_log_entry));
506         }
507 }
508
509 #define VMLOG_INT2STR_BUFFER 20
510
511 vmlog_thread_log *vmlog_thread_log_new(vmlog_log *vml,void *threadid,int index)
512 {
513         vmlog_thread_log *tlog;
514         char buf[VMLOG_INT2STR_BUFFER];
515         int r;
516         char *name;
517         int namelen;
518
519         VMLOG_XZNEW(tlog,vmlog_thread_log);
520
521         tlog->threadid = threadid;
522         tlog->threadidx = index;
523         tlog->logfile.fd = -1;
524
525         vmlog_thread_log_realloc_frames(tlog,VMLOG_INITIAL_FRAMES_CAPACITY);
526
527         if (vml && vml->prefix) {
528                 r = snprintf(buf,VMLOG_INT2STR_BUFFER,"%d",index);
529                 assert(r < VMLOG_INT2STR_BUFFER);
530                 buf[VMLOG_INT2STR_BUFFER-1] = 0;
531                 name = vmlog_concat4len(vml->prefix,vml->prefixlen,
532                                         ".",1,
533                                         buf,strlen(buf),
534                                         ".log",4,
535                                         &namelen);
536                 vmlog_file_open(&(tlog->logfile),name,vmlogTruncateAppend);
537                 VMLOG_FREE_ARRAY(char,namelen+1,name);
538         }
539
540         return tlog;
541 }
542
543 void vmlog_thread_log_free(vmlog_thread_log *tlog)
544 {
545         if (!tlog)
546                 return;
547
548         if (tlog->logbuf)
549                 vmlog_thread_log_flush(tlog);
550         
551         vmlog_file_close(&(tlog->logfile));
552         
553         if (tlog->frames) {
554                 VMLOG_FREE_ARRAY(vmlog_frame,tlog->framescap,tlog->frames);
555         }
556         VMLOG_FREE(vmlog_thread_log,tlog);
557 }
558
559 vmlog_frame * vmlog_thread_log_enter(vmlog_thread_log *tlog,int index,vmlog_seq_t seq)
560 {
561         vmlog_frame *frame;
562
563         if (tlog->depth < 0) {
564                 vmlog_warn("negative call frame depth %d at seq " VMLOG_SEQ_FMT,
565                                 tlog->depth,seq);
566                 return NULL;
567         }
568         
569         if (tlog->depth >= tlog->framescap)
570                 vmlog_thread_log_realloc_frames(tlog,tlog->framescap * 2);
571
572         frame = tlog->frames + tlog->depth;
573
574         frame->index = index;
575         frame->seq = seq;
576
577         tlog->depth++;
578
579         return frame;
580 }
581
582 vmlog_frame * vmlog_thread_log_leave(vmlog_thread_log *tlog,int index,vmlog_seq_t seq)
583 {
584         vmlog_frame *frame;
585
586         if (--tlog->depth < 0) {
587                 vmlog_warn("negative call frame depth %d at seq " VMLOG_SEQ_FMT,
588                                 tlog->depth,seq);
589                 return NULL;
590         }
591         
592         frame = tlog->frames + tlog->depth;
593
594         if (index != frame->index)
595                 vmlog_warn("mismatched leave at seq " VMLOG_SEQ_FMT 
596                                 ": entered index %d, left index %d",
597                         seq,frame->index,index);
598
599         return frame;
600 }
601
602 /*** tag definitions *************************************************/
603
604 /* RETURNS                                                           */
605 /*     the tag number, or -1 if the tag name is invalid              */
606
607 int vmlog_tag_from_name(const char *name,int namelen)
608 {
609         vmlog_tag_definition *td;
610         int i;
611         
612         if (!name || namelen < 1)
613                 return -1;
614
615         td = vmlog_tag_definitions;
616         i = 0;
617         while (td->name) {
618                 if (namelen == strlen(td->name)
619                     && strncmp(td->name,name,namelen) == 0) 
620                 {
621                         return i;
622                 }
623                 td++;
624                 i++;
625         }
626
627         return -1;
628 }
629
630 /*** hash functions **************************************************/
631
632 static unsigned int vmlog_thread_hash(void *threadid) 
633 {
634         /* XXX use a better hash function? */
635         return (unsigned int)(ptrint)threadid;
636 }
637
638 static unsigned int vmlog_string_hash(const char *data,int len) 
639 {
640         register const unsigned char *p = (const unsigned char *) data;
641         register unsigned int hash;
642         register int i;
643
644         /* The algorithm is the "One-at-a-time" algorithm as published    */
645         /* by Bob Jenkins on http://burtleburtle.net/bob/hash/doobs.html. */
646
647         hash = 0;
648         for (i=len; i--;)
649         {
650             hash += *p++;
651             hash += (hash << 10);
652             hash ^= (hash >> 6);
653         }
654         hash += (hash << 3);
655         hash ^= (hash >> 11);
656         hash += (hash << 15);
657
658         return hash;
659 }
660
661 /*** hash tables *****************************************************/
662
663 static vmlog_thread_log *vmlog_get_thread_log(vmlog_log *vml,void *threadid)
664 {
665         unsigned int h;
666         vmlog_hash_entry *preventry = NULL;
667         vmlog_hash_entry *entry;
668         vmlog_thread_log *tlog;
669         
670         assert(vml);
671
672         h = vmlog_thread_hash(threadid);
673         entry = vml->threadhash.table + (h % vml->threadhash.size);
674         do {
675                 tlog = (vmlog_thread_log *)entry->data;
676                 if (tlog && tlog->threadid == threadid)
677                         return tlog;
678                 preventry = entry;
679                 entry = entry->hashlink;
680         } while (entry);
681
682         /* this is a new threadid */
683         tlog = vmlog_thread_log_new(vml,threadid,vml->threadhash.nentries++);
684         
685         assert(preventry);
686         if (preventry->data) {
687                 VMLOG_XZNEW(entry,vmlog_hash_entry);
688
689                 preventry->hashlink = entry;
690         }
691         else {
692                 entry = preventry;
693         }
694
695         entry->data = tlog;
696
697         /* XXX maybe rehash */
698
699         return tlog;
700 }
701
702 int vmlog_get_string_index(vmlog_log *vml,const char *data,int len)
703 {
704         unsigned int hash;
705         vmlog_hash_entry *entry;
706         vmlog_hash_entry *preventry = NULL;
707         
708         assert(vml);
709         assert(data);
710         assert(len >= 0);
711
712         hash = vmlog_string_hash(data,len);
713         entry = vml->stringhash.table + (hash % vml->stringhash.size);
714         do {
715                 if (entry->len == len && entry->data && memcmp(data,entry->data,len) == 0)
716                         return entry->index;
717                 preventry = entry;
718                 entry = entry->hashlink;
719         } while (entry);
720
721         /* this is a new string */
722         assert(preventry);
723         if (preventry->data) {
724                 VMLOG_XZNEW(entry,vmlog_hash_entry);
725
726                 preventry->hashlink = entry;
727         }
728         else {
729                 entry = preventry;
730         }
731
732         entry->data = vmlog_memdup(data,len);
733         entry->len = len;
734         entry->index = vml->stringhash.nentries++;
735         vmlog_add_string(vml,data,len);
736
737         return entry->index;
738 }
739
740 static void vmlog_hashtable_init(vmlog_hash_table *ht,int size)
741 {
742         assert(ht);
743         assert(size > 0);
744         
745         ht->size = size;
746         VMLOG_XZNEW_ARRAY(ht->table,vmlog_hash_entry,size);
747         ht->nentries = 0;
748 }
749
750 static void vmlog_hashtable_free(vmlog_hash_table *ht,vmlog_hash_entry_destructor destr)
751 {
752         int i;
753         vmlog_hash_entry *entry,*next;
754         
755         assert(ht);
756
757         for (i=0; i<ht->size; ++i) {
758                 entry = ht->table + i;
759                 if (destr)
760                         destr(entry);
761
762                 next = entry->hashlink;
763                 while (next) {
764                         entry = next;
765                         if (destr)
766                                 destr(entry);
767                         next = entry->hashlink;
768                         VMLOG_FREE(vmlog_hash_entry,entry);
769                 }
770         }
771
772         VMLOG_FREE_ARRAY(vmlog_hash_entry,ht->size,ht->table);
773         memset(ht,0,sizeof(vmlog_hash_table));
774 }
775
776 static void vmlog_thread_log_destructor(vmlog_hash_entry *entry)
777 {
778         vmlog_thread_log *tlog;
779         
780         assert(entry);
781
782         tlog = (vmlog_thread_log *)entry->data;
783         vmlog_thread_log_free(tlog);
784 }
785
786 static void vmlog_string_destructor(vmlog_hash_entry *entry)
787 {
788         char *str;
789
790         assert(entry);
791
792         str = (char *)entry->data;
793         if (str) {
794                 VMLOG_FREE_ARRAY(char,entry->len,str);
795         }
796 }
797
798 static void vmlog_open_string_files(vmlog_log *vml,int truncate)
799 {
800         char *name;
801         int namelen;
802         int fmode;
803
804         if (!vml->prefix)
805                 return;
806
807         fmode = (truncate) ? vmlogTruncateAppend : vmlogAppend;
808
809         name = vmlog_concat3(vml->prefix,"",".idx",&namelen);
810         vmlog_file_open(&(vml->idxfile),name,fmode);
811         VMLOG_FREE_ARRAY(char,namelen+1,name);
812
813         name = vmlog_concat3(vml->prefix,"",".str",&namelen);
814         vmlog_file_open(&(vml->strfile),name,fmode);
815         VMLOG_FREE_ARRAY(char,namelen+1,name);
816 }
817
818 void vmlog_load_stringhash(vmlog_log *vml,const char *prefix)
819 {
820         int n;
821         vmlog_string_entry *idxmap;
822         vmlog_string_entry *strent;
823         char *strmap;
824         int idxlen;
825         int strlen;
826         char *idxfname;
827         char *strfname;
828         int idxnamelen;
829         int strnamelen;
830         
831         assert(vml);
832         assert(prefix);
833
834         idxfname = vmlog_concat3(prefix,".","idx",&idxnamelen);
835         strfname = vmlog_concat3(prefix,".","str",&strnamelen);
836         
837         vmlog_hashtable_free(&(vml->stringhash),vmlog_string_destructor);
838         vmlog_hashtable_init(&(vml->stringhash),VMLOG_INITIAL_STRING_HASH_SIZE);
839
840         vmlog_file_close(&(vml->idxfile));
841         vmlog_file_close(&(vml->strfile));
842         vmlog_open_string_files(vml,1);
843
844         idxmap = vmlog_file_mmap(idxfname,&idxlen);
845         strmap = vmlog_file_mmap(strfname,&strlen);
846
847         n = idxlen / sizeof(vmlog_string_entry);
848         strent = idxmap;
849         while (n--) {
850                 vmlog_get_string_index(vml,strmap + strent->ofs,strent->len);
851                 strent++;
852         }
853
854         vmlog_file_munmap(idxmap,idxlen);
855         vmlog_file_munmap(strmap,strlen);
856         
857         VMLOG_FREE_ARRAY(char,idxnamelen+1,idxfname);
858         VMLOG_FREE_ARRAY(char,strnamelen+1,strfname);
859 }
860
861 /*** public functions ************************************************/
862
863 vmlog_log * vmlog_log_new(const char *prefix,int truncate)
864 {
865         vmlog_log *vml;
866
867         VMLOG_XZNEW(vml,vmlog_log);
868
869         vml->idxfile.fd = -1;
870         vml->strfile.fd = -1;
871         vmlog_hashtable_init(&(vml->stringhash),VMLOG_INITIAL_STRING_HASH_SIZE);
872         vmlog_hashtable_init(&(vml->threadhash),VMLOG_INITIAL_THREAD_HASH_SIZE);
873         
874         if (prefix) {
875                 vml->prefixlen = strlen(prefix);
876                 vml->prefix = vmlog_memdup(prefix,vml->prefixlen+1);
877
878                 vmlog_open_string_files(vml,truncate);
879         }
880
881         return vml;
882 }
883
884 void vmlog_log_free(vmlog_log *vml)
885 {
886         if (!vml)
887                 return;
888
889         VMLOG_FREE_ARRAY(char,vml->prefixlen+1,vml->prefix);
890         vml->prefix = NULL;
891         vml->prefixlen = 0;     
892
893         vmlog_hashtable_free(&(vml->threadhash),vmlog_thread_log_destructor);
894         vmlog_hashtable_free(&(vml->stringhash),vmlog_string_destructor);
895
896         vmlog_file_close(&(vml->idxfile));
897         vmlog_file_close(&(vml->strfile));
898
899         VMLOG_FREE(vmlog_log,vml);
900 }
901
902 static void vmlog_log_enter_tag(vmlog_log *vml,void *threadid,int tag,const char *name,int namelen)
903 {
904         vmlog_thread_log *tlog;
905         int index;
906         vmlog_log_entry logent;
907         
908         assert(vml);
909         assert(name);
910         assert(namelen >= 0);
911
912         VMLOG_LOCK();
913         tlog = vmlog_get_thread_log(vml,threadid);
914         index = vmlog_get_string_index(vml,name,namelen);
915         VMLOG_UNLOCK();
916
917         if (tlog->ignoredepth) {
918                 tlog->ignoredepth++;
919                 return;
920         }
921
922         if (vmlog_is_ignored(vml,index)) {
923                 tlog->ignoredepth++;
924                 return;
925         }
926         
927         logent.tag = tag;
928         logent.index = index;
929         vmlog_thread_log_append(tlog,&logent);
930
931         tlog->seq++;
932 }
933
934 static void vmlog_log_leave_tag(vmlog_log *vml,void *threadid,int tag,const char *name,int namelen)
935 {
936         vmlog_thread_log *tlog;
937         int index;
938         vmlog_log_entry logent;
939         
940         assert(vml);
941         assert(name);
942         assert(namelen >= 0);
943         
944         VMLOG_LOCK();
945         tlog = vmlog_get_thread_log(vml,threadid);
946         index = vmlog_get_string_index(vml,name,namelen);
947         VMLOG_UNLOCK();
948
949         if (tlog->ignoredepth) {
950                 tlog->ignoredepth--;
951                 return;
952         }
953         
954         logent.tag = tag;
955         logent.index = index;
956         vmlog_thread_log_append(tlog,&logent);
957
958         tlog->seq++;
959 }
960
961 void vmlog_log_enter(vmlog_log *vml,void *threadid,const char *name,int namelen)
962 {
963         vmlog_log_enter_tag(vml,threadid,VMLOG_TAG_ENTER,name,namelen);
964 }
965
966 void vmlog_log_rerol(vmlog_log *vml,void *threadid,const char *name,int namelen)
967 {
968         vmlog_log_enter_tag(vml,threadid,VMLOG_TAG_REROL,name,namelen);
969 }
970
971 void vmlog_log_leave(vmlog_log *vml,void *threadid,const char *name,int namelen)
972 {
973         vmlog_log_leave_tag(vml,threadid,VMLOG_TAG_LEAVE,name,namelen);
974 }
975
976 void vmlog_log_unrol(vmlog_log *vml,void *threadid,const char *name,int namelen)
977 {
978         vmlog_log_leave_tag(vml,threadid,VMLOG_TAG_UNROL,name,namelen);
979 }
980
981 void vmlog_log_throw(vmlog_log *vml,void *threadid,const char *name,int namelen)
982 {
983         vmlog_thread_log *tlog;
984         int index;
985         vmlog_log_entry logent;
986         
987         assert(vml);
988         assert(name);
989         assert(namelen >= 0);
990         
991         VMLOG_LOCK();
992         tlog = vmlog_get_thread_log(vml,threadid);
993         index = vmlog_get_string_index(vml,name,namelen);
994         VMLOG_UNLOCK();
995
996         if (tlog->ignoredepth)
997                 return;
998         
999         logent.tag = VMLOG_TAG_THROW;
1000         logent.index = index;
1001         vmlog_thread_log_append(tlog,&logent);
1002
1003         tlog->seq++;
1004 }
1005
1006 void vmlog_log_catch(vmlog_log *vml,void *threadid,const char *name,int namelen)
1007 {
1008         vmlog_thread_log *tlog;
1009         int index;
1010         vmlog_log_entry logent;
1011         
1012         assert(vml);
1013         assert(name);
1014         assert(namelen >= 0);
1015         
1016         VMLOG_LOCK();
1017         tlog = vmlog_get_thread_log(vml,threadid);
1018         index = vmlog_get_string_index(vml,name,namelen);
1019         VMLOG_UNLOCK();
1020
1021         if (tlog->ignoredepth)
1022                 return;
1023         
1024         logent.tag = VMLOG_TAG_CATCH;
1025         logent.index = index;
1026         vmlog_thread_log_append(tlog,&logent);
1027
1028         tlog->seq++;
1029 }
1030
1031 void vmlog_log_unwnd(vmlog_log *vml,void *threadid,const char *name,int namelen)
1032 {
1033         vmlog_thread_log *tlog;
1034         int index;
1035         vmlog_log_entry logent;
1036         
1037         assert(vml);
1038         assert(name);
1039         assert(namelen >= 0);
1040         
1041         VMLOG_LOCK();
1042         tlog = vmlog_get_thread_log(vml,threadid);
1043         index = vmlog_get_string_index(vml,name,namelen);
1044         VMLOG_UNLOCK();
1045
1046         if (tlog->ignoredepth) {
1047                 tlog->ignoredepth--;
1048                 return;
1049         }
1050         
1051         logent.tag = VMLOG_TAG_UNWND;
1052         logent.index = index;
1053         vmlog_thread_log_append(tlog,&logent);
1054
1055         tlog->seq++;
1056 }
1057
1058 void vmlog_log_signl(vmlog_log *vml,void *threadid,const char *name,int namelen)
1059 {
1060         vmlog_thread_log *tlog;
1061         int index;
1062         vmlog_log_entry logent;
1063         
1064         assert(vml);
1065         assert(name);
1066         assert(namelen >= 0);
1067         
1068         VMLOG_LOCK();
1069         tlog = vmlog_get_thread_log(vml,threadid);
1070         index = vmlog_get_string_index(vml,name,namelen);
1071         VMLOG_UNLOCK();
1072
1073         logent.tag = VMLOG_TAG_SIGNL;
1074         logent.index = index;
1075         vmlog_thread_log_append(tlog,&logent);
1076
1077         tlog->seq++;
1078 }
1079
1080 void vmlog_log_load_ignorelist(vmlog_log *vml,const char *prefix)
1081 {
1082         assert(vml);
1083         assert(prefix);
1084
1085         vmlog_load_stringhash(vml,prefix);
1086         vml->ignorelistlen = vml->stringhash.nentries;
1087 }
1088
1089 /*** ring buffer functions *******************************************/
1090
1091 static void vmlog_ringbuf_visualize(vmlog_ringbuf *ring)
1092 {
1093         int i;
1094
1095         fprintf(stdout,"vmlog_ringbuf %p: bufsize=%d availbefore=%d availafter=%d\n",
1096                         (void*)ring,ring->bufsize,
1097                         ring->debug_availbefore,ring->debug_availafter);
1098
1099         for (i=0; i<=ring->bufsize; ++i) {
1100                 if (i == ring->bufsize) {
1101                         fprintf(stdout,"%3d: xxxxxxxxxxxxx",i);
1102                 }
1103                 else {
1104                         fprintf(stdout,"%3d: %2d %10d",i,ring->buf[i].tag,ring->buf[i].index);
1105                 }
1106                 if (ring->start - ring->buf == i) fputs(" start",stdout);
1107                 if (ring->cur   - ring->buf == i) fputs(" cur",stdout);
1108                 if (ring->end   - ring->buf == i) fputs(" end",stdout);
1109                 if (ring->cur   - ring->buf == i) fprintf(stdout," (" VMLOG_SEQ_FMT ")",ring->seq);
1110                 fputc('\n',stdout);
1111         }       
1112 }
1113
1114 static void vmlog_ringbuf_check_invariants(vmlog_ringbuf *ring)
1115 {
1116         /* vmlog_ringbuf_visualize(ring); */
1117         
1118         assert(ring);
1119
1120         assert(ring->bufsize > 0);
1121         assert(ring->bufend == ring->buf + ring->bufsize);
1122
1123         assert(ring->start >= ring->buf && ring->start < ring->bufend);
1124         assert((ring->end > ring->buf && ring->end <= ring->bufend)
1125                         ||
1126                (ring->end == ring->start));
1127
1128         assert(ring->debug_availbefore >= 0);
1129         assert(ring->debug_availafter >= 0);
1130         assert(ring->debug_availbefore + ring->debug_availafter <= ring->bufsize);
1131
1132         /* ring->cur can point to any present  */
1133         /* element (#) or be equal to ring->end*/
1134
1135         if (ring->end >= ring->start) {
1136                 /* case A: ring->end >= ring->start    */
1137                 /*                                     */
1138                 /* -------#############-----------     */
1139                 /* ^      ^            ^          ^    */
1140                 /* buf    start        end     bufend  */
1141
1142                 assert(ring->cur >= ring->start && ring->cur <= ring->end);
1143
1144                 assert(ring->cur - ring->start == ring->debug_availbefore);
1145                 assert(ring->end - ring->cur   == ring->debug_availafter);
1146         }
1147         else {
1148                 /* case B: ring->end < ring->start     */
1149                 /*                                     */
1150                 /* #######------------############     */
1151                 /* ^      ^           ^           ^    */
1152                 /* buf    end        start     bufend  */
1153
1154                 assert((ring->cur >= ring->start && ring->cur < ring->bufend)
1155                                 ||
1156                        (ring->cur >= ring->buf && ring->cur <= ring->end));
1157
1158                 if (ring->cur >= ring->start) {
1159                         assert(ring->cur - ring->start == ring->debug_availbefore);
1160                         assert((ring->bufend - ring->cur) + (ring->end - ring->buf) 
1161                                         == ring->debug_availafter);
1162                 }
1163                 else {
1164                         assert((ring->bufend - ring->start) + (ring->cur - ring->buf) 
1165                                         == ring->debug_availbefore);
1166                         assert(ring->end - ring->cur == ring->debug_availafter);
1167                 }
1168         }
1169 }
1170
1171 vmlog_ringbuf * vmlog_ringbuf_new(const char *fname,int bufsize)
1172 {
1173         vmlog_ringbuf *ring;
1174
1175         assert(bufsize > 0);
1176
1177         VMLOG_XZNEW(ring,vmlog_ringbuf);
1178         VMLOG_XZNEW_ARRAY(ring->buf,vmlog_log_entry,bufsize);
1179
1180         ring->bufsize = bufsize;
1181         ring->bufend = ring->buf + bufsize;
1182         ring->start = ring->buf;
1183         ring->end = ring->buf;
1184         ring->cur = ring->buf;
1185
1186         vmlog_file_open(&(ring->file),fname,vmlogRead);
1187         vmlog_file_stat(&(ring->file));
1188
1189         vmlog_ringbuf_check_invariants(ring);
1190
1191         return ring;
1192 }
1193
1194 void vmlog_ringbuf_free(vmlog_ringbuf *ring)
1195 {
1196         if (!ring)
1197                 return;
1198
1199         vmlog_ringbuf_check_invariants(ring);
1200
1201         vmlog_file_close(&(ring->file));
1202
1203         VMLOG_FREE_ARRAY(vmlog_log_entry,ring->bufsize,ring->buf);
1204         VMLOG_FREE(vmlog_ringbuf,ring);
1205 }
1206
1207 static int vmlog_ringbuf_read(vmlog_ringbuf *ring,vmlog_log_entry *buf,
1208                 vmlog_seq_t seq,int n)
1209 {
1210         int r;
1211         vmlog_fofs_t ofs;
1212
1213         ofs = seq * sizeof(vmlog_log_entry);
1214         if (ofs != ring->file.ofs)
1215                 vmlog_file_seek(&(ring->file),ofs);
1216
1217         do {
1218                 /* fprintf(stdout,"vmlog_ringbuf_read(%p,%d,%d)\n",
1219                                 (void*)ring,buf-ring->buf,n); */
1220                 
1221                 r = read(ring->file.fd,buf,n * sizeof(vmlog_log_entry));
1222         } while (r == -1 && errno == EINTR);
1223
1224         if (r == -1)
1225                 vmlog_die("reading from file: %s: %s",ring->file.fname,strerror(errno));
1226
1227         ring->file.ofs += r;
1228
1229         if (r % sizeof(vmlog_log_entry) != 0) {
1230                 /* XXX */
1231                 vmlog_warn("partial log entry read from file: %s",ring->file.fname);
1232         }
1233
1234         return r / sizeof(vmlog_log_entry);
1235 }
1236
1237 static int vmlog_ringbuf_fill_forward(vmlog_ringbuf *ring,vmlog_log_entry *fillstart,
1238                                       vmlog_log_entry *fillend,vmlog_seq_t seq,int len)
1239 {
1240         int space;
1241         int n;
1242         int read;
1243         vmlog_log_entry *oldend;
1244
1245 #if 0
1246         fprintf(stdout,"vmlog_ringbuf_fill_forward(%p,%d,%d," VMLOG_SEQ_FMT ",%d)\n",
1247                         (void*)ring,fillstart-ring->buf,fillend-ring->buf,seq,len);
1248 #endif
1249
1250         vmlog_ringbuf_check_invariants(ring);
1251
1252         space = fillend - fillstart;
1253         n = (len <= space) ? len : space;
1254
1255         if (n <= 0)
1256                 return 0;
1257
1258         read = vmlog_ringbuf_read(ring,fillstart,seq,n);
1259         if (!read)
1260                 return 0;
1261
1262         oldend = ring->end;
1263         ring->end = fillstart + read;
1264         ring->debug_availafter += read;
1265
1266         if (ring->cur == ring->bufend)
1267                 ring->cur = ring->buf;
1268
1269         if (ring->start >= fillstart && ring->start != oldend) {
1270                 /* check if old entries have been overwritten */
1271                 if (ring->start <= ring->end) {
1272                         ring->debug_availbefore -=
1273                                 ring->end - ring->start + 1;
1274                         ring->start = ring->end + 1;
1275                         if (ring->start >= ring->bufend) {
1276                                 ring->start = ring->buf;
1277                                 ring->debug_availbefore = ring->cur - ring->start;
1278                         }
1279                 }
1280         }
1281
1282         vmlog_ringbuf_check_invariants(ring);
1283
1284         return read;
1285 }
1286         
1287 static int vmlog_ringbuf_fill_backward(vmlog_ringbuf *ring,vmlog_log_entry *fillstart,
1288                                        vmlog_log_entry *fillend,vmlog_seq_t seq,int len)
1289 {
1290         int space;
1291         int n;
1292         int read;
1293         vmlog_log_entry *oldstart;
1294         vmlog_log_entry *oldend;
1295
1296 #if 0
1297         fprintf(stdout,"vmlog_ringbuf_fill_backward(%p,%d,%d," VMLOG_SEQ_FMT ",%d)\n",
1298                         (void*)ring,fillstart-ring->buf,fillend-ring->buf,seq,len);
1299 #endif
1300
1301         vmlog_ringbuf_check_invariants(ring);
1302
1303         space = fillend - fillstart;
1304         n = (len <= space) ? len : space;
1305
1306         if (n <= 0)
1307                 return 0;
1308
1309         seq += space - n;
1310         fillstart += space - n;
1311
1312         read = vmlog_ringbuf_read(ring,fillstart,seq,n);
1313         if (read != n)
1314                 vmlog_die("could not read backward in file: %s: %s",
1315                         ring->file.fname,strerror(errno));
1316
1317         oldstart = ring->start;
1318         ring->start = fillstart;
1319         ring->debug_availbefore += read;
1320
1321         oldend = ring->end;
1322         if (ring->end <= fillend && ring->end != oldstart) {
1323                 /* check if old entries have been overwritten */
1324                 if (ring->start <= ring->end) {
1325                         ring->debug_availafter -=
1326                                 ring->end - ring->start + 1;
1327                         ring->end = ring->start - 1;
1328
1329                         if (ring->end <= ring->buf) {
1330                                 ring->end = ring->bufend;
1331                                 if (ring->cur == ring->buf && ring->end == ring->bufend)
1332                                         ring->cur = ring->bufend;
1333                                 ring->debug_availafter = ring->end - ring->cur;
1334                         }
1335                 }
1336         }
1337
1338         if (ring->end == ring->buf) {
1339                 assert(oldstart == oldend);
1340                 ring->end = ring->bufend;
1341         }
1342
1343         if (ring->cur == ring->buf && ring->end == ring->bufend)
1344                 ring->cur = ring->bufend;
1345
1346         vmlog_ringbuf_check_invariants(ring);
1347
1348         return read;
1349 }
1350         
1351 int vmlog_ringbuf_fill(vmlog_ringbuf *ring,int len)
1352 {
1353         int count;
1354         int read;
1355         vmlog_log_entry *fillend;
1356         vmlog_seq_t seq;
1357         
1358         assert(ring);
1359
1360         if (!len)
1361                 return 0 /*XXX*/;
1362
1363         count = 0;
1364
1365         vmlog_ringbuf_check_invariants(ring);
1366         
1367         if (len > 0) {
1368                 if (ring->end >= ring->cur) {
1369                         /* case A'1: ring->end >= ring->start  */
1370                         /*                                     */
1371                         /*                     vvvvvvvvvvv     */
1372                         /* ------OOOOO#########-----------     */
1373                         /* ^     ^    ^        ^          ^    */
1374                         /* buf  start cur      end     bufend  */
1375
1376                         /* case B'1: ring->end < ring->start   */
1377                         /*                                     */
1378                         /*                     vvvvvvvvvvv     */
1379                         /* OOOOOOOOOOO#########----OOOOOOO     */
1380                         /* ^          ^        ^   ^      ^    */
1381                         /* buf        cur      end st. bufend  */
1382
1383                         /* fill space at end of buf */
1384                         seq = ring->seq + (ring->end - ring->cur);
1385                         read = vmlog_ringbuf_fill_forward(ring,ring->end,ring->bufend,
1386                                         seq,len);
1387                         count += read;
1388                         len -= read;
1389
1390                         if (ring->end != ring->bufend)
1391                                 goto no_more_entries;
1392
1393                         /* case A'1: ring->end >= ring->start  */
1394                         /*                                     */
1395                         /* vvvvvvvvvv                          */
1396                         /* ------OOOOO####################     */
1397                         /* ^     ^    ^                   ^    */
1398                         /* buf  start cur         end==bufend  */
1399
1400                         /* case B'1: ring->end < ring->start   */
1401                         /*                                     */
1402                         /* vvvvvvvvvv                          */
1403                         /* OOOOOOOOOOO####################     */
1404                         /* ^          ^                   ^    */
1405                         /* buf==start cur         end==bufend  */
1406
1407                         /* fill space at beg of buf */
1408                         seq = ring->seq + (ring->end - ring->cur);
1409                         fillend = (ring->cur == ring->bufend) ? ring->bufend : (ring->cur - 1);
1410                         read = vmlog_ringbuf_fill_forward(ring,ring->buf,fillend,
1411                                         seq,len);
1412                         count += read;
1413                         len -= read;
1414                 }
1415                 else {
1416                         /* ring->end < ring->cur */
1417
1418                         /* no case A'2 */
1419                         assert(ring->end < ring->start);
1420
1421                         /* case B'2: ring->end < ring->start   */
1422                         /*                                     */
1423                         /*      vvvvvvvvvvv                    */
1424                         /* #####------OOOOOO##############     */
1425                         /* ^    ^     ^     ^             ^    */
1426                         /* buf  end   start cur        bufend  */
1427
1428                         /* fill space in middle of buf */
1429                         seq = ring->seq + (ring->bufend - ring->cur) + (ring->end - ring->buf);
1430                         read = vmlog_ringbuf_fill_forward(ring,ring->end,ring->cur - 1,
1431                                         seq,len);
1432                         count += read;
1433                         len -= read;
1434                 }
1435         }
1436         else {
1437                 len = -len;
1438
1439                 if (len > ring->seq)
1440                         len = ring->seq;
1441
1442                 if (ring->start <= ring->cur) {
1443                         /* case A'1: ring->end >= ring->start  */
1444                         /*                                     */
1445                         /* vvvvvv                              */
1446                         /* ------#####OOOOOOOOO-----------     */
1447                         /* ^     ^    ^        ^          ^    */
1448                         /* buf  start cur      end     bufend  */
1449
1450                         /* case B'2: ring->end < ring->start   */
1451                         /*                                     */
1452                         /* vvvvvvvvvvv                         */
1453                         /* OOOOO------######OOOOOOOOOOOOOO     */
1454                         /* ^    ^     ^     ^             ^    */
1455                         /* buf  end   start cur        bufend  */
1456
1457                         /* fill space at beg of buf */
1458                         seq = ring->seq - (ring->cur - ring->buf);
1459                         read = vmlog_ringbuf_fill_backward(ring,ring->buf,ring->start,
1460                                         seq,len);
1461                         count += read;
1462                         len -= read;
1463
1464                         if (ring->start != ring->buf)
1465                                 goto no_more_entries;
1466
1467                         /* case A'1: ring->end >= ring->start  */
1468                         /*                                     */
1469                         /*             vvvvvvvvvvvvvvvvvvv     */
1470                         /* ###########OOOOOOOOO-----------     */
1471                         /* ^          ^        ^          ^    */
1472                         /* buf=start  cur      end     bufend  */
1473
1474                         /* case B'2: ring->end < ring->start   */
1475                         /*                                     */
1476                         /*                   vvvvvvvvvvvvv     */
1477                         /* #################OOOOOOOOOOOOOO     */
1478                         /* ^                ^             ^    */
1479                         /* buf=start        cur    end=bufend  */
1480
1481                         /* fill space at end of buf */
1482                         seq -= (ring->bufend - ring->cur - 1);
1483                         read = vmlog_ringbuf_fill_backward(ring,ring->cur+1,ring->bufend,
1484                                         seq,len);
1485                         count += read;
1486                         len -= read;
1487                 }
1488                 else {
1489                         /* ring->start > ring->cur */
1490
1491                         /* case B'1: ring->end < ring->start   */
1492                         /*                                     */
1493                         /*             vvvvvvvvvvvv            */
1494                         /* ###########OOOOOOOOO----#######     */
1495                         /* ^          ^        ^   ^      ^    */
1496                         /* buf        cur      end st. bufend  */
1497
1498                         /* no case A'2 */
1499                         assert(ring->end < ring->start);
1500
1501                         /* fill space in middle of buf */
1502                         seq = ring->seq - (ring->cur - ring->buf) - (ring->bufend - ring->cur - 1);
1503                         read = vmlog_ringbuf_fill_backward(ring,ring->cur + 1,ring->start,
1504                                         seq,len);
1505                         count += read;
1506                         len -= read;
1507                 }
1508         }
1509
1510 no_more_entries:
1511         vmlog_ringbuf_check_invariants(ring);
1512         
1513         return count;
1514 }
1515
1516 static void vmlog_ringbuf_reset(vmlog_ringbuf *ring)
1517 {
1518         ring->start = ring->buf;
1519         ring->cur = ring->buf;
1520         ring->end = ring->buf;
1521
1522         ring->debug_availbefore = 0;
1523         ring->debug_availafter = 0;
1524
1525         vmlog_ringbuf_check_invariants(ring);
1526 }
1527
1528 static int vmlog_ringbuf_advance(vmlog_ringbuf *ring,int diff)
1529 {
1530         int space;
1531         
1532         if (diff > 0) {
1533                 if (ring->end >= ring->start) {
1534                         /* case A */
1535 advance_cur_to_end:
1536                         space = ring->end - ring->cur;
1537
1538                         if (space <= 0)
1539                                 return 0;
1540
1541                         if (space < diff)
1542                                 diff = space;
1543
1544 simple_advance:
1545                         ring->cur += diff;
1546                         ring->seq += diff;
1547                         ring->debug_availbefore += diff;
1548                         ring->debug_availafter -= diff;
1549                         return diff;
1550                 }
1551                 else {
1552                         /* case B */
1553                         if (ring->end >= ring->cur)
1554                                 goto advance_cur_to_end;
1555
1556                         space = ring->bufend - ring->cur;
1557                         if (space > diff)
1558                                 goto simple_advance;
1559
1560                         ring->cur = ring->buf - space;
1561                         goto advance_cur_to_end;
1562                 }
1563         }
1564         else if (diff < 0) {
1565                 if (ring->end >= ring->start) {
1566                         /* case A */
1567 advance_cur_to_start:
1568                         space = ring->cur - ring->start;
1569
1570                         if (space <= 0)
1571                                 return 0;
1572
1573                         if (-space > diff)
1574                                 diff = -space;
1575                         goto simple_advance;
1576                 }
1577                 else {
1578                         /* case B */
1579                         if (ring->cur >= ring->start)
1580                                 goto advance_cur_to_start;
1581
1582                         space = ring->cur - ring->buf;
1583                         if (space >= -diff)
1584                                 goto simple_advance;
1585
1586                         ring->cur = ring->bufend + space;
1587                         goto advance_cur_to_start;
1588                 }
1589         }
1590         else {
1591                 return 0;
1592         }
1593 }
1594
1595 void vmlog_ringbuf_seek(vmlog_ringbuf *ring,vmlog_seq_t seq)
1596 {
1597         vmlog_seq_t diff;
1598         
1599         vmlog_ringbuf_check_invariants(ring);
1600
1601         diff = seq - ring->seq;
1602         if (abs(diff) < ring->bufsize)
1603                 diff -= vmlog_ringbuf_advance(ring,(int)diff);
1604
1605         if (!diff)
1606                 return;
1607
1608         vmlog_ringbuf_reset(ring);
1609         vmlog_file_seek(&(ring->file),seq * sizeof(vmlog_log_entry));
1610         ring->seq = seq;
1611
1612         vmlog_ringbuf_check_invariants(ring);
1613 }
1614
1615 vmlog_log_entry * vmlog_ringbuf_next(vmlog_ringbuf *ring,int prefetch)
1616 {
1617         vmlog_ringbuf_check_invariants(ring);
1618
1619         while (1) {
1620                 if (ring->end >= ring->start) {
1621                         /* case A */
1622                         if (ring->cur < ring->end) {
1623                                 ring->debug_availafter--;
1624                                 ring->debug_availbefore++;
1625                                 ring->seq++;
1626                                 return ring->cur++;
1627                         }
1628                 }
1629                 else {
1630                         /* case B */
1631                         if (ring->end >= ring->cur) {
1632                                 if (ring->cur < ring->end) {
1633                                         ring->debug_availafter--;
1634                                         ring->debug_availbefore++;
1635                                         ring->seq++;
1636                                         return ring->cur++;
1637                                 }
1638                         }
1639                         else {
1640                                 if (ring->cur < ring->bufend) {
1641                                         vmlog_log_entry *r;
1642
1643                                         r = ring->cur;
1644                                         ring->seq++;
1645                                         if (++ring->cur == ring->bufend)
1646                                                 ring->cur = ring->buf;
1647                                         ring->debug_availafter--;
1648                                         ring->debug_availbefore++;
1649                                         return r;
1650                                 }
1651                         }
1652                 }       
1653
1654                 if (!vmlog_ringbuf_fill(ring,prefetch))
1655                         return NULL;
1656         }
1657
1658         assert(0); /* NOT REACHED */
1659         return NULL;
1660 }
1661
1662 vmlog_log_entry * vmlog_ringbuf_prev(vmlog_ringbuf *ring,int prefetch)
1663 {
1664         vmlog_ringbuf_check_invariants(ring);
1665
1666         while (1) {
1667                 if (ring->end >= ring->start) {
1668                         /* case A */
1669                         if (ring->cur > ring->start) {
1670                                 ring->debug_availafter++;
1671                                 ring->debug_availbefore--;
1672                                 ring->seq--;
1673                                 return --ring->cur;
1674                         }
1675                 }
1676                 else {
1677                         /* case B */
1678                         if (ring->cur >= ring->start) {
1679                                 if (ring->cur > ring->start) {
1680                                         ring->debug_availafter++;
1681                                         ring->debug_availbefore--;
1682                                         ring->seq--;
1683                                         return --ring->cur;
1684                                 }
1685                         }
1686                         else {
1687                                 if (--ring->cur < ring->buf)
1688                                         ring->cur = ring->bufend - 1;
1689                                 ring->seq--;
1690                                 ring->debug_availafter++;
1691                                 ring->debug_availbefore--;
1692                                 return ring->cur;
1693                         }
1694                 }       
1695
1696                 if (!vmlog_ringbuf_fill(ring,-prefetch))
1697                         return NULL;
1698         }
1699
1700         assert(0); /* NOT REACHED */
1701         return NULL;
1702 }
1703
1704 /*** option parsing **************************************************/
1705
1706 int vmlog_opt_parse_seq(const char *arg,int len,vmlog_seq_t *seq)
1707 {
1708         char *buf;
1709         char *endptr;
1710         int r;
1711         
1712         assert(arg);
1713
1714         if (len < 1)
1715                 return 0;
1716
1717         buf = vmlog_strdup(arg,len);
1718         *seq = strtoll(buf,&endptr,10);
1719
1720         r = (endptr[0] == 0);
1721
1722         VMLOG_FREE_ARRAY(char,len+1,buf);
1723         return r;
1724 }
1725
1726 int vmlog_opt_parse_range(const char *arg,vmlog_seq_t *start,vmlog_seq_t *end)
1727 {
1728         const char *sep;
1729         int len;
1730
1731         sep = strchr(arg,':');
1732         if (!sep) {
1733                 len = strlen(arg);
1734                 if (!vmlog_opt_parse_seq(arg,len,start))
1735                         return 0;
1736                 *end = *start;
1737                 return 1;
1738         }
1739
1740         len = sep - arg;
1741         if (!len) {
1742                 *start = 0;
1743         }
1744         else {
1745                 if (!vmlog_opt_parse_seq(arg,len,start))
1746                         return 0;
1747         }
1748
1749         len = strlen(arg) - len - 1;
1750         if (!len) {
1751                 *end = VMLOG_SEQ_MAX;
1752         }
1753         else {
1754                 if (!vmlog_opt_parse_seq(sep+1,len,end))
1755                         return 0;
1756         }
1757         return 1;
1758 }
1759
1760 static int vmlog_opt_parse_one_option(vmlog_options *opts, const char *arg, const char *nextarg)
1761 {
1762         int eat;
1763
1764         if (strncmp(arg,"-vmlog:",7) != 0) {
1765                 return 0;
1766         }
1767
1768         /* a vmlog option */
1769
1770         eat = 1;
1771         if (strcmp(arg,"-vmlog:prefix") == 0) {
1772                 if (!nextarg)
1773                         vmlog_die("expected a prefix after -vmlog:prefix");
1774                 opts->prefix = vmlog_strdup(nextarg,strlen(nextarg));
1775                 eat++;
1776         }
1777         else if (strcmp(arg,"-vmlog:strings") == 0) {
1778                 if (!nextarg)
1779                         vmlog_die("expected a prefix after -vmlog:strings");
1780                 opts->stringprefix = vmlog_strdup(nextarg,strlen(nextarg));
1781                 eat++;
1782         }
1783         else if (strcmp(arg,"-vmlog:ignore") == 0) {
1784                 if (!nextarg)
1785                         vmlog_die("expected a prefix after -vmlog:ignore");
1786                 opts->ignoreprefix = vmlog_strdup(nextarg,strlen(nextarg));
1787                 eat++;
1788         }
1789         else {
1790                 vmlog_die("unknown -vmlog:... option: %s",arg);
1791         }
1792
1793         return eat;
1794 }
1795
1796 vmlog_options *vmlog_opt_parse_cmd_line(int *pargc,char **argv)
1797 {
1798         int i;
1799         const char *arg;
1800         vmlog_options *opts;
1801         int eat;
1802         int left;
1803
1804         assert(pargc);
1805
1806         VMLOG_XZNEW(opts,vmlog_options);
1807
1808         if (*pargc && argv[0])
1809                 opts->progname = vmlog_strdup(argv[0],strlen(argv[0]));
1810
1811         i = 1;
1812         while (i < *pargc) {
1813                 arg = argv[i];
1814                 
1815                 left = *pargc - i - 1;
1816
1817                 eat = vmlog_opt_parse_one_option(opts,arg,
1818                                 (left) ? argv[i+1] : NULL);
1819
1820                 if (eat == 0) {
1821                         i++;
1822                         continue;
1823                 }
1824
1825                 /* remove the option from the command line */
1826                 
1827                 memmove(argv + i,argv + i + eat,sizeof(char*) * (*pargc - (i+eat)));
1828                 *pargc -= eat;
1829         }
1830
1831         return opts;
1832 }
1833
1834 vmlog_options *vmlog_opt_parse_vmargs(JavaVMInitArgs *vmargs)
1835 {
1836         int i;
1837         const char *arg;
1838         vmlog_options *opts;
1839         int eat;
1840
1841         assert(vmargs);
1842
1843         VMLOG_XZNEW(opts,vmlog_options);
1844
1845         i = 0;
1846         while (i < vmargs->nOptions) {
1847                 arg = vmargs->options[i].optionString;
1848
1849                 eat = vmlog_opt_parse_one_option(opts,arg,
1850                                 (i+1 < vmargs->nOptions) ? vmargs->options[i+1].optionString : NULL);
1851
1852                 if (eat == 0) {
1853                         i++;
1854                         continue;
1855                 }
1856                 
1857                 /* remove the option from the command line */
1858                 
1859                 memmove(vmargs->options + i,vmargs->options + i + eat,
1860                                 sizeof(JavaVMOption) * (vmargs->nOptions - (i+eat)));
1861                 vmargs->nOptions -= eat;
1862         }
1863
1864         return opts;
1865 }
1866
1867 void vmlog_opt_free(vmlog_options *opts)
1868 {
1869         if (!opts)
1870                 return;
1871
1872         if (opts->prefix)
1873                 VMLOG_FREE_ARRAY(char,strlen(opts->prefix)+1,opts->prefix);
1874         if (opts->stringprefix)
1875                 VMLOG_FREE_ARRAY(char,strlen(opts->stringprefix)+1,opts->stringprefix);
1876         if (opts->ignoreprefix)
1877                 VMLOG_FREE_ARRAY(char,strlen(opts->ignoreprefix)+1,opts->ignoreprefix);
1878
1879         VMLOG_FREE(vmlog_options,opts);
1880 }
1881
1882 /* vim: noet ts=8 sw=8
1883  */