1 /* vmlog - high-speed logging for free VMs */
2 /* Copyright (C) 2006 Edwin Steiner <edwin.steiner@gmx.net> */
4 /* This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
33 /*** default macros **************************************************/
36 #define VMLOG_LOCK(vml)
37 #define VMLOG_UNLOCK(vml)
40 /* #define VMLOG_ENDIAN_CONVERT_WRITE */
41 /* #define VMLOG_HOST_LITTLE_ENDIAN */
45 #if defined(WORDS_BIGENDIAN)
46 # define VMLOG_ENDIAN_CONVERT_WRITE
47 # undef VMLOG_HOST_LITTLE_ENDIAN
49 # undef VMLOG_ENDIAN_CONVERT_WRITE
50 # define VMLOG_HOST_LITTLE_ENDIAN
54 /*** constants *******************************************************/
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 */
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 */
64 /*** types ***********************************************************/
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. */
70 #ifndef _LARGEFILE64_SOURCE
71 typedef long long off64_t;
72 off64_t lseek64(int fd, off64_t offset, int whence);
75 /*** tag definitions *************************************************/
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 },
90 /*** global variables ************************************************/
92 static char *vmlog_progname = "vmlog";
94 /*** prototypes ******************************************************/
96 static void *vmlog_memdup(const void *m,int len);
98 /*** error reporting *************************************************/
100 void vmlog_set_progname(const char *progname)
103 progname = "vmlog (progname == NULL)";
106 vmlog_progname = vmlog_memdup(progname,strlen(progname)+1);
109 void vmlog_die(const char *fmt,...)
113 fputs(vmlog_progname,stderr);
114 fputs(": error: ",stderr);
116 vfprintf(stderr,fmt,ap);
122 void vmlog_warn(const char *fmt,...)
126 fputs(vmlog_progname,stderr);
127 fputs(": warning: ",stderr);
129 vfprintf(stderr,fmt,ap);
134 void vmlog_die_usage(const char *usage,int error)
138 fputs(usage,(error) ? stderr : stdout);
139 exit((error) ? 1 : 0);
142 /*** utility functions ***********************************************/
144 static void *vmlog_memdup(const void *data,int len)
148 p = VMLOG_NEW_ARRAY(char,len);
155 static void *vmlog_strdup(const void *data,int len)
159 p = VMLOG_NEW_ARRAY(char,len+1);
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,
180 len = alen + blen + clen + dlen;
184 p = VMLOG_NEW_ARRAY(char,len+1);
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;
195 char *vmlog_concat3(const char *a,const char *b,const char *c,int *plen)
197 int len,lena,lenb,lenc;
209 len = lena + lenb + lenc;
213 p = VMLOG_NEW_ARRAY(char,len+1);
215 memcpy(pp,a,lena); pp += lena;
216 memcpy(pp,b,lenb); pp += lenb;
217 memcpy(pp,c,lenc); pp += lenc;
223 /*** file ops ********************************************************/
225 void vmlog_file_open(vmlog_file *file,const char *fname,vmlog_fmode fmode)
239 flags = O_WRONLY | O_APPEND | O_CREAT;
242 case vmlogTruncateAppend:
243 flags = O_WRONLY | O_APPEND | O_CREAT | O_TRUNC;
247 vmlog_die("unknown fmode for opening file: %s: %d",
251 r = open(fname,flags,0644);
253 vmlog_die("could not open file: %s: %s",fname,strerror(errno));
256 file->fnamelen = strlen(fname);
257 file->fname = vmlog_memdup(fname,file->fnamelen+1);
259 r = fstat(file->fd,&st);
261 vmlog_die("could not stat file: %s: %s",fname,strerror(errno));
263 file->ofs = (fmode == vmlogRead) ? 0 : st.st_size;
266 void vmlog_file_close(vmlog_file *file)
275 VMLOG_FREE_ARRAY(char,file->fnamelen+1,file->fname);
280 void vmlog_file_append(vmlog_file *file,const void *data,int len)
290 r = write(file->fd,data,len);
291 } while (r == -1 && errno == EINTR);
294 vmlog_die("could not write to file: %s: %s",file->fname,strerror(errno));
298 vmlog_die("could not write all data to file: %s",file->fname);
304 void vmlog_file_stat(vmlog_file *file)
309 r = fstat(file->fd,&st);
311 vmlog_die("could not stat file: %s: %s",file->fname,strerror(errno));
313 file->size = st.st_size;
316 void * vmlog_file_mmap(const char *fname,int *plen)
323 fd = open(fname,O_RDONLY);
325 vmlog_die("could not open file: %s: %s",fname,strerror(errno));
329 vmlog_die("could not stat file: %s: %s",fname,strerror(errno));
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));
350 void vmlog_file_munmap(void *m,int len)
357 vmlog_warn("could not munmap file: %s",strerror(errno));
364 void vmlog_file_seek(vmlog_file *file,vmlog_fofs_t ofs)
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));
375 /*** string storage **************************************************/
377 static void vmlog_add_string(vmlog_log *vml,const char *data,int len)
379 vmlog_string_entry strent;
380 #if defined(VMLOG_ENDIAN_CONVERT_WRITE)
386 if (vml->strfile.fd == -1)
388 if (vml->idxfile.fd == -1)
391 strent.ofs = vml->strfile.ofs;
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);
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);
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);
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);
426 #endif /* defined(VMLOG_ENDIAN_CONVERT_WRITE) */
428 vmlog_file_append(&(vml->strfile),data,len);
429 vmlog_file_append(&(vml->idxfile),&strent,sizeof(vmlog_string_entry));
432 /*** index functions *************************************************/
434 static int vmlog_is_ignored(vmlog_log *vml,int index)
436 return (index < vml->ignorelistlen);
439 /*** thread log functions ********************************************/
441 static void vmlog_thread_log_alloc_logbuf(vmlog_thread_log *tlog,int cap)
447 VMLOG_XZNEW_ARRAY(tlog->logbuf,vmlog_log_entry,cap);
452 tlog->logbufptr = tlog->logbuf;
453 tlog->logbufend = tlog->logbuf + cap;
454 tlog->logbufcap = cap;
457 static void vmlog_thread_log_flush(vmlog_thread_log *tlog)
460 assert(tlog->logbuf);
462 vmlog_file_append(&(tlog->logfile),tlog->logbuf,
463 (tlog->logbufptr - tlog->logbuf) * sizeof(vmlog_log_entry));
465 tlog->logbufptr = tlog->logbuf;
468 static void vmlog_thread_log_realloc_frames(vmlog_thread_log *tlog,int cap)
470 vmlog_frame *oldframes;
473 assert(cap >= tlog->depth);
475 oldframes = tlog->frames;
478 VMLOG_XZNEW_ARRAY(tlog->frames,vmlog_frame,cap);
486 memcpy(tlog->frames,oldframes,sizeof(vmlog_frame) * tlog->depth);
488 VMLOG_FREE_ARRAY(vmlog_frame,tlog->framescap,oldframes);
490 tlog->framescap = cap;
493 void vmlog_thread_log_append(vmlog_thread_log *tlog,vmlog_log_entry *logent)
495 #if defined(VMLOG_ENDIAN_CONVERT_WRITE)
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);
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);
508 #endif /* defined(VMLOG_ENDIAN_CONVERT_WRITE) */
509 if (tlog->logbufptr) {
510 if (tlog->logbufptr == tlog->logbufend) {
511 vmlog_thread_log_flush(tlog);
513 *tlog->logbufptr++ = *logent;
516 vmlog_file_append(&(tlog->logfile),logent,sizeof(vmlog_log_entry));
520 #define VMLOG_INT2STR_BUFFER 20
522 vmlog_thread_log *vmlog_thread_log_new(vmlog_log *vml,void *threadid,int index)
524 vmlog_thread_log *tlog;
525 char buf[VMLOG_INT2STR_BUFFER];
530 VMLOG_XZNEW(tlog,vmlog_thread_log);
532 tlog->threadid = threadid;
533 tlog->threadidx = index;
534 tlog->logfile.fd = -1;
536 vmlog_thread_log_realloc_frames(tlog,VMLOG_INITIAL_FRAMES_CAPACITY);
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,
547 vmlog_file_open(&(tlog->logfile),name,vmlogTruncateAppend);
548 VMLOG_FREE_ARRAY(char,namelen+1,name);
554 void vmlog_thread_log_free(vmlog_thread_log *tlog)
560 vmlog_thread_log_flush(tlog);
562 vmlog_file_close(&(tlog->logfile));
565 VMLOG_FREE_ARRAY(vmlog_frame,tlog->framescap,tlog->frames);
567 VMLOG_FREE(vmlog_thread_log,tlog);
570 vmlog_frame * vmlog_thread_log_enter(vmlog_thread_log *tlog,int index,vmlog_seq_t seq)
574 if (tlog->depth < 0) {
575 vmlog_warn("negative call frame depth %d at seq " VMLOG_SEQ_FMT,
580 if (tlog->depth >= tlog->framescap)
581 vmlog_thread_log_realloc_frames(tlog,tlog->framescap * 2);
583 frame = tlog->frames + tlog->depth;
585 frame->index = index;
593 vmlog_frame * vmlog_thread_log_leave(vmlog_thread_log *tlog,int index,vmlog_seq_t seq)
597 if (--tlog->depth < 0) {
598 vmlog_warn("negative call frame depth %d at seq " VMLOG_SEQ_FMT,
603 frame = tlog->frames + tlog->depth;
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);
613 /*** tag definitions *************************************************/
616 /* the tag number, or -1 if the tag name is invalid */
618 int vmlog_tag_from_name(const char *name,int namelen)
620 vmlog_tag_definition *td;
623 if (!name || namelen < 1)
626 td = vmlog_tag_definitions;
629 if (namelen == strlen(td->name)
630 && strncmp(td->name,name,namelen) == 0)
641 /*** hash functions **************************************************/
643 static unsigned int vmlog_thread_hash(void *threadid)
645 /* XXX use a better hash function? */
646 return (unsigned int)(ptrint)threadid;
649 static unsigned int vmlog_string_hash(const char *data,int len)
651 register const unsigned char *p = (const unsigned char *) data;
652 register unsigned int hash;
655 /* The algorithm is the "One-at-a-time" algorithm as published */
656 /* by Bob Jenkins on http://burtleburtle.net/bob/hash/doobs.html. */
662 hash += (hash << 10);
666 hash ^= (hash >> 11);
667 hash += (hash << 15);
672 /*** hash tables *****************************************************/
674 static vmlog_thread_log *vmlog_get_thread_log(vmlog_log *vml,void *threadid)
677 vmlog_hash_entry *preventry = NULL;
678 vmlog_hash_entry *entry;
679 vmlog_thread_log *tlog;
683 h = vmlog_thread_hash(threadid);
684 entry = vml->threadhash.table + (h % vml->threadhash.size);
686 tlog = (vmlog_thread_log *)entry->data;
687 if (tlog && tlog->threadid == threadid)
690 entry = entry->hashlink;
693 /* this is a new threadid */
694 tlog = vmlog_thread_log_new(vml,threadid,vml->threadhash.nentries++);
697 if (preventry->data) {
698 VMLOG_XZNEW(entry,vmlog_hash_entry);
700 preventry->hashlink = entry;
708 /* XXX maybe rehash */
713 int vmlog_get_string_index(vmlog_log *vml,const char *data,int len)
716 vmlog_hash_entry *entry;
717 vmlog_hash_entry *preventry = NULL;
723 hash = vmlog_string_hash(data,len);
724 entry = vml->stringhash.table + (hash % vml->stringhash.size);
726 if (entry->len == len && entry->data && memcmp(data,entry->data,len) == 0)
729 entry = entry->hashlink;
732 /* this is a new string */
734 if (preventry->data) {
735 VMLOG_XZNEW(entry,vmlog_hash_entry);
737 preventry->hashlink = entry;
743 entry->data = vmlog_memdup(data,len);
745 entry->index = vml->stringhash.nentries++;
746 vmlog_add_string(vml,data,len);
751 static void vmlog_hashtable_init(vmlog_hash_table *ht,int size)
757 VMLOG_XZNEW_ARRAY(ht->table,vmlog_hash_entry,size);
761 static void vmlog_hashtable_free(vmlog_hash_table *ht,vmlog_hash_entry_destructor destr)
764 vmlog_hash_entry *entry,*next;
768 for (i=0; i<ht->size; ++i) {
769 entry = ht->table + i;
773 next = entry->hashlink;
778 next = entry->hashlink;
779 VMLOG_FREE(vmlog_hash_entry,entry);
783 VMLOG_FREE_ARRAY(vmlog_hash_entry,ht->size,ht->table);
784 memset(ht,0,sizeof(vmlog_hash_table));
787 static void vmlog_thread_log_destructor(vmlog_hash_entry *entry)
789 vmlog_thread_log *tlog;
793 tlog = (vmlog_thread_log *)entry->data;
794 vmlog_thread_log_free(tlog);
797 static void vmlog_string_destructor(vmlog_hash_entry *entry)
803 str = (char *)entry->data;
805 VMLOG_FREE_ARRAY(char,entry->len,str);
809 static void vmlog_open_string_files(vmlog_log *vml,int truncate)
818 fmode = (truncate) ? vmlogTruncateAppend : vmlogAppend;
820 name = vmlog_concat3(vml->prefix,"",".idx",&namelen);
821 vmlog_file_open(&(vml->idxfile),name,fmode);
822 VMLOG_FREE_ARRAY(char,namelen+1,name);
824 name = vmlog_concat3(vml->prefix,"",".str",&namelen);
825 vmlog_file_open(&(vml->strfile),name,fmode);
826 VMLOG_FREE_ARRAY(char,namelen+1,name);
829 void vmlog_load_stringhash(vmlog_log *vml,const char *prefix)
832 vmlog_string_entry *idxmap;
833 vmlog_string_entry *strent;
845 idxfname = vmlog_concat3(prefix,".","idx",&idxnamelen);
846 strfname = vmlog_concat3(prefix,".","str",&strnamelen);
848 vmlog_hashtable_free(&(vml->stringhash),vmlog_string_destructor);
849 vmlog_hashtable_init(&(vml->stringhash),VMLOG_INITIAL_STRING_HASH_SIZE);
851 vmlog_file_close(&(vml->idxfile));
852 vmlog_file_close(&(vml->strfile));
853 vmlog_open_string_files(vml,1);
855 idxmap = vmlog_file_mmap(idxfname,&idxlen);
856 strmap = vmlog_file_mmap(strfname,&strlen);
858 n = idxlen / sizeof(vmlog_string_entry);
861 vmlog_get_string_index(vml,strmap + strent->ofs,strent->len);
865 vmlog_file_munmap(idxmap,idxlen);
866 vmlog_file_munmap(strmap,strlen);
868 VMLOG_FREE_ARRAY(char,idxnamelen+1,idxfname);
869 VMLOG_FREE_ARRAY(char,strnamelen+1,strfname);
872 /*** public functions ************************************************/
874 vmlog_log * vmlog_log_new(const char *prefix,int truncate)
878 VMLOG_XZNEW(vml,vmlog_log);
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);
886 vml->prefixlen = strlen(prefix);
887 vml->prefix = vmlog_memdup(prefix,vml->prefixlen+1);
889 vmlog_open_string_files(vml,truncate);
895 void vmlog_log_free(vmlog_log *vml)
900 VMLOG_FREE_ARRAY(char,vml->prefixlen+1,vml->prefix);
904 vmlog_hashtable_free(&(vml->threadhash),vmlog_thread_log_destructor);
905 vmlog_hashtable_free(&(vml->stringhash),vmlog_string_destructor);
907 vmlog_file_close(&(vml->idxfile));
908 vmlog_file_close(&(vml->strfile));
910 VMLOG_FREE(vmlog_log,vml);
913 static void vmlog_log_enter_tag(vmlog_log *vml,void *threadid,int tag,const char *name,int namelen)
915 vmlog_thread_log *tlog;
917 vmlog_log_entry logent;
921 assert(namelen >= 0);
924 tlog = vmlog_get_thread_log(vml,threadid);
925 index = vmlog_get_string_index(vml,name,namelen);
928 if (tlog->ignoredepth) {
933 if (vmlog_is_ignored(vml,index)) {
939 logent.index = index;
940 vmlog_thread_log_append(tlog,&logent);
945 static void vmlog_log_leave_tag(vmlog_log *vml,void *threadid,int tag,const char *name,int namelen)
947 vmlog_thread_log *tlog;
949 vmlog_log_entry logent;
953 assert(namelen >= 0);
956 tlog = vmlog_get_thread_log(vml,threadid);
957 index = vmlog_get_string_index(vml,name,namelen);
960 if (tlog->ignoredepth) {
966 logent.index = index;
967 vmlog_thread_log_append(tlog,&logent);
972 void vmlog_log_enter(vmlog_log *vml,void *threadid,const char *name,int namelen)
974 vmlog_log_enter_tag(vml,threadid,VMLOG_TAG_ENTER,name,namelen);
977 void vmlog_log_rerol(vmlog_log *vml,void *threadid,const char *name,int namelen)
979 vmlog_log_enter_tag(vml,threadid,VMLOG_TAG_REROL,name,namelen);
982 void vmlog_log_leave(vmlog_log *vml,void *threadid,const char *name,int namelen)
984 vmlog_log_leave_tag(vml,threadid,VMLOG_TAG_LEAVE,name,namelen);
987 void vmlog_log_unrol(vmlog_log *vml,void *threadid,const char *name,int namelen)
989 vmlog_log_leave_tag(vml,threadid,VMLOG_TAG_UNROL,name,namelen);
992 void vmlog_log_throw(vmlog_log *vml,void *threadid,const char *name,int namelen)
994 vmlog_thread_log *tlog;
996 vmlog_log_entry logent;
1000 assert(namelen >= 0);
1003 tlog = vmlog_get_thread_log(vml,threadid);
1004 index = vmlog_get_string_index(vml,name,namelen);
1007 if (tlog->ignoredepth)
1010 logent.tag = VMLOG_TAG_THROW;
1011 logent.index = index;
1012 vmlog_thread_log_append(tlog,&logent);
1017 void vmlog_log_catch(vmlog_log *vml,void *threadid,const char *name,int namelen)
1019 vmlog_thread_log *tlog;
1021 vmlog_log_entry logent;
1025 assert(namelen >= 0);
1028 tlog = vmlog_get_thread_log(vml,threadid);
1029 index = vmlog_get_string_index(vml,name,namelen);
1032 if (tlog->ignoredepth)
1035 logent.tag = VMLOG_TAG_CATCH;
1036 logent.index = index;
1037 vmlog_thread_log_append(tlog,&logent);
1042 void vmlog_log_unwnd(vmlog_log *vml,void *threadid,const char *name,int namelen)
1044 vmlog_thread_log *tlog;
1046 vmlog_log_entry logent;
1050 assert(namelen >= 0);
1053 tlog = vmlog_get_thread_log(vml,threadid);
1054 index = vmlog_get_string_index(vml,name,namelen);
1057 if (tlog->ignoredepth) {
1058 tlog->ignoredepth--;
1062 logent.tag = VMLOG_TAG_UNWND;
1063 logent.index = index;
1064 vmlog_thread_log_append(tlog,&logent);
1069 void vmlog_log_signl(vmlog_log *vml,void *threadid,const char *name,int namelen)
1071 vmlog_thread_log *tlog;
1073 vmlog_log_entry logent;
1077 assert(namelen >= 0);
1080 tlog = vmlog_get_thread_log(vml,threadid);
1081 index = vmlog_get_string_index(vml,name,namelen);
1084 logent.tag = VMLOG_TAG_SIGNL;
1085 logent.index = index;
1086 vmlog_thread_log_append(tlog,&logent);
1091 void vmlog_log_load_ignorelist(vmlog_log *vml,const char *prefix)
1096 vmlog_load_stringhash(vml,prefix);
1097 vml->ignorelistlen = vml->stringhash.nentries;
1100 /*** ring buffer functions *******************************************/
1102 static void vmlog_ringbuf_visualize(vmlog_ringbuf *ring)
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);
1110 for (i=0; i<=ring->bufsize; ++i) {
1111 if (i == ring->bufsize) {
1112 fprintf(stdout,"%3d: xxxxxxxxxxxxx",i);
1115 fprintf(stdout,"%3d: %2d %10d",i,ring->buf[i].tag,ring->buf[i].index);
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);
1125 static void vmlog_ringbuf_check_invariants(vmlog_ringbuf *ring)
1127 /* vmlog_ringbuf_visualize(ring); */
1131 assert(ring->bufsize > 0);
1132 assert(ring->bufend == ring->buf + ring->bufsize);
1134 assert(ring->start >= ring->buf && ring->start < ring->bufend);
1135 assert((ring->end > ring->buf && ring->end <= ring->bufend)
1137 (ring->end == ring->start));
1139 assert(ring->debug_availbefore >= 0);
1140 assert(ring->debug_availafter >= 0);
1141 assert(ring->debug_availbefore + ring->debug_availafter <= ring->bufsize);
1143 /* ring->cur can point to any present */
1144 /* element (#) or be equal to ring->end*/
1146 if (ring->end >= ring->start) {
1147 /* case A: ring->end >= ring->start */
1149 /* -------#############----------- */
1151 /* buf start end bufend */
1153 assert(ring->cur >= ring->start && ring->cur <= ring->end);
1155 assert(ring->cur - ring->start == ring->debug_availbefore);
1156 assert(ring->end - ring->cur == ring->debug_availafter);
1159 /* case B: ring->end < ring->start */
1161 /* #######------------############ */
1163 /* buf end start bufend */
1165 assert((ring->cur >= ring->start && ring->cur < ring->bufend)
1167 (ring->cur >= ring->buf && ring->cur <= ring->end));
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);
1175 assert((ring->bufend - ring->start) + (ring->cur - ring->buf)
1176 == ring->debug_availbefore);
1177 assert(ring->end - ring->cur == ring->debug_availafter);
1182 vmlog_ringbuf * vmlog_ringbuf_new(const char *fname,int bufsize)
1184 vmlog_ringbuf *ring;
1186 assert(bufsize > 0);
1188 VMLOG_XZNEW(ring,vmlog_ringbuf);
1189 VMLOG_XZNEW_ARRAY(ring->buf,vmlog_log_entry,bufsize);
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;
1197 vmlog_file_open(&(ring->file),fname,vmlogRead);
1198 vmlog_file_stat(&(ring->file));
1200 vmlog_ringbuf_check_invariants(ring);
1205 void vmlog_ringbuf_free(vmlog_ringbuf *ring)
1210 vmlog_ringbuf_check_invariants(ring);
1212 vmlog_file_close(&(ring->file));
1214 VMLOG_FREE_ARRAY(vmlog_log_entry,ring->bufsize,ring->buf);
1215 VMLOG_FREE(vmlog_ringbuf,ring);
1218 static int vmlog_ringbuf_read(vmlog_ringbuf *ring,vmlog_log_entry *buf,
1219 vmlog_seq_t seq,int n)
1224 ofs = seq * sizeof(vmlog_log_entry);
1225 if (ofs != ring->file.ofs)
1226 vmlog_file_seek(&(ring->file),ofs);
1229 /* fprintf(stdout,"vmlog_ringbuf_read(%p,%d,%d)\n",
1230 (void*)ring,buf-ring->buf,n); */
1232 r = read(ring->file.fd,buf,n * sizeof(vmlog_log_entry));
1233 } while (r == -1 && errno == EINTR);
1236 vmlog_die("reading from file: %s: %s",ring->file.fname,strerror(errno));
1238 ring->file.ofs += r;
1240 if (r % sizeof(vmlog_log_entry) != 0) {
1242 vmlog_warn("partial log entry read from file: %s",ring->file.fname);
1245 return r / sizeof(vmlog_log_entry);
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)
1254 vmlog_log_entry *oldend;
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);
1261 vmlog_ringbuf_check_invariants(ring);
1263 space = fillend - fillstart;
1264 n = (len <= space) ? len : space;
1269 read = vmlog_ringbuf_read(ring,fillstart,seq,n);
1274 ring->end = fillstart + read;
1275 ring->debug_availafter += read;
1277 if (ring->cur == ring->bufend)
1278 ring->cur = ring->buf;
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;
1293 vmlog_ringbuf_check_invariants(ring);
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)
1304 vmlog_log_entry *oldstart;
1305 vmlog_log_entry *oldend;
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);
1312 vmlog_ringbuf_check_invariants(ring);
1314 space = fillend - fillstart;
1315 n = (len <= space) ? len : space;
1321 fillstart += space - n;
1323 read = vmlog_ringbuf_read(ring,fillstart,seq,n);
1325 vmlog_die("could not read backward in file: %s: %s",
1326 ring->file.fname,strerror(errno));
1328 oldstart = ring->start;
1329 ring->start = fillstart;
1330 ring->debug_availbefore += read;
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;
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;
1349 if (ring->end == ring->buf) {
1350 assert(oldstart == oldend);
1351 ring->end = ring->bufend;
1354 if (ring->cur == ring->buf && ring->end == ring->bufend)
1355 ring->cur = ring->bufend;
1357 vmlog_ringbuf_check_invariants(ring);
1362 int vmlog_ringbuf_fill(vmlog_ringbuf *ring,int len)
1366 vmlog_log_entry *fillend;
1376 vmlog_ringbuf_check_invariants(ring);
1379 if (ring->end >= ring->cur) {
1380 /* case A'1: ring->end >= ring->start */
1383 /* ------OOOOO#########----------- */
1385 /* buf start cur end bufend */
1387 /* case B'1: ring->end < ring->start */
1390 /* OOOOOOOOOOO#########----OOOOOOO */
1392 /* buf cur end st. bufend */
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,
1401 if (ring->end != ring->bufend)
1402 goto no_more_entries;
1404 /* case A'1: ring->end >= ring->start */
1407 /* ------OOOOO#################### */
1409 /* buf start cur end==bufend */
1411 /* case B'1: ring->end < ring->start */
1414 /* OOOOOOOOOOO#################### */
1416 /* buf==start cur end==bufend */
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,
1427 /* ring->end < ring->cur */
1430 assert(ring->end < ring->start);
1432 /* case B'2: ring->end < ring->start */
1435 /* #####------OOOOOO############## */
1437 /* buf end start cur bufend */
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,
1450 if (len > ring->seq)
1453 if (ring->start <= ring->cur) {
1454 /* case A'1: ring->end >= ring->start */
1457 /* ------#####OOOOOOOOO----------- */
1459 /* buf start cur end bufend */
1461 /* case B'2: ring->end < ring->start */
1464 /* OOOOO------######OOOOOOOOOOOOOO */
1466 /* buf end start cur bufend */
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,
1475 if (ring->start != ring->buf)
1476 goto no_more_entries;
1478 /* case A'1: ring->end >= ring->start */
1480 /* vvvvvvvvvvvvvvvvvvv */
1481 /* ###########OOOOOOOOO----------- */
1483 /* buf=start cur end bufend */
1485 /* case B'2: ring->end < ring->start */
1488 /* #################OOOOOOOOOOOOOO */
1490 /* buf=start cur end=bufend */
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,
1500 /* ring->start > ring->cur */
1502 /* case B'1: ring->end < ring->start */
1505 /* ###########OOOOOOOOO----####### */
1507 /* buf cur end st. bufend */
1510 assert(ring->end < ring->start);
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,
1522 vmlog_ringbuf_check_invariants(ring);
1527 static void vmlog_ringbuf_reset(vmlog_ringbuf *ring)
1529 ring->start = ring->buf;
1530 ring->cur = ring->buf;
1531 ring->end = ring->buf;
1533 ring->debug_availbefore = 0;
1534 ring->debug_availafter = 0;
1536 vmlog_ringbuf_check_invariants(ring);
1539 static int vmlog_ringbuf_advance(vmlog_ringbuf *ring,int diff)
1544 if (ring->end >= ring->start) {
1547 space = ring->end - ring->cur;
1558 ring->debug_availbefore += diff;
1559 ring->debug_availafter -= diff;
1564 if (ring->end >= ring->cur)
1565 goto advance_cur_to_end;
1567 space = ring->bufend - ring->cur;
1569 goto simple_advance;
1571 ring->cur = ring->buf - space;
1572 goto advance_cur_to_end;
1575 else if (diff < 0) {
1576 if (ring->end >= ring->start) {
1578 advance_cur_to_start:
1579 space = ring->cur - ring->start;
1586 goto simple_advance;
1590 if (ring->cur >= ring->start)
1591 goto advance_cur_to_start;
1593 space = ring->cur - ring->buf;
1595 goto simple_advance;
1597 ring->cur = ring->bufend + space;
1598 goto advance_cur_to_start;
1606 void vmlog_ringbuf_seek(vmlog_ringbuf *ring,vmlog_seq_t seq)
1610 vmlog_ringbuf_check_invariants(ring);
1612 diff = seq - ring->seq;
1613 if (abs(diff) < ring->bufsize)
1614 diff -= vmlog_ringbuf_advance(ring,(int)diff);
1619 vmlog_ringbuf_reset(ring);
1620 vmlog_file_seek(&(ring->file),seq * sizeof(vmlog_log_entry));
1623 vmlog_ringbuf_check_invariants(ring);
1626 vmlog_log_entry * vmlog_ringbuf_next(vmlog_ringbuf *ring,int prefetch)
1628 vmlog_ringbuf_check_invariants(ring);
1631 if (ring->end >= ring->start) {
1633 if (ring->cur < ring->end) {
1634 ring->debug_availafter--;
1635 ring->debug_availbefore++;
1642 if (ring->end >= ring->cur) {
1643 if (ring->cur < ring->end) {
1644 ring->debug_availafter--;
1645 ring->debug_availbefore++;
1651 if (ring->cur < ring->bufend) {
1656 if (++ring->cur == ring->bufend)
1657 ring->cur = ring->buf;
1658 ring->debug_availafter--;
1659 ring->debug_availbefore++;
1665 if (!vmlog_ringbuf_fill(ring,prefetch))
1669 assert(0); /* NOT REACHED */
1673 vmlog_log_entry * vmlog_ringbuf_prev(vmlog_ringbuf *ring,int prefetch)
1675 vmlog_ringbuf_check_invariants(ring);
1678 if (ring->end >= ring->start) {
1680 if (ring->cur > ring->start) {
1681 ring->debug_availafter++;
1682 ring->debug_availbefore--;
1689 if (ring->cur >= ring->start) {
1690 if (ring->cur > ring->start) {
1691 ring->debug_availafter++;
1692 ring->debug_availbefore--;
1698 if (--ring->cur < ring->buf)
1699 ring->cur = ring->bufend - 1;
1701 ring->debug_availafter++;
1702 ring->debug_availbefore--;
1707 if (!vmlog_ringbuf_fill(ring,-prefetch))
1711 assert(0); /* NOT REACHED */
1715 /*** option parsing **************************************************/
1717 vmlog_options *vmlog_opt_new(void)
1719 vmlog_options *opts;
1721 VMLOG_XZNEW(opts,vmlog_options);
1726 int vmlog_opt_parse_seq(const char *arg,int len,vmlog_seq_t *seq)
1737 buf = vmlog_strdup(arg,len);
1738 *seq = strtoll(buf,&endptr,10);
1740 r = (endptr[0] == 0);
1742 VMLOG_FREE_ARRAY(char,len+1,buf);
1746 int vmlog_opt_parse_range(const char *arg,vmlog_seq_t *start,vmlog_seq_t *end)
1751 sep = strchr(arg,':');
1754 if (!vmlog_opt_parse_seq(arg,len,start))
1765 if (!vmlog_opt_parse_seq(arg,len,start))
1769 len = strlen(arg) - len - 1;
1771 *end = VMLOG_SEQ_MAX;
1774 if (!vmlog_opt_parse_seq(sep+1,len,end))
1780 void vmlog_opt_set_prefix(vmlog_options *opts, const char *arg)
1782 opts->prefix = vmlog_strdup(arg,strlen(arg));
1785 void vmlog_opt_set_stringprefix(vmlog_options *opts, const char *arg)
1787 opts->stringprefix = vmlog_strdup(arg,strlen(arg));
1790 void vmlog_opt_set_ignoreprefix(vmlog_options *opts, const char *arg)
1792 opts->ignoreprefix = vmlog_strdup(arg,strlen(arg));
1795 static int vmlog_opt_parse_one_option(vmlog_options *opts, const char *arg, const char *nextarg)
1799 if (strncmp(arg,"-vmlog:",7) != 0) {
1803 /* a vmlog option */
1806 if (strcmp(arg,"-vmlog:prefix") == 0) {
1808 vmlog_die("expected a prefix after -vmlog:prefix");
1809 vmlog_opt_set_prefix(opts,nextarg);
1812 else if (strcmp(arg,"-vmlog:strings") == 0) {
1814 vmlog_die("expected a prefix after -vmlog:strings");
1815 vmlog_opt_set_stringprefix(opts,nextarg);
1818 else if (strcmp(arg,"-vmlog:ignore") == 0) {
1820 vmlog_die("expected a prefix after -vmlog:ignore");
1821 vmlog_opt_set_ignoreprefix(opts,nextarg);
1825 vmlog_die("unknown -vmlog:... option: %s",arg);
1831 vmlog_options *vmlog_opt_parse_cmd_line(int *pargc,char **argv)
1835 vmlog_options *opts;
1841 opts = vmlog_opt_new();
1843 if (*pargc && argv[0])
1844 opts->progname = vmlog_strdup(argv[0],strlen(argv[0]));
1847 while (i < *pargc) {
1850 left = *pargc - i - 1;
1852 eat = vmlog_opt_parse_one_option(opts,arg,
1853 (left) ? argv[i+1] : NULL);
1860 /* remove the option from the command line */
1862 memmove(argv + i,argv + i + eat,sizeof(char*) * (*pargc - (i+eat)));
1869 vmlog_options *vmlog_opt_parse_vmargs(JavaVMInitArgs *vmargs)
1873 vmlog_options *opts;
1878 opts = vmlog_opt_new();
1881 while (i < vmargs->nOptions) {
1882 arg = vmargs->options[i].optionString;
1884 eat = vmlog_opt_parse_one_option(opts,arg,
1885 (i+1 < vmargs->nOptions) ? vmargs->options[i+1].optionString : NULL);
1892 /* remove the option from the command line */
1894 memmove(vmargs->options + i,vmargs->options + i + eat,
1895 sizeof(JavaVMOption) * (vmargs->nOptions - (i+eat)));
1896 vmargs->nOptions -= eat;
1902 void vmlog_opt_free(vmlog_options *opts)
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);
1914 VMLOG_FREE(vmlog_options,opts);
1917 /* vim: noet ts=8 sw=8