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