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
43 /*** constants *******************************************************/
45 /* currently vmlog does no rehashing, so these should be quite big */
46 #define VMLOG_INITIAL_STRING_HASH_SIZE 50000 /* XXX debug */
47 #define VMLOG_INITIAL_THREAD_HASH_SIZE 8 /* XXX debug */
49 /* initial size of the frame buffer - this is doubled each time */
50 /* the frame buffer has to grow */
51 #define VMLOG_INITIAL_FRAMES_CAPACITY 1 /* XXX debug */
53 /*** types ***********************************************************/
55 /* we declare this here because defining _LARGEFILE64_SOURCE works */
56 /* only if we are the first ones to include the headers, which may */
57 /* not be the case if vmlog.c is used as an include file. */
59 #ifndef _LARGEFILE64_SOURCE
60 typedef long long off64_t;
61 off64_t lseek64(int fd, off64_t offset, int whence);
64 /*** tag definitions *************************************************/
66 /* CAUTION: these must are indexed by the VMLOG_TAG_... constants! */
67 vmlog_tag_definition vmlog_tag_definitions[] = {
68 { "enter", "enter", +1 },
69 { "leave", "leave", -1 },
70 { "throw", "throw", 0 },
71 { "catch", "catch", 0 },
72 { "unwnd", "unwnd", -1 },
73 { "signl", "signl", 0 },
74 { "unrol", "unrol", -1 },
75 { "rerol", "rerol", +1 },
79 /*** global variables ************************************************/
81 static char *vmlog_progname = "vmlog";
83 /*** prototypes ******************************************************/
85 static void *vmlog_memdup(const void *m,int len);
87 /*** error reporting *************************************************/
89 void vmlog_set_progname(const char *progname)
92 progname = "vmlog (progname == NULL)";
95 vmlog_progname = vmlog_memdup(progname,strlen(progname)+1);
98 void vmlog_die(const char *fmt,...)
102 fputs(vmlog_progname,stderr);
103 fputs(": error: ",stderr);
105 vfprintf(stderr,fmt,ap);
111 void vmlog_warn(const char *fmt,...)
115 fputs(vmlog_progname,stderr);
116 fputs(": warning: ",stderr);
118 vfprintf(stderr,fmt,ap);
123 void vmlog_die_usage(const char *usage,int error)
127 fputs(usage,(error) ? stderr : stdout);
128 exit((error) ? 1 : 0);
131 /*** utility functions ***********************************************/
133 static void *vmlog_memdup(const void *data,int len)
137 p = VMLOG_NEW_ARRAY(char,len);
144 static void *vmlog_strdup(const void *data,int len)
148 p = VMLOG_NEW_ARRAY(char,len+1);
156 char *vmlog_concat4len(const char *a,int alen,const char *b,int blen,
157 const char *c,int clen,const char *d,int dlen,
169 len = alen + blen + clen + dlen;
173 p = VMLOG_NEW_ARRAY(char,len+1);
175 memcpy(pp,a,alen); pp += alen;
176 memcpy(pp,b,blen); pp += blen;
177 memcpy(pp,c,clen); pp += clen;
178 memcpy(pp,d,dlen); pp += dlen;
184 char *vmlog_concat3(const char *a,const char *b,const char *c,int *plen)
186 int len,lena,lenb,lenc;
198 len = lena + lenb + lenc;
202 p = VMLOG_NEW_ARRAY(char,len+1);
204 memcpy(pp,a,lena); pp += lena;
205 memcpy(pp,b,lenb); pp += lenb;
206 memcpy(pp,c,lenc); pp += lenc;
212 /*** file ops ********************************************************/
214 void vmlog_file_open(vmlog_file *file,const char *fname,vmlog_fmode fmode)
228 flags = O_WRONLY | O_APPEND | O_CREAT;
231 case vmlogTruncateAppend:
232 flags = O_WRONLY | O_APPEND | O_CREAT | O_TRUNC;
236 vmlog_die("unknown fmode for opening file: %s: %d",
240 r = open(fname,flags,0644);
242 vmlog_die("could not open file: %s: %s",fname,strerror(errno));
245 file->fnamelen = strlen(fname);
246 file->fname = vmlog_memdup(fname,file->fnamelen+1);
248 r = fstat(file->fd,&st);
250 vmlog_die("could not stat file: %s: %s",fname,strerror(errno));
252 file->ofs = (fmode == vmlogRead) ? 0 : st.st_size;
255 void vmlog_file_close(vmlog_file *file)
264 VMLOG_FREE_ARRAY(char,file->fnamelen+1,file->fname);
269 void vmlog_file_append(vmlog_file *file,const void *data,int len)
279 r = write(file->fd,data,len);
280 } while (r == -1 && errno == EINTR);
283 vmlog_die("could not write to file: %s: %s",file->fname,strerror(errno));
287 vmlog_die("could not write all data to file: %s",file->fname);
293 void vmlog_file_stat(vmlog_file *file)
298 r = fstat(file->fd,&st);
300 vmlog_die("could not stat file: %s: %s",file->fname,strerror(errno));
302 file->size = st.st_size;
305 void * vmlog_file_mmap(const char *fname,int *plen)
312 fd = open(fname,O_RDONLY);
314 vmlog_die("could not open file: %s: %s",fname,strerror(errno));
318 vmlog_die("could not stat file: %s: %s",fname,strerror(errno));
324 m = mmap(NULL,st.st_size,PROT_READ,MAP_PRIVATE,fd,0);
325 if (m == MAP_FAILED) {
326 vmlog_die("could not mmap file: %s: %s",fname,strerror(errno));
339 void vmlog_file_munmap(void *m,int len)
346 vmlog_warn("could not munmap file: %s",strerror(errno));
353 void vmlog_file_seek(vmlog_file *file,vmlog_fofs_t ofs)
357 r = lseek64(file->fd,ofs,SEEK_SET);
358 if (r == (off64_t)-1)
359 vmlog_die("could not seek position in file: %s: %s",
360 file->fname,strerror(errno));
364 /*** string storage **************************************************/
366 static void vmlog_add_string(vmlog_log *vml,const char *data,int len)
368 vmlog_string_entry strent;
369 #if defined(VMLOG_ENDIAN_CONVERT_WRITE)
375 if (vml->strfile.fd == -1)
377 if (vml->idxfile.fd == -1)
380 strent.ofs = vml->strfile.ofs;
383 #if defined(VMLOG_ENDIAN_CONVERT_WRITE)
384 #if defined(VMLOG_HOST_LITTLE_ENDIAN)
385 tmp = ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[0]) << 56)
386 | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[1]) << 48)
387 | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[2]) << 40)
388 | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[3]) << 32)
389 | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[4]) << 24)
390 | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[5]) << 16)
391 | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[6]) << 8)
392 | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[7]) << 0);
394 tmp = ((vmlog_fofs_t)(((unsigned char*)&strent.len)[0]) << 24)
395 | ((vmlog_fofs_t)(((unsigned char*)&strent.len)[1]) << 16)
396 | ((vmlog_fofs_t)(((unsigned char*)&strent.len)[2]) << 8)
397 | ((vmlog_fofs_t)(((unsigned char*)&strent.len)[3]) << 0);
400 tmp = ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[7]) << 56)
401 | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[6]) << 48)
402 | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[5]) << 40)
403 | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[4]) << 32)
404 | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[3]) << 24)
405 | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[2]) << 16)
406 | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[1]) << 8)
407 | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[0]) << 0);
409 tmp = ((vmlog_fofs_t)(((unsigned char*)&strent.len)[3]) << 24)
410 | ((vmlog_fofs_t)(((unsigned char*)&strent.len)[2]) << 16)
411 | ((vmlog_fofs_t)(((unsigned char*)&strent.len)[1]) << 8)
412 | ((vmlog_fofs_t)(((unsigned char*)&strent.len)[0]) << 0);
415 #endif /* defined(VMLOG_ENDIAN_CONVERT_WRITE) */
417 vmlog_file_append(&(vml->strfile),data,len);
418 vmlog_file_append(&(vml->idxfile),&strent,sizeof(vmlog_string_entry));
421 /*** index functions *************************************************/
423 static int vmlog_is_ignored(vmlog_log *vml,int index)
425 return (index < vml->ignorelistlen);
428 /*** thread log functions ********************************************/
430 static void vmlog_thread_log_alloc_logbuf(vmlog_thread_log *tlog,int cap)
436 VMLOG_XZNEW_ARRAY(tlog->logbuf,vmlog_log_entry,cap);
441 tlog->logbufptr = tlog->logbuf;
442 tlog->logbufend = tlog->logbuf + cap;
443 tlog->logbufcap = cap;
446 static void vmlog_thread_log_flush(vmlog_thread_log *tlog)
449 assert(tlog->logbuf);
451 vmlog_file_append(&(tlog->logfile),tlog->logbuf,
452 (tlog->logbufptr - tlog->logbuf) * sizeof(vmlog_log_entry));
454 tlog->logbufptr = tlog->logbuf;
457 static void vmlog_thread_log_realloc_frames(vmlog_thread_log *tlog,int cap)
459 vmlog_frame *oldframes;
462 assert(cap >= tlog->depth);
464 oldframes = tlog->frames;
467 VMLOG_XZNEW_ARRAY(tlog->frames,vmlog_frame,cap);
475 memcpy(tlog->frames,oldframes,sizeof(vmlog_frame) * tlog->depth);
477 VMLOG_FREE_ARRAY(vmlog_frame,tlog->framescap,oldframes);
479 tlog->framescap = cap;
482 static void vmlog_thread_log_append(vmlog_thread_log *tlog,vmlog_log_entry *logent)
484 #if defined(VMLOG_ENDIAN_CONVERT_WRITE)
487 #if defined(VMLOG_HOST_LITTLE_ENDIAN)
488 tmp = ((unsigned int)(((unsigned char*)logent)[3]) << 0)
489 | ((unsigned int)(((unsigned char*)logent)[2]) << 8)
490 | ((unsigned int)(((unsigned char*)logent)[1]) << 16);
492 tmp = ((unsigned int)(((unsigned char*)logent)[1]) << 0)
493 | ((unsigned int)(((unsigned char*)logent)[2]) << 8)
494 | ((unsigned int)(((unsigned char*)logent)[3]) << 16);
497 #endif /* defined(VMLOG_ENDIAN_CONVERT_WRITE) */
498 if (tlog->logbufptr) {
499 if (tlog->logbufptr == tlog->logbufend) {
500 vmlog_thread_log_flush(tlog);
502 *tlog->logbufptr++ = *logent;
505 vmlog_file_append(&(tlog->logfile),logent,sizeof(vmlog_log_entry));
509 #define VMLOG_INT2STR_BUFFER 20
511 vmlog_thread_log *vmlog_thread_log_new(vmlog_log *vml,void *threadid,int index)
513 vmlog_thread_log *tlog;
514 char buf[VMLOG_INT2STR_BUFFER];
519 VMLOG_XZNEW(tlog,vmlog_thread_log);
521 tlog->threadid = threadid;
522 tlog->threadidx = index;
523 tlog->logfile.fd = -1;
525 vmlog_thread_log_realloc_frames(tlog,VMLOG_INITIAL_FRAMES_CAPACITY);
527 if (vml && vml->prefix) {
528 r = snprintf(buf,VMLOG_INT2STR_BUFFER,"%d",index);
529 assert(r < VMLOG_INT2STR_BUFFER);
530 buf[VMLOG_INT2STR_BUFFER-1] = 0;
531 name = vmlog_concat4len(vml->prefix,vml->prefixlen,
536 vmlog_file_open(&(tlog->logfile),name,vmlogTruncateAppend);
537 VMLOG_FREE_ARRAY(char,namelen+1,name);
543 void vmlog_thread_log_free(vmlog_thread_log *tlog)
549 vmlog_thread_log_flush(tlog);
551 vmlog_file_close(&(tlog->logfile));
554 VMLOG_FREE_ARRAY(vmlog_frame,tlog->framescap,tlog->frames);
556 VMLOG_FREE(vmlog_thread_log,tlog);
559 vmlog_frame * vmlog_thread_log_enter(vmlog_thread_log *tlog,int index,vmlog_seq_t seq)
563 if (tlog->depth < 0) {
564 vmlog_warn("negative call frame depth %d at seq " VMLOG_SEQ_FMT,
569 if (tlog->depth >= tlog->framescap)
570 vmlog_thread_log_realloc_frames(tlog,tlog->framescap * 2);
572 frame = tlog->frames + tlog->depth;
574 frame->index = index;
582 vmlog_frame * vmlog_thread_log_leave(vmlog_thread_log *tlog,int index,vmlog_seq_t seq)
586 if (--tlog->depth < 0) {
587 vmlog_warn("negative call frame depth %d at seq " VMLOG_SEQ_FMT,
592 frame = tlog->frames + tlog->depth;
594 if (index != frame->index)
595 vmlog_warn("mismatched leave at seq " VMLOG_SEQ_FMT
596 ": entered index %d, left index %d",
597 seq,frame->index,index);
602 /*** tag definitions *************************************************/
605 /* the tag number, or -1 if the tag name is invalid */
607 int vmlog_tag_from_name(const char *name,int namelen)
609 vmlog_tag_definition *td;
612 if (!name || namelen < 1)
615 td = vmlog_tag_definitions;
618 if (namelen == strlen(td->name)
619 && strncmp(td->name,name,namelen) == 0)
630 /*** hash functions **************************************************/
632 static unsigned int vmlog_thread_hash(void *threadid)
634 /* XXX use a better hash function? */
635 return (unsigned int)(ptrint)threadid;
638 static unsigned int vmlog_string_hash(const char *data,int len)
640 register const unsigned char *p = (const unsigned char *) data;
641 register unsigned int hash;
644 /* The algorithm is the "One-at-a-time" algorithm as published */
645 /* by Bob Jenkins on http://burtleburtle.net/bob/hash/doobs.html. */
651 hash += (hash << 10);
655 hash ^= (hash >> 11);
656 hash += (hash << 15);
661 /*** hash tables *****************************************************/
663 static vmlog_thread_log *vmlog_get_thread_log(vmlog_log *vml,void *threadid)
666 vmlog_hash_entry *preventry = NULL;
667 vmlog_hash_entry *entry;
668 vmlog_thread_log *tlog;
672 h = vmlog_thread_hash(threadid);
673 entry = vml->threadhash.table + (h % vml->threadhash.size);
675 tlog = (vmlog_thread_log *)entry->data;
676 if (tlog && tlog->threadid == threadid)
679 entry = entry->hashlink;
682 /* this is a new threadid */
683 tlog = vmlog_thread_log_new(vml,threadid,vml->threadhash.nentries++);
686 if (preventry->data) {
687 VMLOG_XZNEW(entry,vmlog_hash_entry);
689 preventry->hashlink = entry;
697 /* XXX maybe rehash */
702 int vmlog_get_string_index(vmlog_log *vml,const char *data,int len)
705 vmlog_hash_entry *entry;
706 vmlog_hash_entry *preventry = NULL;
712 hash = vmlog_string_hash(data,len);
713 entry = vml->stringhash.table + (hash % vml->stringhash.size);
715 if (entry->len == len && entry->data && memcmp(data,entry->data,len) == 0)
718 entry = entry->hashlink;
721 /* this is a new string */
723 if (preventry->data) {
724 VMLOG_XZNEW(entry,vmlog_hash_entry);
726 preventry->hashlink = entry;
732 entry->data = vmlog_memdup(data,len);
734 entry->index = vml->stringhash.nentries++;
735 vmlog_add_string(vml,data,len);
740 static void vmlog_hashtable_init(vmlog_hash_table *ht,int size)
746 VMLOG_XZNEW_ARRAY(ht->table,vmlog_hash_entry,size);
750 static void vmlog_hashtable_free(vmlog_hash_table *ht,vmlog_hash_entry_destructor destr)
753 vmlog_hash_entry *entry,*next;
757 for (i=0; i<ht->size; ++i) {
758 entry = ht->table + i;
762 next = entry->hashlink;
767 next = entry->hashlink;
768 VMLOG_FREE(vmlog_hash_entry,entry);
772 VMLOG_FREE_ARRAY(vmlog_hash_entry,ht->size,ht->table);
773 memset(ht,0,sizeof(vmlog_hash_table));
776 static void vmlog_thread_log_destructor(vmlog_hash_entry *entry)
778 vmlog_thread_log *tlog;
782 tlog = (vmlog_thread_log *)entry->data;
783 vmlog_thread_log_free(tlog);
786 static void vmlog_string_destructor(vmlog_hash_entry *entry)
792 str = (char *)entry->data;
794 VMLOG_FREE_ARRAY(char,entry->len,str);
798 static void vmlog_open_string_files(vmlog_log *vml,int truncate)
807 fmode = (truncate) ? vmlogTruncateAppend : vmlogAppend;
809 name = vmlog_concat3(vml->prefix,"",".idx",&namelen);
810 vmlog_file_open(&(vml->idxfile),name,fmode);
811 VMLOG_FREE_ARRAY(char,namelen+1,name);
813 name = vmlog_concat3(vml->prefix,"",".str",&namelen);
814 vmlog_file_open(&(vml->strfile),name,fmode);
815 VMLOG_FREE_ARRAY(char,namelen+1,name);
818 void vmlog_load_stringhash(vmlog_log *vml,const char *prefix)
821 vmlog_string_entry *idxmap;
822 vmlog_string_entry *strent;
834 idxfname = vmlog_concat3(prefix,".","idx",&idxnamelen);
835 strfname = vmlog_concat3(prefix,".","str",&strnamelen);
837 vmlog_hashtable_free(&(vml->stringhash),vmlog_string_destructor);
838 vmlog_hashtable_init(&(vml->stringhash),VMLOG_INITIAL_STRING_HASH_SIZE);
840 vmlog_file_close(&(vml->idxfile));
841 vmlog_file_close(&(vml->strfile));
842 vmlog_open_string_files(vml,1);
844 idxmap = vmlog_file_mmap(idxfname,&idxlen);
845 strmap = vmlog_file_mmap(strfname,&strlen);
847 n = idxlen / sizeof(vmlog_string_entry);
850 vmlog_get_string_index(vml,strmap + strent->ofs,strent->len);
854 vmlog_file_munmap(idxmap,idxlen);
855 vmlog_file_munmap(strmap,strlen);
857 VMLOG_FREE_ARRAY(char,idxnamelen+1,idxfname);
858 VMLOG_FREE_ARRAY(char,strnamelen+1,strfname);
861 /*** public functions ************************************************/
863 vmlog_log * vmlog_log_new(const char *prefix,int truncate)
867 VMLOG_XZNEW(vml,vmlog_log);
869 vml->idxfile.fd = -1;
870 vml->strfile.fd = -1;
871 vmlog_hashtable_init(&(vml->stringhash),VMLOG_INITIAL_STRING_HASH_SIZE);
872 vmlog_hashtable_init(&(vml->threadhash),VMLOG_INITIAL_THREAD_HASH_SIZE);
875 vml->prefixlen = strlen(prefix);
876 vml->prefix = vmlog_memdup(prefix,vml->prefixlen+1);
878 vmlog_open_string_files(vml,truncate);
884 void vmlog_log_free(vmlog_log *vml)
889 VMLOG_FREE_ARRAY(char,vml->prefixlen+1,vml->prefix);
893 vmlog_hashtable_free(&(vml->threadhash),vmlog_thread_log_destructor);
894 vmlog_hashtable_free(&(vml->stringhash),vmlog_string_destructor);
896 vmlog_file_close(&(vml->idxfile));
897 vmlog_file_close(&(vml->strfile));
899 VMLOG_FREE(vmlog_log,vml);
902 static void vmlog_log_enter_tag(vmlog_log *vml,void *threadid,int tag,const char *name,int namelen)
904 vmlog_thread_log *tlog;
906 vmlog_log_entry logent;
910 assert(namelen >= 0);
913 tlog = vmlog_get_thread_log(vml,threadid);
914 index = vmlog_get_string_index(vml,name,namelen);
917 if (tlog->ignoredepth) {
922 if (vmlog_is_ignored(vml,index)) {
928 logent.index = index;
929 vmlog_thread_log_append(tlog,&logent);
934 static void vmlog_log_leave_tag(vmlog_log *vml,void *threadid,int tag,const char *name,int namelen)
936 vmlog_thread_log *tlog;
938 vmlog_log_entry logent;
942 assert(namelen >= 0);
945 tlog = vmlog_get_thread_log(vml,threadid);
946 index = vmlog_get_string_index(vml,name,namelen);
949 if (tlog->ignoredepth) {
955 logent.index = index;
956 vmlog_thread_log_append(tlog,&logent);
961 void vmlog_log_enter(vmlog_log *vml,void *threadid,const char *name,int namelen)
963 vmlog_log_enter_tag(vml,threadid,VMLOG_TAG_ENTER,name,namelen);
966 void vmlog_log_rerol(vmlog_log *vml,void *threadid,const char *name,int namelen)
968 vmlog_log_enter_tag(vml,threadid,VMLOG_TAG_REROL,name,namelen);
971 void vmlog_log_leave(vmlog_log *vml,void *threadid,const char *name,int namelen)
973 vmlog_log_leave_tag(vml,threadid,VMLOG_TAG_LEAVE,name,namelen);
976 void vmlog_log_unrol(vmlog_log *vml,void *threadid,const char *name,int namelen)
978 vmlog_log_leave_tag(vml,threadid,VMLOG_TAG_UNROL,name,namelen);
981 void vmlog_log_throw(vmlog_log *vml,void *threadid,const char *name,int namelen)
983 vmlog_thread_log *tlog;
985 vmlog_log_entry logent;
989 assert(namelen >= 0);
992 tlog = vmlog_get_thread_log(vml,threadid);
993 index = vmlog_get_string_index(vml,name,namelen);
996 if (tlog->ignoredepth)
999 logent.tag = VMLOG_TAG_THROW;
1000 logent.index = index;
1001 vmlog_thread_log_append(tlog,&logent);
1006 void vmlog_log_catch(vmlog_log *vml,void *threadid,const char *name,int namelen)
1008 vmlog_thread_log *tlog;
1010 vmlog_log_entry logent;
1014 assert(namelen >= 0);
1017 tlog = vmlog_get_thread_log(vml,threadid);
1018 index = vmlog_get_string_index(vml,name,namelen);
1021 if (tlog->ignoredepth)
1024 logent.tag = VMLOG_TAG_CATCH;
1025 logent.index = index;
1026 vmlog_thread_log_append(tlog,&logent);
1031 void vmlog_log_unwnd(vmlog_log *vml,void *threadid,const char *name,int namelen)
1033 vmlog_thread_log *tlog;
1035 vmlog_log_entry logent;
1039 assert(namelen >= 0);
1042 tlog = vmlog_get_thread_log(vml,threadid);
1043 index = vmlog_get_string_index(vml,name,namelen);
1046 if (tlog->ignoredepth) {
1047 tlog->ignoredepth--;
1051 logent.tag = VMLOG_TAG_UNWND;
1052 logent.index = index;
1053 vmlog_thread_log_append(tlog,&logent);
1058 void vmlog_log_signl(vmlog_log *vml,void *threadid,const char *name,int namelen)
1060 vmlog_thread_log *tlog;
1062 vmlog_log_entry logent;
1066 assert(namelen >= 0);
1069 tlog = vmlog_get_thread_log(vml,threadid);
1070 index = vmlog_get_string_index(vml,name,namelen);
1073 logent.tag = VMLOG_TAG_SIGNL;
1074 logent.index = index;
1075 vmlog_thread_log_append(tlog,&logent);
1080 void vmlog_log_load_ignorelist(vmlog_log *vml,const char *prefix)
1085 vmlog_load_stringhash(vml,prefix);
1086 vml->ignorelistlen = vml->stringhash.nentries;
1089 /*** ring buffer functions *******************************************/
1091 static void vmlog_ringbuf_visualize(vmlog_ringbuf *ring)
1095 fprintf(stdout,"vmlog_ringbuf %p: bufsize=%d availbefore=%d availafter=%d\n",
1096 (void*)ring,ring->bufsize,
1097 ring->debug_availbefore,ring->debug_availafter);
1099 for (i=0; i<=ring->bufsize; ++i) {
1100 if (i == ring->bufsize) {
1101 fprintf(stdout,"%3d: xxxxxxxxxxxxx",i);
1104 fprintf(stdout,"%3d: %2d %10d",i,ring->buf[i].tag,ring->buf[i].index);
1106 if (ring->start - ring->buf == i) fputs(" start",stdout);
1107 if (ring->cur - ring->buf == i) fputs(" cur",stdout);
1108 if (ring->end - ring->buf == i) fputs(" end",stdout);
1109 if (ring->cur - ring->buf == i) fprintf(stdout," (" VMLOG_SEQ_FMT ")",ring->seq);
1114 static void vmlog_ringbuf_check_invariants(vmlog_ringbuf *ring)
1116 /* vmlog_ringbuf_visualize(ring); */
1120 assert(ring->bufsize > 0);
1121 assert(ring->bufend == ring->buf + ring->bufsize);
1123 assert(ring->start >= ring->buf && ring->start < ring->bufend);
1124 assert((ring->end > ring->buf && ring->end <= ring->bufend)
1126 (ring->end == ring->start));
1128 assert(ring->debug_availbefore >= 0);
1129 assert(ring->debug_availafter >= 0);
1130 assert(ring->debug_availbefore + ring->debug_availafter <= ring->bufsize);
1132 /* ring->cur can point to any present */
1133 /* element (#) or be equal to ring->end*/
1135 if (ring->end >= ring->start) {
1136 /* case A: ring->end >= ring->start */
1138 /* -------#############----------- */
1140 /* buf start end bufend */
1142 assert(ring->cur >= ring->start && ring->cur <= ring->end);
1144 assert(ring->cur - ring->start == ring->debug_availbefore);
1145 assert(ring->end - ring->cur == ring->debug_availafter);
1148 /* case B: ring->end < ring->start */
1150 /* #######------------############ */
1152 /* buf end start bufend */
1154 assert((ring->cur >= ring->start && ring->cur < ring->bufend)
1156 (ring->cur >= ring->buf && ring->cur <= ring->end));
1158 if (ring->cur >= ring->start) {
1159 assert(ring->cur - ring->start == ring->debug_availbefore);
1160 assert((ring->bufend - ring->cur) + (ring->end - ring->buf)
1161 == ring->debug_availafter);
1164 assert((ring->bufend - ring->start) + (ring->cur - ring->buf)
1165 == ring->debug_availbefore);
1166 assert(ring->end - ring->cur == ring->debug_availafter);
1171 vmlog_ringbuf * vmlog_ringbuf_new(const char *fname,int bufsize)
1173 vmlog_ringbuf *ring;
1175 assert(bufsize > 0);
1177 VMLOG_XZNEW(ring,vmlog_ringbuf);
1178 VMLOG_XZNEW_ARRAY(ring->buf,vmlog_log_entry,bufsize);
1180 ring->bufsize = bufsize;
1181 ring->bufend = ring->buf + bufsize;
1182 ring->start = ring->buf;
1183 ring->end = ring->buf;
1184 ring->cur = ring->buf;
1186 vmlog_file_open(&(ring->file),fname,vmlogRead);
1187 vmlog_file_stat(&(ring->file));
1189 vmlog_ringbuf_check_invariants(ring);
1194 void vmlog_ringbuf_free(vmlog_ringbuf *ring)
1199 vmlog_ringbuf_check_invariants(ring);
1201 vmlog_file_close(&(ring->file));
1203 VMLOG_FREE_ARRAY(vmlog_log_entry,ring->bufsize,ring->buf);
1204 VMLOG_FREE(vmlog_ringbuf,ring);
1207 static int vmlog_ringbuf_read(vmlog_ringbuf *ring,vmlog_log_entry *buf,
1208 vmlog_seq_t seq,int n)
1213 ofs = seq * sizeof(vmlog_log_entry);
1214 if (ofs != ring->file.ofs)
1215 vmlog_file_seek(&(ring->file),ofs);
1218 /* fprintf(stdout,"vmlog_ringbuf_read(%p,%d,%d)\n",
1219 (void*)ring,buf-ring->buf,n); */
1221 r = read(ring->file.fd,buf,n * sizeof(vmlog_log_entry));
1222 } while (r == -1 && errno == EINTR);
1225 vmlog_die("reading from file: %s: %s",ring->file.fname,strerror(errno));
1227 ring->file.ofs += r;
1229 if (r % sizeof(vmlog_log_entry) != 0) {
1231 vmlog_warn("partial log entry read from file: %s",ring->file.fname);
1234 return r / sizeof(vmlog_log_entry);
1237 static int vmlog_ringbuf_fill_forward(vmlog_ringbuf *ring,vmlog_log_entry *fillstart,
1238 vmlog_log_entry *fillend,vmlog_seq_t seq,int len)
1243 vmlog_log_entry *oldend;
1246 fprintf(stdout,"vmlog_ringbuf_fill_forward(%p,%d,%d," VMLOG_SEQ_FMT ",%d)\n",
1247 (void*)ring,fillstart-ring->buf,fillend-ring->buf,seq,len);
1250 vmlog_ringbuf_check_invariants(ring);
1252 space = fillend - fillstart;
1253 n = (len <= space) ? len : space;
1258 read = vmlog_ringbuf_read(ring,fillstart,seq,n);
1263 ring->end = fillstart + read;
1264 ring->debug_availafter += read;
1266 if (ring->cur == ring->bufend)
1267 ring->cur = ring->buf;
1269 if (ring->start >= fillstart && ring->start != oldend) {
1270 /* check if old entries have been overwritten */
1271 if (ring->start <= ring->end) {
1272 ring->debug_availbefore -=
1273 ring->end - ring->start + 1;
1274 ring->start = ring->end + 1;
1275 if (ring->start >= ring->bufend) {
1276 ring->start = ring->buf;
1277 ring->debug_availbefore = ring->cur - ring->start;
1282 vmlog_ringbuf_check_invariants(ring);
1287 static int vmlog_ringbuf_fill_backward(vmlog_ringbuf *ring,vmlog_log_entry *fillstart,
1288 vmlog_log_entry *fillend,vmlog_seq_t seq,int len)
1293 vmlog_log_entry *oldstart;
1294 vmlog_log_entry *oldend;
1297 fprintf(stdout,"vmlog_ringbuf_fill_backward(%p,%d,%d," VMLOG_SEQ_FMT ",%d)\n",
1298 (void*)ring,fillstart-ring->buf,fillend-ring->buf,seq,len);
1301 vmlog_ringbuf_check_invariants(ring);
1303 space = fillend - fillstart;
1304 n = (len <= space) ? len : space;
1310 fillstart += space - n;
1312 read = vmlog_ringbuf_read(ring,fillstart,seq,n);
1314 vmlog_die("could not read backward in file: %s: %s",
1315 ring->file.fname,strerror(errno));
1317 oldstart = ring->start;
1318 ring->start = fillstart;
1319 ring->debug_availbefore += read;
1322 if (ring->end <= fillend && ring->end != oldstart) {
1323 /* check if old entries have been overwritten */
1324 if (ring->start <= ring->end) {
1325 ring->debug_availafter -=
1326 ring->end - ring->start + 1;
1327 ring->end = ring->start - 1;
1329 if (ring->end <= ring->buf) {
1330 ring->end = ring->bufend;
1331 if (ring->cur == ring->buf && ring->end == ring->bufend)
1332 ring->cur = ring->bufend;
1333 ring->debug_availafter = ring->end - ring->cur;
1338 if (ring->end == ring->buf) {
1339 assert(oldstart == oldend);
1340 ring->end = ring->bufend;
1343 if (ring->cur == ring->buf && ring->end == ring->bufend)
1344 ring->cur = ring->bufend;
1346 vmlog_ringbuf_check_invariants(ring);
1351 int vmlog_ringbuf_fill(vmlog_ringbuf *ring,int len)
1355 vmlog_log_entry *fillend;
1365 vmlog_ringbuf_check_invariants(ring);
1368 if (ring->end >= ring->cur) {
1369 /* case A'1: ring->end >= ring->start */
1372 /* ------OOOOO#########----------- */
1374 /* buf start cur end bufend */
1376 /* case B'1: ring->end < ring->start */
1379 /* OOOOOOOOOOO#########----OOOOOOO */
1381 /* buf cur end st. bufend */
1383 /* fill space at end of buf */
1384 seq = ring->seq + (ring->end - ring->cur);
1385 read = vmlog_ringbuf_fill_forward(ring,ring->end,ring->bufend,
1390 if (ring->end != ring->bufend)
1391 goto no_more_entries;
1393 /* case A'1: ring->end >= ring->start */
1396 /* ------OOOOO#################### */
1398 /* buf start cur end==bufend */
1400 /* case B'1: ring->end < ring->start */
1403 /* OOOOOOOOOOO#################### */
1405 /* buf==start cur end==bufend */
1407 /* fill space at beg of buf */
1408 seq = ring->seq + (ring->end - ring->cur);
1409 fillend = (ring->cur == ring->bufend) ? ring->bufend : (ring->cur - 1);
1410 read = vmlog_ringbuf_fill_forward(ring,ring->buf,fillend,
1416 /* ring->end < ring->cur */
1419 assert(ring->end < ring->start);
1421 /* case B'2: ring->end < ring->start */
1424 /* #####------OOOOOO############## */
1426 /* buf end start cur bufend */
1428 /* fill space in middle of buf */
1429 seq = ring->seq + (ring->bufend - ring->cur) + (ring->end - ring->buf);
1430 read = vmlog_ringbuf_fill_forward(ring,ring->end,ring->cur - 1,
1439 if (len > ring->seq)
1442 if (ring->start <= ring->cur) {
1443 /* case A'1: ring->end >= ring->start */
1446 /* ------#####OOOOOOOOO----------- */
1448 /* buf start cur end bufend */
1450 /* case B'2: ring->end < ring->start */
1453 /* OOOOO------######OOOOOOOOOOOOOO */
1455 /* buf end start cur bufend */
1457 /* fill space at beg of buf */
1458 seq = ring->seq - (ring->cur - ring->buf);
1459 read = vmlog_ringbuf_fill_backward(ring,ring->buf,ring->start,
1464 if (ring->start != ring->buf)
1465 goto no_more_entries;
1467 /* case A'1: ring->end >= ring->start */
1469 /* vvvvvvvvvvvvvvvvvvv */
1470 /* ###########OOOOOOOOO----------- */
1472 /* buf=start cur end bufend */
1474 /* case B'2: ring->end < ring->start */
1477 /* #################OOOOOOOOOOOOOO */
1479 /* buf=start cur end=bufend */
1481 /* fill space at end of buf */
1482 seq -= (ring->bufend - ring->cur - 1);
1483 read = vmlog_ringbuf_fill_backward(ring,ring->cur+1,ring->bufend,
1489 /* ring->start > ring->cur */
1491 /* case B'1: ring->end < ring->start */
1494 /* ###########OOOOOOOOO----####### */
1496 /* buf cur end st. bufend */
1499 assert(ring->end < ring->start);
1501 /* fill space in middle of buf */
1502 seq = ring->seq - (ring->cur - ring->buf) - (ring->bufend - ring->cur - 1);
1503 read = vmlog_ringbuf_fill_backward(ring,ring->cur + 1,ring->start,
1511 vmlog_ringbuf_check_invariants(ring);
1516 static void vmlog_ringbuf_reset(vmlog_ringbuf *ring)
1518 ring->start = ring->buf;
1519 ring->cur = ring->buf;
1520 ring->end = ring->buf;
1522 ring->debug_availbefore = 0;
1523 ring->debug_availafter = 0;
1525 vmlog_ringbuf_check_invariants(ring);
1528 static int vmlog_ringbuf_advance(vmlog_ringbuf *ring,int diff)
1533 if (ring->end >= ring->start) {
1536 space = ring->end - ring->cur;
1547 ring->debug_availbefore += diff;
1548 ring->debug_availafter -= diff;
1553 if (ring->end >= ring->cur)
1554 goto advance_cur_to_end;
1556 space = ring->bufend - ring->cur;
1558 goto simple_advance;
1560 ring->cur = ring->buf - space;
1561 goto advance_cur_to_end;
1564 else if (diff < 0) {
1565 if (ring->end >= ring->start) {
1567 advance_cur_to_start:
1568 space = ring->cur - ring->start;
1575 goto simple_advance;
1579 if (ring->cur >= ring->start)
1580 goto advance_cur_to_start;
1582 space = ring->cur - ring->buf;
1584 goto simple_advance;
1586 ring->cur = ring->bufend + space;
1587 goto advance_cur_to_start;
1595 void vmlog_ringbuf_seek(vmlog_ringbuf *ring,vmlog_seq_t seq)
1599 vmlog_ringbuf_check_invariants(ring);
1601 diff = seq - ring->seq;
1602 if (abs(diff) < ring->bufsize)
1603 diff -= vmlog_ringbuf_advance(ring,(int)diff);
1608 vmlog_ringbuf_reset(ring);
1609 vmlog_file_seek(&(ring->file),seq * sizeof(vmlog_log_entry));
1612 vmlog_ringbuf_check_invariants(ring);
1615 vmlog_log_entry * vmlog_ringbuf_next(vmlog_ringbuf *ring,int prefetch)
1617 vmlog_ringbuf_check_invariants(ring);
1620 if (ring->end >= ring->start) {
1622 if (ring->cur < ring->end) {
1623 ring->debug_availafter--;
1624 ring->debug_availbefore++;
1631 if (ring->end >= ring->cur) {
1632 if (ring->cur < ring->end) {
1633 ring->debug_availafter--;
1634 ring->debug_availbefore++;
1640 if (ring->cur < ring->bufend) {
1645 if (++ring->cur == ring->bufend)
1646 ring->cur = ring->buf;
1647 ring->debug_availafter--;
1648 ring->debug_availbefore++;
1654 if (!vmlog_ringbuf_fill(ring,prefetch))
1658 assert(0); /* NOT REACHED */
1662 vmlog_log_entry * vmlog_ringbuf_prev(vmlog_ringbuf *ring,int prefetch)
1664 vmlog_ringbuf_check_invariants(ring);
1667 if (ring->end >= ring->start) {
1669 if (ring->cur > ring->start) {
1670 ring->debug_availafter++;
1671 ring->debug_availbefore--;
1678 if (ring->cur >= ring->start) {
1679 if (ring->cur > ring->start) {
1680 ring->debug_availafter++;
1681 ring->debug_availbefore--;
1687 if (--ring->cur < ring->buf)
1688 ring->cur = ring->bufend - 1;
1690 ring->debug_availafter++;
1691 ring->debug_availbefore--;
1696 if (!vmlog_ringbuf_fill(ring,-prefetch))
1700 assert(0); /* NOT REACHED */
1704 /*** option parsing **************************************************/
1706 int vmlog_opt_parse_seq(const char *arg,int len,vmlog_seq_t *seq)
1717 buf = vmlog_strdup(arg,len);
1718 *seq = strtoll(buf,&endptr,10);
1720 r = (endptr[0] == 0);
1722 VMLOG_FREE_ARRAY(char,len+1,buf);
1726 int vmlog_opt_parse_range(const char *arg,vmlog_seq_t *start,vmlog_seq_t *end)
1731 sep = strchr(arg,':');
1734 if (!vmlog_opt_parse_seq(arg,len,start))
1745 if (!vmlog_opt_parse_seq(arg,len,start))
1749 len = strlen(arg) - len - 1;
1751 *end = VMLOG_SEQ_MAX;
1754 if (!vmlog_opt_parse_seq(sep+1,len,end))
1760 static int vmlog_opt_parse_one_option(vmlog_options *opts, const char *arg, const char *nextarg)
1764 if (strncmp(arg,"-vmlog:",7) != 0) {
1768 /* a vmlog option */
1771 if (strcmp(arg,"-vmlog:prefix") == 0) {
1773 vmlog_die("expected a prefix after -vmlog:prefix");
1774 opts->prefix = vmlog_strdup(nextarg,strlen(nextarg));
1777 else if (strcmp(arg,"-vmlog:strings") == 0) {
1779 vmlog_die("expected a prefix after -vmlog:strings");
1780 opts->stringprefix = vmlog_strdup(nextarg,strlen(nextarg));
1783 else if (strcmp(arg,"-vmlog:ignore") == 0) {
1785 vmlog_die("expected a prefix after -vmlog:ignore");
1786 opts->ignoreprefix = vmlog_strdup(nextarg,strlen(nextarg));
1790 vmlog_die("unknown -vmlog:... option: %s",arg);
1796 vmlog_options *vmlog_opt_parse_cmd_line(int *pargc,char **argv)
1800 vmlog_options *opts;
1806 VMLOG_XZNEW(opts,vmlog_options);
1808 if (*pargc && argv[0])
1809 opts->progname = vmlog_strdup(argv[0],strlen(argv[0]));
1812 while (i < *pargc) {
1815 left = *pargc - i - 1;
1817 eat = vmlog_opt_parse_one_option(opts,arg,
1818 (left) ? argv[i+1] : NULL);
1825 /* remove the option from the command line */
1827 memmove(argv + i,argv + i + eat,sizeof(char*) * (*pargc - (i+eat)));
1834 vmlog_options *vmlog_opt_parse_vmargs(JavaVMInitArgs *vmargs)
1838 vmlog_options *opts;
1843 VMLOG_XZNEW(opts,vmlog_options);
1846 while (i < vmargs->nOptions) {
1847 arg = vmargs->options[i].optionString;
1849 eat = vmlog_opt_parse_one_option(opts,arg,
1850 (i+1 < vmargs->nOptions) ? vmargs->options[i+1].optionString : NULL);
1857 /* remove the option from the command line */
1859 memmove(vmargs->options + i,vmargs->options + i + eat,
1860 sizeof(JavaVMOption) * (vmargs->nOptions - (i+eat)));
1861 vmargs->nOptions -= eat;
1867 void vmlog_opt_free(vmlog_options *opts)
1873 VMLOG_FREE_ARRAY(char,strlen(opts->prefix)+1,opts->prefix);
1874 if (opts->stringprefix)
1875 VMLOG_FREE_ARRAY(char,strlen(opts->stringprefix)+1,opts->stringprefix);
1876 if (opts->ignoreprefix)
1877 VMLOG_FREE_ARRAY(char,strlen(opts->ignoreprefix)+1,opts->ignoreprefix);
1879 VMLOG_FREE(vmlog_options,opts);
1882 /* vim: noet ts=8 sw=8