* contrib/vmlog: Committed vmlog 0.0.5.
authoredwin <none@none>
Sun, 1 Apr 2007 10:50:39 +0000 (10:50 +0000)
committeredwin <none@none>
Sun, 1 Apr 2007 10:50:39 +0000 (10:50 +0000)
* THIRDPARTY: Added copyright notice for vmlog.

26 files changed:
THIRDPARTY
contrib/vmlog/COPYING [new file with mode: 0644]
contrib/vmlog/Makefile [new file with mode: 0644]
contrib/vmlog/README [new file with mode: 0644]
contrib/vmlog/t/concat.c [new file with mode: 0644]
contrib/vmlog/t/file.c [new file with mode: 0644]
contrib/vmlog/t/hash.c [new file with mode: 0644]
contrib/vmlog/t/log.c [new file with mode: 0644]
contrib/vmlog/t/memdup.c [new file with mode: 0644]
contrib/vmlog/t/opt.c [new file with mode: 0644]
contrib/vmlog/t/prolog.h [new file with mode: 0644]
contrib/vmlog/t/ring.c [new file with mode: 0644]
contrib/vmlog/t/string.c [new file with mode: 0644]
contrib/vmlog/t/tags.c [new file with mode: 0644]
contrib/vmlog/t/threadhash.c [new file with mode: 0644]
contrib/vmlog/vmlog.c [new file with mode: 0644]
contrib/vmlog/vmlog.h [new file with mode: 0644]
contrib/vmlog/vmlog_cacao.c [new file with mode: 0644]
contrib/vmlog/vmlog_cacao.h [new file with mode: 0644]
contrib/vmlog/vmlog_cacao.patch [new file with mode: 0644]
contrib/vmlog/vmlog_jamvm.c [new file with mode: 0644]
contrib/vmlog/vmlog_jamvm.h [new file with mode: 0644]
contrib/vmlog/vmlog_jamvm.patch [new file with mode: 0644]
contrib/vmlog/vmlogdiff.c [new file with mode: 0644]
contrib/vmlog/vmlogdump.c [new file with mode: 0644]
contrib/vmlog/vmlogindex.c [new file with mode: 0644]

index d1931e0420933c6b8e08fb36306289b845428a23..e63655fdeeceb552c91686c2aa85420d8bc9930f 100644 (file)
@@ -197,3 +197,16 @@ notice and this notice are preserved.
    IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
    OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
    IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+* contrib/vmlog
+
+Copyright (C) 2006 Edwin Steiner <edwin.steiner@gmx.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+See contrib/vmlog/COPYING for the full text of the license.
+
diff --git a/contrib/vmlog/COPYING b/contrib/vmlog/COPYING
new file mode 100644 (file)
index 0000000..3912109
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/contrib/vmlog/Makefile b/contrib/vmlog/Makefile
new file mode 100644 (file)
index 0000000..3821b68
--- /dev/null
@@ -0,0 +1,50 @@
+VERSION=0.0.5
+RELEASENAME=vmlog-$(VERSION)
+TARBALL=releases/$(RELEASENAME).tar.gz
+
+CFLAGS=-g -ansi -std=c99 -pedantic -Wall -Wno-long-long -Wno-unused-function -I/home/edwin/local/classpath/include
+#CFLAGS_TOOL=-O2 -DNDEBUG -ansi -std=c99 -march=pentium4m -pedantic -Wall -Wno-long-long
+CFLAGS_TOOL=-g3 -pedantic -ansi -std=c99 -Wall -Wno-long-long -Wno-unused-function
+
+all: vmlog.o vmlogdump vmlogindex vmlogdiff
+
+runtests: t/hash.run t/memdup.run t/string.run t/threadhash.run t/file.run t/log.run t/concat.run t/ring.run t/opt.run t/tags.run
+
+test: cleantests runtests
+       cat TESTRESULTS
+
+cleantests:
+       @rm -f TESTIDX TESTSTR TESTLOG? TESTFILE TESTRESULTS
+       
+%.run: %.c t/prolog.h vmlog.c vmlog.h
+       $(CC) $(CFLAGS) -I. -o $* $<
+       echo $* >>TESTRESULTS
+       { $* || echo "FAILED: exitcode == $$?" ; } | uniq -c >>TESTRESULTS
+
+vmlogdump: vmlogdump.c vmlog.c vmlog.h
+       $(CC) $(CFLAGS_TOOL) -o $@ vmlogdump.c vmlog.c
+vmlogindex: vmlogindex.c vmlog.c vmlog.h
+       $(CC) $(CFLAGS_TOOL) -o $@ vmlogindex.c vmlog.c
+vmlogdiff: vmlogdiff.c vmlog.c vmlog.h
+       $(CC) $(CFLAGS_TOOL) -o $@ vmlogdiff.c vmlog.c
+
+cscope:
+       cscope -b *.[ch] t/*.[ch]
+
+clean: cleantests
+       rm -f *.o
+       rm -f vmlogdump vmlogindex vmlogdiff
+       rm -f cscope.out
+       rm -f t/hash t/memdup t/string t/threadhash t/file t/log t/concat t/ring t/opt t/tags
+
+checkedin:
+       if (cg-status | grep ^[AMD]) ; then echo "difference to repository!"; exit 1; else /bin/true ; fi
+
+distprep: clean all test clean
+       mkdir $(RELEASENAME)
+       cp -a --parents `git-ls-files` $(RELEASENAME)
+       tar cfz $(TARBALL) $(RELEASENAME)/*
+       rm -rf $(RELEASENAME)
+
+dist: checkedin distprep
+       cg-tag $(VERSION)
diff --git a/contrib/vmlog/README b/contrib/vmlog/README
new file mode 100644 (file)
index 0000000..b95a813
--- /dev/null
@@ -0,0 +1,125 @@
+vmlog - high-speed logging for free VMs
+Copyright (c) 2006 Edwin Steiner <edwin.steiner@gmx.net>
+-----------------------------------------------------------------------
+
+About
+-----
+
+vmlog is a software module for doing fast call logging in free Java VMs and a
+set of tools for examining the created logfiles.
+
+Some features of vmlog:
+
+       * high data density because of binary formats with hashed strings
+       * high speed (ie. you can run big applications with full logging)
+       * small, non-intrusive interface to several VMs
+       * clean separation of threads
+       * can ignore a user-specified list of methods
+
+Right now there are complete interfaces for the following VMs:
+
+       * CACAO
+       * jamvm 1.4.2
+
+       
+License
+-------
+       
+vmlog is free software distributed under the GPL. See the file COPYING
+for details.
+
+
+How to compile a VM with vmlog
+------------------------------
+
+You need to do two things:
+
+       * patch the VM code with the provided vmlog_<vm>.patch
+       * configure the VM with CFLAGS that include -Ivmlogdir
+         where vmlogdir is the directory the contains the vmlog
+         source code
+
+You can then compile your VM with vmlog. 
+
+
+Using vmlog
+-----------
+
+To use it call the VM with at least the following option added to the command
+line:
+
+       -vmlog:prefix PREFIX
+
+PREFIX is an arbitrary string that will be used as a prefix for the generated
+file names.
+
+Note: For cacao you currently also have to add `-verbose:call` to activate
+the logging.
+
+
+Ignore Lists
+------------
+
+If you want vmlog to ignore certain methods do the following:
+
+       * put the full names of the methods (as displayed by vmlog)
+         into a file, say "vmloginore.txt".
+
+       * type
+
+               vmlogindex vmloginore vmloginore.txt
+
+       * add the following option to your VM command line:
+
+               -vmlog:ignore vmloginore
+
+
+The Tools
+---------
+
+After running something with your prepared VM, you will find the following
+files:
+
+    PREFIX.idx          string index
+       PREFIX.str          string data
+       PREFIX.#.log        log for thread number #
+
+Some things you can do now:
+
+       * Count called methods:
+
+               vmlogdump PREFIX -c
+
+       * Show open call frames at the end of the log:
+
+               vmlogdump PREFIX -o
+       
+       * Dump all strings in the hash:
+
+               vmlogdump PREFIX -s
+
+       * Print an indented call trace:
+
+               vmlogdump PREFIX
+
+
+Comparing logs
+--------------
+
+An important feature of vmlog is that you can compare the logs created by
+different VMs. To do this first do a normal vmlog run with the first VM.
+Then start the second VM with theese options:
+
+       -vmlog:prefix OTHERPREFIX -vmlog:strings PREFIX
+
+where PREFIX is the PREFIX you used for the first VM run and OTHERPREFIX
+is a different string.
+
+You can now compare the logs (for example of the first thread):
+
+       vmlogdiff OTHERPREFIX PREFIX.0.log OTHERPREFIX.0.log
+
+Note: vmlogdiff is currently not fast enough, yet. You can add the
+`-v` option to it to see what it is doing (on stderr) at least.
+
+
diff --git a/contrib/vmlog/t/concat.c b/contrib/vmlog/t/concat.c
new file mode 100644 (file)
index 0000000..46b4da0
--- /dev/null
@@ -0,0 +1,34 @@
+#include "t/prolog.h"
+
+int main(int argc,char **argv) 
+{
+       char *p;
+       int len;
+
+       p = vmlog_concat3("foo","","bar",&len);
+       NOTNULL(p);
+       IS(len,6);
+       TRUE(strcmp(p,"foobar") == 0);
+
+       p = vmlog_concat3("","","",&len);
+       NOTNULL(p);
+       IS(len,0);
+       TRUE(strcmp(p,"") == 0);
+
+       p = vmlog_concat3("This is a"," stupid ","test.",&len);
+       NOTNULL(p);
+       IS(len,strlen("This is a stupid test."));
+       TRUE(strcmp(p,"This is a stupid test.") == 0);
+
+       p = vmlog_concat4len("eins",2,"zwei",4,"drei",0,"vier",4,&len);
+       NOTNULL(p);
+       IS(len,10);
+       TRUE(strcmp(p,"eizweivier") == 0);
+       
+       return 0;
+}
+
+/* vim: noet ts=8 sw=8
+ */
+
+
diff --git a/contrib/vmlog/t/file.c b/contrib/vmlog/t/file.c
new file mode 100644 (file)
index 0000000..e9cee58
--- /dev/null
@@ -0,0 +1,58 @@
+#include "t/prolog.h"
+
+int main(int argc,char **argv) 
+{
+       vmlog_file file;
+       int x;
+       int fd;
+       struct stat st;
+       int r;
+       int len;
+       char buf[100];
+
+       vmlog_file_open(&file,"TESTFILE",vmlogTruncateAppend);
+       IS(file.fnamelen,8);
+       TRUE(strcmp(file.fname,"TESTFILE") == 0);
+
+       len = 0;
+       IS(file.ofs,0);
+       vmlog_file_append(&file,"foo",3); len += 3;
+       IS(file.ofs,len);
+       vmlog_file_append(&file,"",1); len += 1;
+       IS(file.ofs,len);
+       vmlog_file_append(&file,"",0);
+       IS(file.ofs,len);
+       x = 0;
+       vmlog_file_append(&file,&x,sizeof(int)); len += sizeof(int);
+       IS(file.ofs,len);
+       vmlog_file_append(&file,"ENDE",4); len += 4;
+       IS(file.ofs,len);
+       
+       vmlog_file_close(&file);
+       IS(file.fd,-1);
+       IS(file.fname,NULL);
+       IS(file.fnamelen,0);
+
+       fd = open("TESTFILE",O_RDONLY);
+       TRUE(fd != -1);
+       r = fstat(fd,&st);
+       TRUE(r != -1);
+       IS(st.st_size,len);
+
+       r = read(fd,buf,len);
+       IS(r,len);
+
+       TRUE(memcmp(buf,"foo",3) == 0);
+       IS(buf[3],0);
+       TRUE(*(int*)(buf+4) == 0);
+       TRUE(memcmp(buf+len-4,"ENDE",4) == 0);
+       
+       close(fd);
+
+       return 0;
+}
+
+
+/* vim: noet ts=8 sw=8
+ */
+
diff --git a/contrib/vmlog/t/hash.c b/contrib/vmlog/t/hash.c
new file mode 100644 (file)
index 0000000..dc9d532
--- /dev/null
@@ -0,0 +1,31 @@
+#include "t/prolog.h"
+
+static int destruct_counter = 0;
+
+static void test_destuctor(vmlog_hash_entry *entry) {
+       printf("# destroying entry %p\n",(void *)entry);
+       destruct_counter++;
+}
+
+int main(int argc,char **argv) 
+{
+       vmlog_hash_table ht;
+
+       vmlog_hashtable_init(&ht,10);
+
+       IS(ht.size,10);
+       IS(ht.nentries,0);
+
+       vmlog_hashtable_free(&ht,test_destuctor);
+
+       IS(destruct_counter,10);
+       IS(ht.size,0);
+       IS(ht.nentries,0);
+       IS(ht.table,0);
+
+       return 0;
+}
+
+
+/* vim: noet ts=8 sw=8
+ */
diff --git a/contrib/vmlog/t/log.c b/contrib/vmlog/t/log.c
new file mode 100644 (file)
index 0000000..423fe33
--- /dev/null
@@ -0,0 +1,64 @@
+#include "t/prolog.h"
+
+int main(int argc,char **argv) 
+{
+       vmlog_log *vml;
+       int i;
+       int r;
+       int fd;
+       struct stat st;
+       int cumlen;
+       void *t1 = (void*)123;
+       char *name;
+       vmlog_thread_log *tlog;
+       vmlog_log_entry logent;
+
+       vml = vmlog_log_new(NULL,1);
+       NOTNULL(vml);
+
+       vmlog_file_open(&(vml->idxfile),"TESTIDX",vmlogTruncateAppend);
+       vmlog_file_open(&(vml->strfile),"TESTSTR",vmlogTruncateAppend);
+
+       tlog = vmlog_get_thread_log(vml,t1);
+       vmlog_file_open(&(tlog->logfile),"TESTLOG1",vmlogTruncateAppend);
+
+       vmlog_log_enter(vml,t1,"foo(II)V",8);
+       name = "bar(Ljava/lang/Object;)Z"; vmlog_log_enter(vml,t1,name,strlen(name));
+       name = "bar(Ljava/lang/Object;)Z"; vmlog_log_leave(vml,t1,name,strlen(name));
+       vmlog_log_leave(vml,t1,"foo(II)V",8);
+
+       vmlog_log_free(vml);
+       vml = NULL;
+
+       fd = open("TESTLOG1",O_RDONLY);
+       TRUE(fd != -1);
+       r = fstat(fd,&st);
+       TRUE(r != -1);
+       IS(st.st_size,4*sizeof(vmlog_log_entry));
+
+       r = read(fd,&logent,sizeof(vmlog_log_entry));
+       TRUE(r == sizeof(vmlog_log_entry));
+       IS(logent.tag,VMLOG_TAG_ENTER);
+       
+       r = read(fd,&logent,sizeof(vmlog_log_entry));
+       TRUE(r == sizeof(vmlog_log_entry));
+       IS(logent.tag,VMLOG_TAG_ENTER);
+       
+       r = read(fd,&logent,sizeof(vmlog_log_entry));
+       TRUE(r == sizeof(vmlog_log_entry));
+       IS(logent.tag,VMLOG_TAG_LEAVE);
+       
+       r = read(fd,&logent,sizeof(vmlog_log_entry));
+       TRUE(r == sizeof(vmlog_log_entry));
+       IS(logent.tag,VMLOG_TAG_LEAVE);
+       
+       close(fd);
+
+       return 0;
+}
+
+/* vim: noet ts=8 sw=8
+ */
+
+
+
diff --git a/contrib/vmlog/t/memdup.c b/contrib/vmlog/t/memdup.c
new file mode 100644 (file)
index 0000000..4a9a7f3
--- /dev/null
@@ -0,0 +1,20 @@
+#include "t/prolog.h"
+
+int main(int argc,char **argv) 
+{
+       char *str = "this is a test";
+       char *p;
+
+       p = (char*) vmlog_memdup(str,strlen(str));
+       NOTNULL(p);
+       IS(memcmp(p,str,strlen(str)),0);
+
+       p = (char*) vmlog_memdup("",0);
+       NOTNULL(p);
+
+       return 0;
+}
+
+/* vim: noet ts=8 sw=8
+ */
+
diff --git a/contrib/vmlog/t/opt.c b/contrib/vmlog/t/opt.c
new file mode 100644 (file)
index 0000000..3ed70bb
--- /dev/null
@@ -0,0 +1,104 @@
+#include "t/prolog.h"
+
+int main(int argc,char **argv) 
+{
+       char *p;
+       int len;
+       int r;
+       vmlog_seq_t seq,end;
+       vmlog_options *opts;
+       char **myargv;
+       int myargc;
+
+       seq = 7777;
+       r = vmlog_opt_parse_seq("123",0,&seq);
+       IS(r,0);
+       IS(seq,7777);
+       
+       seq = 7777;
+       r = vmlog_opt_parse_seq("123",1,&seq);
+       IS(r,1);
+       IS(seq,1);
+       
+       seq = 7777;
+       r = vmlog_opt_parse_seq("123",2,&seq);
+       IS(r,1);
+       IS(seq,12);
+       
+       seq = 7777;
+       r = vmlog_opt_parse_seq("123",3,&seq);
+       IS(r,1);
+       IS(seq,123);
+       
+       seq = 7777;
+       r = vmlog_opt_parse_seq("123",4,&seq);
+       IS(r,1);
+       IS(seq,123);
+       
+       seq = 7777;
+       r = vmlog_opt_parse_seq("123x",4,&seq);
+       IS(r,0);
+       IS(seq,123);
+
+       seq = 7777; end = 8888;
+       r = vmlog_opt_parse_range("345",&seq,&end);
+       IS(r,1);
+       IS(seq,345);
+       IS(end,345);
+
+       seq = 7777; end = 8888;
+       r = vmlog_opt_parse_range("345:",&seq,&end);
+       IS(r,1);
+       IS(seq,345);
+       IS(end,LLONG_MAX);
+
+       seq = 7777; end = 8888;
+       r = vmlog_opt_parse_range(":345",&seq,&end);
+       IS(r,1);
+       IS(seq,0);
+       IS(end,345);
+
+       seq = 7777; end = 8888;
+       r = vmlog_opt_parse_range("234:345",&seq,&end);
+       IS(r,1);
+       IS(seq,234);
+       IS(end,345);
+
+       seq = 7777; end = 8888;
+       r = vmlog_opt_parse_range("234x:345",&seq,&end);
+       IS(r,0);
+
+       VMLOG_XZNEW_ARRAY(myargv,char*,10);
+
+       myargv[0] = "progname";
+       myargv[1] = "xyz";
+       myargv[2] = "-vmlog:ignore";
+       myargv[3] = "testIGN";
+       myargv[4] = "bar";
+       myargv[5] = "-vmlog:prefix";
+       myargv[6] = "theprefix";
+       myargv[7] = "last";
+       myargc = 8;
+
+       opts = vmlog_opt_parse_cmd_line(&myargc,myargv);
+
+       NOTNULL(opts);
+       TRUE(strcmp(opts->progname,"progname") == 0);
+       TRUE(strcmp(opts->prefix,"theprefix") == 0);
+       TRUE(strcmp(opts->ignoreprefix,"testIGN") == 0);
+       TRUE(opts->stringprefix == NULL);
+
+       IS(myargc,4);
+       TRUE(strcmp(myargv[0],"progname") == 0);
+       TRUE(strcmp(myargv[1],"xyz") == 0);
+       TRUE(strcmp(myargv[2],"bar") == 0);
+       TRUE(strcmp(myargv[3],"last") == 0);
+
+       finished();
+}
+
+/* vim: noet ts=8 sw=8
+ */
+
+
+
diff --git a/contrib/vmlog/t/prolog.h b/contrib/vmlog/t/prolog.h
new file mode 100644 (file)
index 0000000..9d1cf87
--- /dev/null
@@ -0,0 +1,23 @@
+#include "vmlog.h"
+#include "vmlog.c"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#define IS(a,b)  do{ if ((a) == (b)) { printf("ok\n"); } else { printf("FAILED: " #a " == " #b "\n"); } }while(0)
+#define TRUE(a)  do{ if ((a)) { printf("ok\n"); } else { printf("FAILED: " #a "\n"); } }while(0)
+#define NOTNULL(a)  do{ if ((a) != NULL) { printf("ok\n"); } else { printf("FAILED: " #a " != NULL\n"); } }while(0)
+
+void finished()
+{
+       printf("finished\n");
+       exit(0);
+}
+
+/* vim: noet ts=8 sw=8
+ */
+
diff --git a/contrib/vmlog/t/ring.c b/contrib/vmlog/t/ring.c
new file mode 100644 (file)
index 0000000..b0647b7
--- /dev/null
@@ -0,0 +1,137 @@
+#include "t/prolog.h"
+
+const char *testfname = "TESTFILE";
+const int testdelta = 1000000;
+const int testsize = 100;
+const int testbufsize = 11;
+
+vmlog_ringbuf *ring;
+       
+static void prepare_test_file(void)
+{
+       vmlog_log_entry logent;
+       vmlog_file file;
+       int i;
+
+       vmlog_file_open(&file,testfname,vmlogTruncateAppend);
+       for (i=0; i<testsize; ++i) {
+               logent.tag = i % 5;
+               logent.index = i + testdelta;
+               vmlog_file_append(&file,&logent,sizeof(vmlog_log_entry));
+       }
+
+       vmlog_file_close(&file);
+}
+
+static void check_logent(vmlog_log_entry *logent,int seq)
+{
+       TRUE(logent->tag == seq % 5 && logent->index == seq + testdelta);
+       if (!(logent->tag == seq % 5 && logent->index == seq + testdelta)) {
+               fprintf(stdout,"THE FAILED ONE:\n");
+               vmlog_ringbuf_visualize(ring);
+       }
+}
+
+static void check_ringbuf(void)
+{
+       int i;
+       int n;
+       vmlog_log_entry *logent;
+
+       vmlog_ringbuf_check_invariants(ring);
+
+       n = ring->debug_availbefore;
+       logent = ring->cur;
+       for (i=0; i<n; ++i) {
+               if (--logent < ring->buf)
+                       logent = ring->bufend - 1;
+
+               check_logent(logent,ring->seq - i - 1);
+       }
+       
+       n = ring->debug_availafter;
+       logent = ring->cur;
+       for (i=0; i<n; ++i) {
+               check_logent(logent,ring->seq + i);
+               
+               if (++logent >= ring->bufend)
+                       logent = ring->buf;
+       }
+}
+
+static void test_forward_iteration(int base,int n,int expect)
+{
+       vmlog_log_entry *logent;
+       int count;
+
+       count = 0;
+       check_ringbuf();
+       while ((count < n) && (logent = vmlog_ringbuf_next(ring,1 + rand()%7))) {
+               check_logent(logent,base + count);
+               check_ringbuf();
+
+               count++;
+       }
+       check_ringbuf();
+
+       IS(count,expect);
+}
+
+static void test_backward_iteration(int base,int n,int expect)
+{
+       vmlog_log_entry *logent;
+       int count;
+
+       count = 0;
+       check_ringbuf();
+       while ((count < n) && (logent = vmlog_ringbuf_prev(ring,1 + rand()%7))) {
+               check_logent(logent,base - count - 1);
+               check_ringbuf();
+
+               count++;
+       }
+       check_ringbuf();
+
+       IS(count,expect);
+}
+
+#define MIN(a,b)  (((a) <= (b)) ? (a) : (b))
+
+static void test_random_walk(void)
+{
+       int i;
+       int steps;
+       int base;
+       
+       base = testsize/2;
+       vmlog_ringbuf_seek(ring,base);
+
+       for (i=0; i<1000; ++i) {
+               steps = rand()%20 - 10;
+               
+               if (steps > 0 || (steps == 0 && rand()%2)) {
+                       test_forward_iteration(base,steps,MIN(steps,testsize - base));
+                       base += MIN(steps,testsize - base);
+               }
+               else {
+                       test_backward_iteration(base,-steps,MIN(-steps,base));
+                       base -= MIN(-steps,base);
+               }
+       }
+}
+
+int main(int argc,char **argv) 
+{
+       prepare_test_file();
+
+       ring = vmlog_ringbuf_new(testfname,testbufsize);
+
+       test_forward_iteration(0,testsize+7,testsize);
+       test_backward_iteration(testsize,testsize+7,testsize);
+       test_random_walk();     
+
+       finished();
+}
+
+/* vim: noet ts=8 sw=8
+ */
diff --git a/contrib/vmlog/t/string.c b/contrib/vmlog/t/string.c
new file mode 100644 (file)
index 0000000..42fb18f
--- /dev/null
@@ -0,0 +1,83 @@
+#include "t/prolog.h"
+
+static int destruct_counter = 0;
+static int destruct_len = 0;
+
+static void test_destuctor(vmlog_hash_entry *entry) {
+       if (entry->data) {
+               destruct_counter++;
+               destruct_len += entry->len;
+       }
+       else {
+               IS(entry->len,0);
+               IS(entry->index,0);
+       }
+}
+
+int main(int argc,char **argv) 
+{
+       vmlog_log *vml;
+       int i;
+       int r;
+       int fd;
+       struct stat st;
+       int cumlen;
+
+       vml = vmlog_log_new(NULL,1);
+       NOTNULL(vml);
+
+       vmlog_file_open(&(vml->idxfile),"TESTIDX",vmlogTruncateAppend);
+       vmlog_file_open(&(vml->strfile),"TESTSTR",vmlogTruncateAppend);
+
+       cumlen = 0;
+       i = vmlog_get_string_index(vml,"foo",3);
+       IS(i,0);
+       IS(vml->stringhash.nentries,1);
+       cumlen += 3;
+
+       i = vmlog_get_string_index(vml,"foo",3);
+       IS(i,0);
+       IS(vml->stringhash.nentries,1);
+
+       i = vmlog_get_string_index(vml,"bar",3);
+       IS(i,1);
+       IS(vml->stringhash.nentries,2);
+       cumlen += 3;
+
+       i = vmlog_get_string_index(vml,"foo",3);
+       IS(i,0);
+       IS(vml->stringhash.nentries,2);
+
+       i = vmlog_get_string_index(vml,"",0);
+       IS(i,2);
+       IS(vml->stringhash.nentries,3);
+
+       i = vmlog_get_string_index(vml,"",0);
+       IS(i,2);
+       IS(vml->stringhash.nentries,3);
+
+       vmlog_hashtable_free(&(vml->stringhash),test_destuctor);
+       IS(destruct_counter,3);
+
+       vmlog_file_close(&(vml->idxfile));
+       vmlog_file_close(&(vml->strfile));
+
+       fd = open("TESTIDX",O_RDONLY);
+       TRUE(fd != -1);
+       r = fstat(fd,&st);
+       IS(st.st_size,3*sizeof(vmlog_string_entry));
+       close(fd);
+
+       fd = open("TESTSTR",O_RDONLY);
+       TRUE(fd != -1);
+       r = fstat(fd,&st);
+       IS(st.st_size,cumlen);
+       close(fd);
+
+       return 0;
+}
+
+/* vim: noet ts=8 sw=8
+ */
+
+
diff --git a/contrib/vmlog/t/tags.c b/contrib/vmlog/t/tags.c
new file mode 100644 (file)
index 0000000..f1da7ef
--- /dev/null
@@ -0,0 +1,44 @@
+#include "t/prolog.h"
+
+int main(int argc,char **argv) 
+{
+       int r;
+
+       r = vmlog_tag_from_name("enter",5);
+       IS(r,VMLOG_TAG_ENTER);
+    
+       r = vmlog_tag_from_name("leave",5);
+       IS(r,VMLOG_TAG_LEAVE);
+    
+       r = vmlog_tag_from_name("throw",5);
+       IS(r,VMLOG_TAG_THROW);
+    
+       r = vmlog_tag_from_name("catch",5);
+       IS(r,VMLOG_TAG_CATCH);
+    
+       r = vmlog_tag_from_name("unwnd",5);
+       IS(r,VMLOG_TAG_UNWND);
+    
+       r = vmlog_tag_from_name(NULL,0);
+       IS(r,-1);
+    
+       r = vmlog_tag_from_name(NULL,5);
+       IS(r,-1);
+    
+       r = vmlog_tag_from_name("enter",-1);
+       IS(r,-1);
+
+       r = vmlog_tag_from_name("enter",0);
+       IS(r,-1);
+    
+       r = vmlog_tag_from_name("enter",4);
+       IS(r,-1);
+       
+       r = vmlog_tag_from_name("enter",6);
+       IS(r,-1);
+
+       finished();
+}
+
+/* vim: noet ts=8 sw=8
+ */
diff --git a/contrib/vmlog/t/threadhash.c b/contrib/vmlog/t/threadhash.c
new file mode 100644 (file)
index 0000000..6f2015e
--- /dev/null
@@ -0,0 +1,68 @@
+#include "t/prolog.h"
+
+static int destruct_counter = 0;
+static int destruct_len = 0;
+
+static void test_destuctor(vmlog_hash_entry *entry) {
+       if (entry->data) {
+               destruct_counter++;
+               destruct_len += entry->len;
+       }
+       else {
+               IS(entry->len,0);
+               IS(entry->index,0);
+       }
+}
+
+int main(int argc,char **argv) 
+{
+       vmlog_log *vml;
+       vmlog_thread_log *tlog;
+       vmlog_thread_log *tlog1;
+       int i;
+       vmlog_thread_log *tlogs[100];
+
+       vml = vmlog_log_new(NULL,1);
+       NOTNULL(vml);
+
+       tlog = vmlog_get_thread_log(vml,(void*)123);
+       tlog1 = tlog;
+       NOTNULL(tlog);
+       IS(vml->threadhash.nentries,1);
+       IS(tlog->threadid,(void*)123);
+
+       tlog = vmlog_get_thread_log(vml,(void*)0);
+       NOTNULL(tlog);
+       IS(vml->threadhash.nentries,2);
+       IS(tlog->threadid,(void*)0);
+
+       tlog = vmlog_get_thread_log(vml,(void*)123);
+       NOTNULL(tlog);
+       IS(vml->threadhash.nentries,2);
+       IS(tlog,tlog1);
+       IS(tlog->threadid,(void*)123);
+
+       vmlog_hashtable_free(&(vml->threadhash),test_destuctor);
+       IS(destruct_counter,2);
+
+       vmlog_hashtable_init(&(vml->threadhash),5);
+       for (i=0; i<10; ++i) {
+               tlog = vmlog_get_thread_log(vml,(void*)(1000+i));
+               NOTNULL(tlog);
+               tlogs[i] = tlog;
+       }
+       for (i=0; i<10; ++i) {
+               tlog = vmlog_get_thread_log(vml,(void*)(1000+i));
+               IS(tlog,tlogs[i]);
+       }
+       destruct_counter = 0;
+       vmlog_hashtable_free(&(vml->threadhash),test_destuctor);
+       IS(destruct_counter,10);
+
+       return 0;
+}
+
+/* vim: noet ts=8 sw=8
+ */
+
+
diff --git a/contrib/vmlog/vmlog.c b/contrib/vmlog/vmlog.c
new file mode 100644 (file)
index 0000000..75818aa
--- /dev/null
@@ -0,0 +1,1883 @@
+/* vmlog - high-speed logging for free VMs                  */
+/* Copyright (C) 2006 Edwin Steiner <edwin.steiner@gmx.net> */
+
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "vmlog.h"
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <jni.h>
+
+/*** default macros **************************************************/
+
+#ifndef VMLOG_LOCK
+#define VMLOG_LOCK(vml)
+#define VMLOG_UNLOCK(vml)
+#endif
+
+/* #define VMLOG_ENDIAN_CONVERT_WRITE */
+#define VMLOG_HOST_LITTLE_ENDIAN
+
+/*** constants *******************************************************/
+
+/* currently vmlog does no rehashing, so these should be quite big */
+#define VMLOG_INITIAL_STRING_HASH_SIZE       50000 /* XXX debug */
+#define VMLOG_INITIAL_THREAD_HASH_SIZE       8 /* XXX debug */
+
+/* initial size of the frame buffer - this is doubled each time    */
+/* the frame buffer has to grow                                    */
+#define VMLOG_INITIAL_FRAMES_CAPACITY        1 /* XXX debug */
+
+/*** types ***********************************************************/
+
+/* we declare this here because defining _LARGEFILE64_SOURCE works */
+/* only if we are the first ones to include the headers, which may */
+/* not be the case if vmlog.c is used as an include file.          */
+
+#ifndef _LARGEFILE64_SOURCE
+typedef long long off64_t;
+off64_t lseek64(int fd, off64_t offset, int whence);
+#endif
+
+/*** tag definitions *************************************************/
+
+/* CAUTION: these must are indexed by the VMLOG_TAG_... constants! */
+vmlog_tag_definition vmlog_tag_definitions[] = {
+       { "enter", "enter", +1 },
+       { "leave", "leave", -1 },
+       { "throw", "throw",  0 },
+       { "catch", "catch",  0 },
+       { "unwnd", "unwnd", -1 },
+       { "signl", "signl",  0 },
+       { "unrol", "unrol", -1 },
+       { "rerol", "rerol", +1 },
+       { NULL   , NULL   ,  0 }
+};
+
+/*** global variables ************************************************/
+
+static char *vmlog_progname = "vmlog";
+
+/*** prototypes ******************************************************/
+
+static void *vmlog_memdup(const void *m,int len);
+
+/*** error reporting *************************************************/
+
+void vmlog_set_progname(const char *progname)
+{
+       if (!progname) {
+               progname = "vmlog (progname == NULL)";
+       }
+
+       vmlog_progname = vmlog_memdup(progname,strlen(progname)+1);
+}
+
+void vmlog_die(const char *fmt,...)
+{
+       va_list ap;
+
+       fputs(vmlog_progname,stderr);
+       fputs(": error: ",stderr);
+       va_start(ap,fmt);
+       vfprintf(stderr,fmt,ap);
+       va_end(ap);
+       fputc('\n',stderr);
+       exit(1);
+}
+
+void vmlog_warn(const char *fmt,...)
+{
+       va_list ap;
+
+       fputs(vmlog_progname,stderr);
+       fputs(": warning: ",stderr);
+       va_start(ap,fmt);
+       vfprintf(stderr,fmt,ap);
+       va_end(ap);
+       fputc('\n',stderr);
+}
+
+void vmlog_die_usage(const char *usage,int error)
+{
+       assert(usage);
+       
+       fputs(usage,(error) ? stderr : stdout);
+       exit((error) ? 1 : 0);
+}
+
+/*** utility functions ***********************************************/
+
+static void *vmlog_memdup(const void *data,int len)
+{
+       char *p;
+
+       p = VMLOG_NEW_ARRAY(char,len);
+       assert(p);
+       memcpy(p,data,len);
+
+       return p;
+}
+
+static void *vmlog_strdup(const void *data,int len)
+{
+       char *p;
+
+       p = VMLOG_NEW_ARRAY(char,len+1);
+       assert(p);
+       memcpy(p,data,len);
+       p[len] = 0;
+
+       return p;
+}
+
+char *vmlog_concat4len(const char *a,int alen,const char *b,int blen,
+                      const char *c,int clen,const char *d,int dlen,
+                      int *plen)
+{
+       int len;
+       char *p;
+       char *pp;
+
+       assert(a);
+       assert(b);
+       assert(c);
+       assert(d);
+
+       len = alen + blen + clen + dlen;
+       if (plen)
+               *plen = len;
+
+       p = VMLOG_NEW_ARRAY(char,len+1);
+       pp = p;
+       memcpy(pp,a,alen); pp += alen;
+       memcpy(pp,b,blen); pp += blen;
+       memcpy(pp,c,clen); pp += clen;
+       memcpy(pp,d,dlen); pp += dlen;
+       *pp = 0;
+
+       return p;
+}
+
+char *vmlog_concat3(const char *a,const char *b,const char *c,int *plen)
+{
+       int len,lena,lenb,lenc;
+       char *p;
+       char *pp;
+
+       assert(a);
+       assert(b);
+       assert(c);
+
+       lena = strlen(a);
+       lenb = strlen(b);
+       lenc = strlen(c);
+
+       len = lena + lenb + lenc;
+       if (plen)
+               *plen = len;
+
+       p = VMLOG_NEW_ARRAY(char,len+1);
+       pp = p;
+       memcpy(pp,a,lena); pp += lena;
+       memcpy(pp,b,lenb); pp += lenb;
+       memcpy(pp,c,lenc); pp += lenc;
+       *pp = 0;
+
+       return p;
+}
+
+/*** file ops ********************************************************/
+
+void vmlog_file_open(vmlog_file *file,const char *fname,vmlog_fmode fmode)
+{
+       int r;
+       struct stat st;
+       int flags = 0;
+       
+       assert(file);
+
+       switch (fmode) {
+               case vmlogRead:
+                       flags = O_RDONLY;
+                       break;
+
+               case vmlogAppend:
+                       flags = O_WRONLY | O_APPEND | O_CREAT;
+                       break;
+
+               case vmlogTruncateAppend:
+                       flags = O_WRONLY | O_APPEND | O_CREAT | O_TRUNC;
+                       break;
+
+               default:
+                       vmlog_die("unknown fmode for opening file: %s: %d",
+                               fname,fmode);
+       }
+
+       r = open(fname,flags,0644);
+       if (r == -1) {
+               vmlog_die("could not open file: %s: %s",fname,strerror(errno));
+       }
+       file->fd = r;
+       file->fnamelen = strlen(fname);
+       file->fname = vmlog_memdup(fname,file->fnamelen+1);
+
+       r = fstat(file->fd,&st);
+       if (r == -1) {
+               vmlog_die("could not stat file: %s: %s",fname,strerror(errno));
+       }
+       file->ofs = (fmode == vmlogRead) ? 0 : st.st_size;
+}
+
+void vmlog_file_close(vmlog_file *file)
+{
+       assert(file);
+
+       if (file->fd == -1)
+               return;
+
+       close(file->fd);
+       file->fd = -1;
+       VMLOG_FREE_ARRAY(char,file->fnamelen+1,file->fname);
+       file->fname = NULL;
+       file->fnamelen = 0;
+}
+
+void vmlog_file_append(vmlog_file *file,const void *data,int len)
+{
+       int r;
+
+       assert(len >= 0);
+       if (!len)
+               return;
+       assert(data);
+       
+       do {
+               r = write(file->fd,data,len);
+       } while (r == -1 && errno == EINTR);
+
+       if (r == -1) {
+               vmlog_die("could not write to file: %s: %s",file->fname,strerror(errno));
+       }
+
+       if (r != len) {
+               vmlog_die("could not write all data to file: %s",file->fname);
+       }
+
+       file->ofs += len;
+}
+
+void vmlog_file_stat(vmlog_file *file)
+{
+       int r;
+       struct stat st;
+       
+       r = fstat(file->fd,&st);
+       if (r == -1)
+               vmlog_die("could not stat file: %s: %s",file->fname,strerror(errno));
+
+       file->size = st.st_size;
+}
+
+void * vmlog_file_mmap(const char *fname,int *plen)
+{
+       int fd;
+       int r;
+       struct stat st;
+       void *m;
+
+       fd = open(fname,O_RDONLY);
+       if (fd == -1)
+               vmlog_die("could not open file: %s: %s",fname,strerror(errno));
+       
+       r = fstat(fd,&st);
+       if (r == -1)
+               vmlog_die("could not stat file: %s: %s",fname,strerror(errno));
+
+       if (plen)
+               *plen = st.st_size;
+       
+       if (st.st_size) {
+               m = mmap(NULL,st.st_size,PROT_READ,MAP_PRIVATE,fd,0);
+               if (m == MAP_FAILED) {
+                       vmlog_die("could not mmap file: %s: %s",fname,strerror(errno));
+               }
+       }
+       else {
+               /* fake a pointer */
+               m = VMLOG_NEW(char);
+       }
+
+       close(fd);
+
+       return m;
+}
+
+void vmlog_file_munmap(void *m,int len)
+{
+       int r;
+       
+       if (len) {
+               r = munmap(m,len);
+               if (r != 0)
+                       vmlog_warn("could not munmap file: %s",strerror(errno));
+       }
+       else {
+               VMLOG_FREE(char,m);
+       }
+}
+
+void vmlog_file_seek(vmlog_file *file,vmlog_fofs_t ofs)
+{
+       off64_t r;
+
+       r = lseek64(file->fd,ofs,SEEK_SET);
+       if (r == (off64_t)-1)
+               vmlog_die("could not seek position in file: %s: %s",
+                       file->fname,strerror(errno));
+       file->ofs = ofs;
+}
+
+/*** string storage **************************************************/
+
+static void vmlog_add_string(vmlog_log *vml,const char *data,int len)
+{
+       vmlog_string_entry strent;
+#if defined(VMLOG_ENDIAN_CONVERT_WRITE)
+       vmlog_fofs_t tmp;
+#endif
+       
+       assert(vml);
+
+       if (vml->strfile.fd == -1)
+               return;
+       if (vml->idxfile.fd == -1)
+               return;
+
+       strent.ofs = vml->strfile.ofs;
+       strent.len = len;
+
+#if defined(VMLOG_ENDIAN_CONVERT_WRITE)
+#if defined(VMLOG_HOST_LITTLE_ENDIAN)
+       tmp = ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[0]) << 56)
+           | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[1]) << 48)
+           | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[2]) << 40)
+           | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[3]) << 32)
+           | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[4]) << 24)
+           | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[5]) << 16)
+           | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[6]) <<  8)
+           | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[7]) <<  0);
+       strent.ofs = tmp;
+       tmp = ((vmlog_fofs_t)(((unsigned char*)&strent.len)[0]) << 24)
+           | ((vmlog_fofs_t)(((unsigned char*)&strent.len)[1]) << 16)
+           | ((vmlog_fofs_t)(((unsigned char*)&strent.len)[2]) <<  8)
+           | ((vmlog_fofs_t)(((unsigned char*)&strent.len)[3]) <<  0);
+       strent.len = tmp;
+#else
+       tmp = ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[7]) << 56)
+           | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[6]) << 48)
+           | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[5]) << 40)
+           | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[4]) << 32)
+           | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[3]) << 24)
+           | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[2]) << 16)
+           | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[1]) <<  8)
+           | ((vmlog_fofs_t)(((unsigned char*)&strent.ofs)[0]) <<  0);
+       strent.ofs = tmp;
+       tmp = ((vmlog_fofs_t)(((unsigned char*)&strent.len)[3]) << 24)
+           | ((vmlog_fofs_t)(((unsigned char*)&strent.len)[2]) << 16)
+           | ((vmlog_fofs_t)(((unsigned char*)&strent.len)[1]) <<  8)
+           | ((vmlog_fofs_t)(((unsigned char*)&strent.len)[0]) <<  0);
+       strent.len = tmp;
+#endif
+#endif /* defined(VMLOG_ENDIAN_CONVERT_WRITE) */
+       
+       vmlog_file_append(&(vml->strfile),data,len);
+       vmlog_file_append(&(vml->idxfile),&strent,sizeof(vmlog_string_entry));
+}
+
+/*** index functions *************************************************/
+
+static int vmlog_is_ignored(vmlog_log *vml,int index)
+{
+       return (index < vml->ignorelistlen);
+}
+
+/*** thread log functions ********************************************/
+
+static void vmlog_thread_log_alloc_logbuf(vmlog_thread_log *tlog,int cap)
+{
+       assert(tlog);
+       assert(cap >= 0);
+
+       if (cap) {
+               VMLOG_XZNEW_ARRAY(tlog->logbuf,vmlog_log_entry,cap);
+       }
+       else {
+               tlog->logbuf = NULL;
+       }
+       tlog->logbufptr = tlog->logbuf;
+       tlog->logbufend = tlog->logbuf + cap;
+       tlog->logbufcap = cap;
+}
+
+static void vmlog_thread_log_flush(vmlog_thread_log *tlog)
+{
+       assert(tlog);
+       assert(tlog->logbuf);
+
+       vmlog_file_append(&(tlog->logfile),tlog->logbuf,
+                       (tlog->logbufptr - tlog->logbuf) * sizeof(vmlog_log_entry));
+
+       tlog->logbufptr = tlog->logbuf;
+}
+
+static void vmlog_thread_log_realloc_frames(vmlog_thread_log *tlog,int cap)
+{
+       vmlog_frame *oldframes;
+
+       assert(tlog);
+       assert(cap >= tlog->depth);
+
+       oldframes = tlog->frames;
+
+       if (cap) {
+               VMLOG_XZNEW_ARRAY(tlog->frames,vmlog_frame,cap);
+       }
+       else {
+               tlog->frames = NULL;
+       }
+       
+       if (oldframes) {
+               if (tlog->frames) {
+                       memcpy(tlog->frames,oldframes,sizeof(vmlog_frame) * tlog->depth);
+               }
+               VMLOG_FREE_ARRAY(vmlog_frame,tlog->framescap,oldframes);
+       }
+       tlog->framescap = cap;
+}
+
+static void vmlog_thread_log_append(vmlog_thread_log *tlog,vmlog_log_entry *logent)
+{
+#if defined(VMLOG_ENDIAN_CONVERT_WRITE)
+       unsigned int tmp;
+
+#if defined(VMLOG_HOST_LITTLE_ENDIAN)
+       tmp = ((unsigned int)(((unsigned char*)logent)[3]) <<  0)
+           | ((unsigned int)(((unsigned char*)logent)[2]) <<  8)
+           | ((unsigned int)(((unsigned char*)logent)[1]) << 16);
+#else
+       tmp = ((unsigned int)(((unsigned char*)logent)[1]) <<  0)
+           | ((unsigned int)(((unsigned char*)logent)[2]) <<  8)
+           | ((unsigned int)(((unsigned char*)logent)[3]) << 16);
+#endif
+       logent->index = tmp;
+#endif /* defined(VMLOG_ENDIAN_CONVERT_WRITE) */
+       if (tlog->logbufptr) {
+               if (tlog->logbufptr == tlog->logbufend) {
+                       vmlog_thread_log_flush(tlog);
+               }
+               *tlog->logbufptr++ = *logent;
+       }
+       else {
+               vmlog_file_append(&(tlog->logfile),logent,sizeof(vmlog_log_entry));
+       }
+}
+
+#define VMLOG_INT2STR_BUFFER 20
+
+vmlog_thread_log *vmlog_thread_log_new(vmlog_log *vml,void *threadid,int index)
+{
+       vmlog_thread_log *tlog;
+       char buf[VMLOG_INT2STR_BUFFER];
+       int r;
+       char *name;
+       int namelen;
+
+       VMLOG_XZNEW(tlog,vmlog_thread_log);
+
+       tlog->threadid = threadid;
+       tlog->threadidx = index;
+       tlog->logfile.fd = -1;
+
+       vmlog_thread_log_realloc_frames(tlog,VMLOG_INITIAL_FRAMES_CAPACITY);
+
+       if (vml && vml->prefix) {
+               r = snprintf(buf,VMLOG_INT2STR_BUFFER,"%d",index);
+               assert(r < VMLOG_INT2STR_BUFFER);
+               buf[VMLOG_INT2STR_BUFFER-1] = 0;
+               name = vmlog_concat4len(vml->prefix,vml->prefixlen,
+                                       ".",1,
+                                       buf,strlen(buf),
+                                       ".log",4,
+                                       &namelen);
+               vmlog_file_open(&(tlog->logfile),name,vmlogTruncateAppend);
+               VMLOG_FREE_ARRAY(char,namelen+1,name);
+       }
+
+       return tlog;
+}
+
+void vmlog_thread_log_free(vmlog_thread_log *tlog)
+{
+       if (!tlog)
+               return;
+
+       if (tlog->logbuf)
+               vmlog_thread_log_flush(tlog);
+       
+       vmlog_file_close(&(tlog->logfile));
+       
+       if (tlog->frames) {
+               VMLOG_FREE_ARRAY(vmlog_frame,tlog->framescap,tlog->frames);
+       }
+       VMLOG_FREE(vmlog_thread_log,tlog);
+}
+
+vmlog_frame * vmlog_thread_log_enter(vmlog_thread_log *tlog,int index,vmlog_seq_t seq)
+{
+       vmlog_frame *frame;
+
+       if (tlog->depth < 0) {
+               vmlog_warn("negative call frame depth %d at seq " VMLOG_SEQ_FMT,
+                               tlog->depth,seq);
+               return NULL;
+       }
+       
+       if (tlog->depth >= tlog->framescap)
+               vmlog_thread_log_realloc_frames(tlog,tlog->framescap * 2);
+
+       frame = tlog->frames + tlog->depth;
+
+       frame->index = index;
+       frame->seq = seq;
+
+       tlog->depth++;
+
+       return frame;
+}
+
+vmlog_frame * vmlog_thread_log_leave(vmlog_thread_log *tlog,int index,vmlog_seq_t seq)
+{
+       vmlog_frame *frame;
+
+       if (--tlog->depth < 0) {
+               vmlog_warn("negative call frame depth %d at seq " VMLOG_SEQ_FMT,
+                               tlog->depth,seq);
+               return NULL;
+       }
+       
+       frame = tlog->frames + tlog->depth;
+
+       if (index != frame->index)
+               vmlog_warn("mismatched leave at seq " VMLOG_SEQ_FMT 
+                               ": entered index %d, left index %d",
+                       seq,frame->index,index);
+
+       return frame;
+}
+
+/*** tag definitions *************************************************/
+
+/* RETURNS                                                           */
+/*     the tag number, or -1 if the tag name is invalid              */
+
+int vmlog_tag_from_name(const char *name,int namelen)
+{
+       vmlog_tag_definition *td;
+       int i;
+       
+       if (!name || namelen < 1)
+               return -1;
+
+       td = vmlog_tag_definitions;
+       i = 0;
+       while (td->name) {
+               if (namelen == strlen(td->name)
+                   && strncmp(td->name,name,namelen) == 0) 
+               {
+                       return i;
+               }
+               td++;
+               i++;
+       }
+
+       return -1;
+}
+
+/*** hash functions **************************************************/
+
+static unsigned int vmlog_thread_hash(void *threadid) 
+{
+       /* XXX use a better hash function? */
+       return (unsigned int)(ptrint)threadid;
+}
+
+static unsigned int vmlog_string_hash(const char *data,int len) 
+{
+       register const unsigned char *p = (const unsigned char *) data;
+       register unsigned int hash;
+       register int i;
+
+       /* The algorithm is the "One-at-a-time" algorithm as published    */
+       /* by Bob Jenkins on http://burtleburtle.net/bob/hash/doobs.html. */
+
+       hash = 0;
+       for (i=len; i--;)
+       {
+           hash += *p++;
+           hash += (hash << 10);
+           hash ^= (hash >> 6);
+       }
+       hash += (hash << 3);
+       hash ^= (hash >> 11);
+       hash += (hash << 15);
+
+       return hash;
+}
+
+/*** hash tables *****************************************************/
+
+static vmlog_thread_log *vmlog_get_thread_log(vmlog_log *vml,void *threadid)
+{
+       unsigned int h;
+       vmlog_hash_entry *preventry = NULL;
+       vmlog_hash_entry *entry;
+       vmlog_thread_log *tlog;
+       
+       assert(vml);
+
+       h = vmlog_thread_hash(threadid);
+       entry = vml->threadhash.table + (h % vml->threadhash.size);
+       do {
+               tlog = (vmlog_thread_log *)entry->data;
+               if (tlog && tlog->threadid == threadid)
+                       return tlog;
+               preventry = entry;
+               entry = entry->hashlink;
+       } while (entry);
+
+       /* this is a new threadid */
+       tlog = vmlog_thread_log_new(vml,threadid,vml->threadhash.nentries++);
+       
+       assert(preventry);
+       if (preventry->data) {
+               VMLOG_XZNEW(entry,vmlog_hash_entry);
+
+               preventry->hashlink = entry;
+       }
+       else {
+               entry = preventry;
+       }
+
+       entry->data = tlog;
+
+       /* XXX maybe rehash */
+
+       return tlog;
+}
+
+int vmlog_get_string_index(vmlog_log *vml,const char *data,int len)
+{
+       unsigned int hash;
+       vmlog_hash_entry *entry;
+       vmlog_hash_entry *preventry = NULL;
+       
+       assert(vml);
+       assert(data);
+       assert(len >= 0);
+
+       hash = vmlog_string_hash(data,len);
+       entry = vml->stringhash.table + (hash % vml->stringhash.size);
+       do {
+               if (entry->len == len && entry->data && memcmp(data,entry->data,len) == 0)
+                       return entry->index;
+               preventry = entry;
+               entry = entry->hashlink;
+       } while (entry);
+
+       /* this is a new string */
+       assert(preventry);
+       if (preventry->data) {
+               VMLOG_XZNEW(entry,vmlog_hash_entry);
+
+               preventry->hashlink = entry;
+       }
+       else {
+               entry = preventry;
+       }
+
+       entry->data = vmlog_memdup(data,len);
+       entry->len = len;
+       entry->index = vml->stringhash.nentries++;
+       vmlog_add_string(vml,data,len);
+
+       return entry->index;
+}
+
+static void vmlog_hashtable_init(vmlog_hash_table *ht,int size)
+{
+       assert(ht);
+       assert(size > 0);
+       
+       ht->size = size;
+       VMLOG_XZNEW_ARRAY(ht->table,vmlog_hash_entry,size);
+       ht->nentries = 0;
+}
+
+static void vmlog_hashtable_free(vmlog_hash_table *ht,vmlog_hash_entry_destructor destr)
+{
+       int i;
+       vmlog_hash_entry *entry,*next;
+       
+       assert(ht);
+
+       for (i=0; i<ht->size; ++i) {
+               entry = ht->table + i;
+               if (destr)
+                       destr(entry);
+
+               next = entry->hashlink;
+               while (next) {
+                       entry = next;
+                       if (destr)
+                               destr(entry);
+                       next = entry->hashlink;
+                       VMLOG_FREE(vmlog_hash_entry,entry);
+               }
+       }
+
+       VMLOG_FREE_ARRAY(vmlog_hash_entry,ht->size,ht->table);
+       memset(ht,0,sizeof(vmlog_hash_table));
+}
+
+static void vmlog_thread_log_destructor(vmlog_hash_entry *entry)
+{
+       vmlog_thread_log *tlog;
+       
+       assert(entry);
+
+       tlog = (vmlog_thread_log *)entry->data;
+       vmlog_thread_log_free(tlog);
+}
+
+static void vmlog_string_destructor(vmlog_hash_entry *entry)
+{
+       char *str;
+
+       assert(entry);
+
+       str = (char *)entry->data;
+       if (str) {
+               VMLOG_FREE_ARRAY(char,entry->len,str);
+       }
+}
+
+static void vmlog_open_string_files(vmlog_log *vml,int truncate)
+{
+       char *name;
+       int namelen;
+       int fmode;
+
+       if (!vml->prefix)
+               return;
+
+       fmode = (truncate) ? vmlogTruncateAppend : vmlogAppend;
+
+       name = vmlog_concat3(vml->prefix,"",".idx",&namelen);
+       vmlog_file_open(&(vml->idxfile),name,fmode);
+       VMLOG_FREE_ARRAY(char,namelen+1,name);
+
+       name = vmlog_concat3(vml->prefix,"",".str",&namelen);
+       vmlog_file_open(&(vml->strfile),name,fmode);
+       VMLOG_FREE_ARRAY(char,namelen+1,name);
+}
+
+void vmlog_load_stringhash(vmlog_log *vml,const char *prefix)
+{
+       int n;
+       vmlog_string_entry *idxmap;
+       vmlog_string_entry *strent;
+       char *strmap;
+       int idxlen;
+       int strlen;
+       char *idxfname;
+       char *strfname;
+       int idxnamelen;
+       int strnamelen;
+       
+       assert(vml);
+       assert(prefix);
+
+       idxfname = vmlog_concat3(prefix,".","idx",&idxnamelen);
+       strfname = vmlog_concat3(prefix,".","str",&strnamelen);
+       
+       vmlog_hashtable_free(&(vml->stringhash),vmlog_string_destructor);
+       vmlog_hashtable_init(&(vml->stringhash),VMLOG_INITIAL_STRING_HASH_SIZE);
+
+       vmlog_file_close(&(vml->idxfile));
+       vmlog_file_close(&(vml->strfile));
+       vmlog_open_string_files(vml,1);
+
+       idxmap = vmlog_file_mmap(idxfname,&idxlen);
+       strmap = vmlog_file_mmap(strfname,&strlen);
+
+       n = idxlen / sizeof(vmlog_string_entry);
+       strent = idxmap;
+       while (n--) {
+               vmlog_get_string_index(vml,strmap + strent->ofs,strent->len);
+               strent++;
+       }
+
+       vmlog_file_munmap(idxmap,idxlen);
+       vmlog_file_munmap(strmap,strlen);
+       
+       VMLOG_FREE_ARRAY(char,idxnamelen+1,idxfname);
+       VMLOG_FREE_ARRAY(char,strnamelen+1,strfname);
+}
+
+/*** public functions ************************************************/
+
+vmlog_log * vmlog_log_new(const char *prefix,int truncate)
+{
+       vmlog_log *vml;
+
+       VMLOG_XZNEW(vml,vmlog_log);
+
+       vml->idxfile.fd = -1;
+       vml->strfile.fd = -1;
+       vmlog_hashtable_init(&(vml->stringhash),VMLOG_INITIAL_STRING_HASH_SIZE);
+       vmlog_hashtable_init(&(vml->threadhash),VMLOG_INITIAL_THREAD_HASH_SIZE);
+       
+       if (prefix) {
+               vml->prefixlen = strlen(prefix);
+               vml->prefix = vmlog_memdup(prefix,vml->prefixlen+1);
+
+               vmlog_open_string_files(vml,truncate);
+       }
+
+       return vml;
+}
+
+void vmlog_log_free(vmlog_log *vml)
+{
+       if (!vml)
+               return;
+
+       VMLOG_FREE_ARRAY(char,vml->prefixlen+1,vml->prefix);
+       vml->prefix = NULL;
+       vml->prefixlen = 0;     
+
+       vmlog_hashtable_free(&(vml->threadhash),vmlog_thread_log_destructor);
+       vmlog_hashtable_free(&(vml->stringhash),vmlog_string_destructor);
+
+       vmlog_file_close(&(vml->idxfile));
+       vmlog_file_close(&(vml->strfile));
+
+       VMLOG_FREE(vmlog_log,vml);
+}
+
+static void vmlog_log_enter_tag(vmlog_log *vml,void *threadid,int tag,const char *name,int namelen)
+{
+       vmlog_thread_log *tlog;
+       int index;
+       vmlog_log_entry logent;
+       
+       assert(vml);
+       assert(name);
+       assert(namelen >= 0);
+
+       VMLOG_LOCK();
+       tlog = vmlog_get_thread_log(vml,threadid);
+       index = vmlog_get_string_index(vml,name,namelen);
+       VMLOG_UNLOCK();
+
+       if (tlog->ignoredepth) {
+               tlog->ignoredepth++;
+               return;
+       }
+
+       if (vmlog_is_ignored(vml,index)) {
+               tlog->ignoredepth++;
+               return;
+       }
+       
+       logent.tag = tag;
+       logent.index = index;
+       vmlog_thread_log_append(tlog,&logent);
+
+       tlog->seq++;
+}
+
+static void vmlog_log_leave_tag(vmlog_log *vml,void *threadid,int tag,const char *name,int namelen)
+{
+       vmlog_thread_log *tlog;
+       int index;
+       vmlog_log_entry logent;
+       
+       assert(vml);
+       assert(name);
+       assert(namelen >= 0);
+       
+       VMLOG_LOCK();
+       tlog = vmlog_get_thread_log(vml,threadid);
+       index = vmlog_get_string_index(vml,name,namelen);
+       VMLOG_UNLOCK();
+
+       if (tlog->ignoredepth) {
+               tlog->ignoredepth--;
+               return;
+       }
+       
+       logent.tag = tag;
+       logent.index = index;
+       vmlog_thread_log_append(tlog,&logent);
+
+       tlog->seq++;
+}
+
+void vmlog_log_enter(vmlog_log *vml,void *threadid,const char *name,int namelen)
+{
+       vmlog_log_enter_tag(vml,threadid,VMLOG_TAG_ENTER,name,namelen);
+}
+
+void vmlog_log_rerol(vmlog_log *vml,void *threadid,const char *name,int namelen)
+{
+       vmlog_log_enter_tag(vml,threadid,VMLOG_TAG_REROL,name,namelen);
+}
+
+void vmlog_log_leave(vmlog_log *vml,void *threadid,const char *name,int namelen)
+{
+       vmlog_log_leave_tag(vml,threadid,VMLOG_TAG_LEAVE,name,namelen);
+}
+
+void vmlog_log_unrol(vmlog_log *vml,void *threadid,const char *name,int namelen)
+{
+       vmlog_log_leave_tag(vml,threadid,VMLOG_TAG_UNROL,name,namelen);
+}
+
+void vmlog_log_throw(vmlog_log *vml,void *threadid,const char *name,int namelen)
+{
+       vmlog_thread_log *tlog;
+       int index;
+       vmlog_log_entry logent;
+       
+       assert(vml);
+       assert(name);
+       assert(namelen >= 0);
+       
+       VMLOG_LOCK();
+       tlog = vmlog_get_thread_log(vml,threadid);
+       index = vmlog_get_string_index(vml,name,namelen);
+       VMLOG_UNLOCK();
+
+       if (tlog->ignoredepth)
+               return;
+       
+       logent.tag = VMLOG_TAG_THROW;
+       logent.index = index;
+       vmlog_thread_log_append(tlog,&logent);
+
+       tlog->seq++;
+}
+
+void vmlog_log_catch(vmlog_log *vml,void *threadid,const char *name,int namelen)
+{
+       vmlog_thread_log *tlog;
+       int index;
+       vmlog_log_entry logent;
+       
+       assert(vml);
+       assert(name);
+       assert(namelen >= 0);
+       
+       VMLOG_LOCK();
+       tlog = vmlog_get_thread_log(vml,threadid);
+       index = vmlog_get_string_index(vml,name,namelen);
+       VMLOG_UNLOCK();
+
+       if (tlog->ignoredepth)
+               return;
+       
+       logent.tag = VMLOG_TAG_CATCH;
+       logent.index = index;
+       vmlog_thread_log_append(tlog,&logent);
+
+       tlog->seq++;
+}
+
+void vmlog_log_unwnd(vmlog_log *vml,void *threadid,const char *name,int namelen)
+{
+       vmlog_thread_log *tlog;
+       int index;
+       vmlog_log_entry logent;
+       
+       assert(vml);
+       assert(name);
+       assert(namelen >= 0);
+       
+       VMLOG_LOCK();
+       tlog = vmlog_get_thread_log(vml,threadid);
+       index = vmlog_get_string_index(vml,name,namelen);
+       VMLOG_UNLOCK();
+
+       if (tlog->ignoredepth) {
+               tlog->ignoredepth--;
+               return;
+       }
+       
+       logent.tag = VMLOG_TAG_UNWND;
+       logent.index = index;
+       vmlog_thread_log_append(tlog,&logent);
+
+       tlog->seq++;
+}
+
+void vmlog_log_signl(vmlog_log *vml,void *threadid,const char *name,int namelen)
+{
+       vmlog_thread_log *tlog;
+       int index;
+       vmlog_log_entry logent;
+       
+       assert(vml);
+       assert(name);
+       assert(namelen >= 0);
+       
+       VMLOG_LOCK();
+       tlog = vmlog_get_thread_log(vml,threadid);
+       index = vmlog_get_string_index(vml,name,namelen);
+       VMLOG_UNLOCK();
+
+       logent.tag = VMLOG_TAG_SIGNL;
+       logent.index = index;
+       vmlog_thread_log_append(tlog,&logent);
+
+       tlog->seq++;
+}
+
+void vmlog_log_load_ignorelist(vmlog_log *vml,const char *prefix)
+{
+       assert(vml);
+       assert(prefix);
+
+       vmlog_load_stringhash(vml,prefix);
+       vml->ignorelistlen = vml->stringhash.nentries;
+}
+
+/*** ring buffer functions *******************************************/
+
+static void vmlog_ringbuf_visualize(vmlog_ringbuf *ring)
+{
+       int i;
+
+       fprintf(stdout,"vmlog_ringbuf %p: bufsize=%d availbefore=%d availafter=%d\n",
+                       (void*)ring,ring->bufsize,
+                       ring->debug_availbefore,ring->debug_availafter);
+
+       for (i=0; i<=ring->bufsize; ++i) {
+               if (i == ring->bufsize) {
+                       fprintf(stdout,"%3d: xxxxxxxxxxxxx",i);
+               }
+               else {
+                       fprintf(stdout,"%3d: %2d %10d",i,ring->buf[i].tag,ring->buf[i].index);
+               }
+               if (ring->start - ring->buf == i) fputs(" start",stdout);
+               if (ring->cur   - ring->buf == i) fputs(" cur",stdout);
+               if (ring->end   - ring->buf == i) fputs(" end",stdout);
+               if (ring->cur   - ring->buf == i) fprintf(stdout," (" VMLOG_SEQ_FMT ")",ring->seq);
+               fputc('\n',stdout);
+       }       
+}
+
+static void vmlog_ringbuf_check_invariants(vmlog_ringbuf *ring)
+{
+       /* vmlog_ringbuf_visualize(ring); */
+       
+       assert(ring);
+
+       assert(ring->bufsize > 0);
+       assert(ring->bufend == ring->buf + ring->bufsize);
+
+       assert(ring->start >= ring->buf && ring->start < ring->bufend);
+       assert((ring->end > ring->buf && ring->end <= ring->bufend)
+                       ||
+              (ring->end == ring->start));
+
+       assert(ring->debug_availbefore >= 0);
+       assert(ring->debug_availafter >= 0);
+       assert(ring->debug_availbefore + ring->debug_availafter <= ring->bufsize);
+
+       /* ring->cur can point to any present  */
+       /* element (#) or be equal to ring->end*/
+
+       if (ring->end >= ring->start) {
+               /* case A: ring->end >= ring->start    */
+               /*                                     */
+               /* -------#############-----------     */
+               /* ^      ^            ^          ^    */
+               /* buf    start        end     bufend  */
+
+               assert(ring->cur >= ring->start && ring->cur <= ring->end);
+
+               assert(ring->cur - ring->start == ring->debug_availbefore);
+               assert(ring->end - ring->cur   == ring->debug_availafter);
+       }
+       else {
+               /* case B: ring->end < ring->start     */
+               /*                                     */
+               /* #######------------############     */
+               /* ^      ^           ^           ^    */
+               /* buf    end        start     bufend  */
+
+               assert((ring->cur >= ring->start && ring->cur < ring->bufend)
+                               ||
+                      (ring->cur >= ring->buf && ring->cur <= ring->end));
+
+               if (ring->cur >= ring->start) {
+                       assert(ring->cur - ring->start == ring->debug_availbefore);
+                       assert((ring->bufend - ring->cur) + (ring->end - ring->buf) 
+                                       == ring->debug_availafter);
+               }
+               else {
+                       assert((ring->bufend - ring->start) + (ring->cur - ring->buf) 
+                                       == ring->debug_availbefore);
+                       assert(ring->end - ring->cur == ring->debug_availafter);
+               }
+       }
+}
+
+vmlog_ringbuf * vmlog_ringbuf_new(const char *fname,int bufsize)
+{
+       vmlog_ringbuf *ring;
+
+       assert(bufsize > 0);
+
+       VMLOG_XZNEW(ring,vmlog_ringbuf);
+       VMLOG_XZNEW_ARRAY(ring->buf,vmlog_log_entry,bufsize);
+
+       ring->bufsize = bufsize;
+       ring->bufend = ring->buf + bufsize;
+       ring->start = ring->buf;
+       ring->end = ring->buf;
+       ring->cur = ring->buf;
+
+       vmlog_file_open(&(ring->file),fname,vmlogRead);
+       vmlog_file_stat(&(ring->file));
+
+       vmlog_ringbuf_check_invariants(ring);
+
+       return ring;
+}
+
+void vmlog_ringbuf_free(vmlog_ringbuf *ring)
+{
+       if (!ring)
+               return;
+
+       vmlog_ringbuf_check_invariants(ring);
+
+       vmlog_file_close(&(ring->file));
+
+       VMLOG_FREE_ARRAY(vmlog_log_entry,ring->bufsize,ring->buf);
+       VMLOG_FREE(vmlog_ringbuf,ring);
+}
+
+static int vmlog_ringbuf_read(vmlog_ringbuf *ring,vmlog_log_entry *buf,
+               vmlog_seq_t seq,int n)
+{
+       int r;
+       vmlog_fofs_t ofs;
+
+       ofs = seq * sizeof(vmlog_log_entry);
+       if (ofs != ring->file.ofs)
+               vmlog_file_seek(&(ring->file),ofs);
+
+       do {
+               /* fprintf(stdout,"vmlog_ringbuf_read(%p,%d,%d)\n",
+                               (void*)ring,buf-ring->buf,n); */
+               
+               r = read(ring->file.fd,buf,n * sizeof(vmlog_log_entry));
+       } while (r == -1 && errno == EINTR);
+
+       if (r == -1)
+               vmlog_die("reading from file: %s: %s",ring->file.fname,strerror(errno));
+
+       ring->file.ofs += r;
+
+       if (r % sizeof(vmlog_log_entry) != 0) {
+               /* XXX */
+               vmlog_warn("partial log entry read from file: %s",ring->file.fname);
+       }
+
+       return r / sizeof(vmlog_log_entry);
+}
+
+static int vmlog_ringbuf_fill_forward(vmlog_ringbuf *ring,vmlog_log_entry *fillstart,
+                                     vmlog_log_entry *fillend,vmlog_seq_t seq,int len)
+{
+       int space;
+       int n;
+       int read;
+       vmlog_log_entry *oldend;
+
+#if 0
+       fprintf(stdout,"vmlog_ringbuf_fill_forward(%p,%d,%d," VMLOG_SEQ_FMT ",%d)\n",
+                       (void*)ring,fillstart-ring->buf,fillend-ring->buf,seq,len);
+#endif
+
+       vmlog_ringbuf_check_invariants(ring);
+
+       space = fillend - fillstart;
+       n = (len <= space) ? len : space;
+
+       if (n <= 0)
+               return 0;
+
+       read = vmlog_ringbuf_read(ring,fillstart,seq,n);
+       if (!read)
+               return 0;
+
+       oldend = ring->end;
+       ring->end = fillstart + read;
+       ring->debug_availafter += read;
+
+       if (ring->cur == ring->bufend)
+               ring->cur = ring->buf;
+
+       if (ring->start >= fillstart && ring->start != oldend) {
+               /* check if old entries have been overwritten */
+               if (ring->start <= ring->end) {
+                       ring->debug_availbefore -=
+                               ring->end - ring->start + 1;
+                       ring->start = ring->end + 1;
+                       if (ring->start >= ring->bufend) {
+                               ring->start = ring->buf;
+                               ring->debug_availbefore = ring->cur - ring->start;
+                       }
+               }
+       }
+
+       vmlog_ringbuf_check_invariants(ring);
+
+       return read;
+}
+       
+static int vmlog_ringbuf_fill_backward(vmlog_ringbuf *ring,vmlog_log_entry *fillstart,
+                                      vmlog_log_entry *fillend,vmlog_seq_t seq,int len)
+{
+       int space;
+       int n;
+       int read;
+       vmlog_log_entry *oldstart;
+       vmlog_log_entry *oldend;
+
+#if 0
+       fprintf(stdout,"vmlog_ringbuf_fill_backward(%p,%d,%d," VMLOG_SEQ_FMT ",%d)\n",
+                       (void*)ring,fillstart-ring->buf,fillend-ring->buf,seq,len);
+#endif
+
+       vmlog_ringbuf_check_invariants(ring);
+
+       space = fillend - fillstart;
+       n = (len <= space) ? len : space;
+
+       if (n <= 0)
+               return 0;
+
+       seq += space - n;
+       fillstart += space - n;
+
+       read = vmlog_ringbuf_read(ring,fillstart,seq,n);
+       if (read != n)
+               vmlog_die("could not read backward in file: %s: %s",
+                       ring->file.fname,strerror(errno));
+
+       oldstart = ring->start;
+       ring->start = fillstart;
+       ring->debug_availbefore += read;
+
+       oldend = ring->end;
+       if (ring->end <= fillend && ring->end != oldstart) {
+               /* check if old entries have been overwritten */
+               if (ring->start <= ring->end) {
+                       ring->debug_availafter -=
+                               ring->end - ring->start + 1;
+                       ring->end = ring->start - 1;
+
+                       if (ring->end <= ring->buf) {
+                               ring->end = ring->bufend;
+                               if (ring->cur == ring->buf && ring->end == ring->bufend)
+                                       ring->cur = ring->bufend;
+                               ring->debug_availafter = ring->end - ring->cur;
+                       }
+               }
+       }
+
+       if (ring->end == ring->buf) {
+               assert(oldstart == oldend);
+               ring->end = ring->bufend;
+       }
+
+       if (ring->cur == ring->buf && ring->end == ring->bufend)
+               ring->cur = ring->bufend;
+
+       vmlog_ringbuf_check_invariants(ring);
+
+       return read;
+}
+       
+int vmlog_ringbuf_fill(vmlog_ringbuf *ring,int len)
+{
+       int count;
+       int read;
+       vmlog_log_entry *fillend;
+       vmlog_seq_t seq;
+       
+       assert(ring);
+
+       if (!len)
+               return 0 /*XXX*/;
+
+       count = 0;
+
+       vmlog_ringbuf_check_invariants(ring);
+       
+       if (len > 0) {
+               if (ring->end >= ring->cur) {
+                       /* case A'1: ring->end >= ring->start  */
+                       /*                                     */
+                       /*                     vvvvvvvvvvv     */
+                       /* ------OOOOO#########-----------     */
+                       /* ^     ^    ^        ^          ^    */
+                       /* buf  start cur      end     bufend  */
+
+                       /* case B'1: ring->end < ring->start   */
+                       /*                                     */
+                       /*                     vvvvvvvvvvv     */
+                       /* OOOOOOOOOOO#########----OOOOOOO     */
+                       /* ^          ^        ^   ^      ^    */
+                       /* buf        cur      end st. bufend  */
+
+                       /* fill space at end of buf */
+                       seq = ring->seq + (ring->end - ring->cur);
+                       read = vmlog_ringbuf_fill_forward(ring,ring->end,ring->bufend,
+                                       seq,len);
+                       count += read;
+                       len -= read;
+
+                       if (ring->end != ring->bufend)
+                               goto no_more_entries;
+
+                       /* case A'1: ring->end >= ring->start  */
+                       /*                                     */
+                       /* vvvvvvvvvv                          */
+                       /* ------OOOOO####################     */
+                       /* ^     ^    ^                   ^    */
+                       /* buf  start cur         end==bufend  */
+
+                       /* case B'1: ring->end < ring->start   */
+                       /*                                     */
+                       /* vvvvvvvvvv                          */
+                       /* OOOOOOOOOOO####################     */
+                       /* ^          ^                   ^    */
+                       /* buf==start cur         end==bufend  */
+
+                       /* fill space at beg of buf */
+                       seq = ring->seq + (ring->end - ring->cur);
+                       fillend = (ring->cur == ring->bufend) ? ring->bufend : (ring->cur - 1);
+                       read = vmlog_ringbuf_fill_forward(ring,ring->buf,fillend,
+                                       seq,len);
+                       count += read;
+                       len -= read;
+               }
+               else {
+                       /* ring->end < ring->cur */
+
+                       /* no case A'2 */
+                       assert(ring->end < ring->start);
+
+                       /* case B'2: ring->end < ring->start   */
+                       /*                                     */
+                       /*      vvvvvvvvvvv                    */
+                       /* #####------OOOOOO##############     */
+                       /* ^    ^     ^     ^             ^    */
+                       /* buf  end   start cur        bufend  */
+
+                       /* fill space in middle of buf */
+                       seq = ring->seq + (ring->bufend - ring->cur) + (ring->end - ring->buf);
+                       read = vmlog_ringbuf_fill_forward(ring,ring->end,ring->cur - 1,
+                                       seq,len);
+                       count += read;
+                       len -= read;
+               }
+       }
+       else {
+               len = -len;
+
+               if (len > ring->seq)
+                       len = ring->seq;
+
+               if (ring->start <= ring->cur) {
+                       /* case A'1: ring->end >= ring->start  */
+                       /*                                     */
+                       /* vvvvvv                              */
+                       /* ------#####OOOOOOOOO-----------     */
+                       /* ^     ^    ^        ^          ^    */
+                       /* buf  start cur      end     bufend  */
+
+                       /* case B'2: ring->end < ring->start   */
+                       /*                                     */
+                       /* vvvvvvvvvvv                         */
+                       /* OOOOO------######OOOOOOOOOOOOOO     */
+                       /* ^    ^     ^     ^             ^    */
+                       /* buf  end   start cur        bufend  */
+
+                       /* fill space at beg of buf */
+                       seq = ring->seq - (ring->cur - ring->buf);
+                       read = vmlog_ringbuf_fill_backward(ring,ring->buf,ring->start,
+                                       seq,len);
+                       count += read;
+                       len -= read;
+
+                       if (ring->start != ring->buf)
+                               goto no_more_entries;
+
+                       /* case A'1: ring->end >= ring->start  */
+                       /*                                     */
+                       /*             vvvvvvvvvvvvvvvvvvv     */
+                       /* ###########OOOOOOOOO-----------     */
+                       /* ^          ^        ^          ^    */
+                       /* buf=start  cur      end     bufend  */
+
+                       /* case B'2: ring->end < ring->start   */
+                       /*                                     */
+                       /*                   vvvvvvvvvvvvv     */
+                       /* #################OOOOOOOOOOOOOO     */
+                       /* ^                ^             ^    */
+                       /* buf=start        cur    end=bufend  */
+
+                       /* fill space at end of buf */
+                       seq -= (ring->bufend - ring->cur - 1);
+                       read = vmlog_ringbuf_fill_backward(ring,ring->cur+1,ring->bufend,
+                                       seq,len);
+                       count += read;
+                       len -= read;
+               }
+               else {
+                       /* ring->start > ring->cur */
+
+                       /* case B'1: ring->end < ring->start   */
+                       /*                                     */
+                       /*             vvvvvvvvvvvv            */
+                       /* ###########OOOOOOOOO----#######     */
+                       /* ^          ^        ^   ^      ^    */
+                       /* buf        cur      end st. bufend  */
+
+                       /* no case A'2 */
+                       assert(ring->end < ring->start);
+
+                       /* fill space in middle of buf */
+                       seq = ring->seq - (ring->cur - ring->buf) - (ring->bufend - ring->cur - 1);
+                       read = vmlog_ringbuf_fill_backward(ring,ring->cur + 1,ring->start,
+                                       seq,len);
+                       count += read;
+                       len -= read;
+               }
+       }
+
+no_more_entries:
+       vmlog_ringbuf_check_invariants(ring);
+       
+       return count;
+}
+
+static void vmlog_ringbuf_reset(vmlog_ringbuf *ring)
+{
+       ring->start = ring->buf;
+       ring->cur = ring->buf;
+       ring->end = ring->buf;
+
+       ring->debug_availbefore = 0;
+       ring->debug_availafter = 0;
+
+       vmlog_ringbuf_check_invariants(ring);
+}
+
+static int vmlog_ringbuf_advance(vmlog_ringbuf *ring,int diff)
+{
+       int space;
+       
+       if (diff > 0) {
+               if (ring->end >= ring->start) {
+                       /* case A */
+advance_cur_to_end:
+                       space = ring->end - ring->cur;
+
+                       if (space <= 0)
+                               return 0;
+
+                       if (space < diff)
+                               diff = space;
+
+simple_advance:
+                       ring->cur += diff;
+                       ring->seq += diff;
+                       ring->debug_availbefore += diff;
+                       ring->debug_availafter -= diff;
+                       return diff;
+               }
+               else {
+                       /* case B */
+                       if (ring->end >= ring->cur)
+                               goto advance_cur_to_end;
+
+                       space = ring->bufend - ring->cur;
+                       if (space > diff)
+                               goto simple_advance;
+
+                       ring->cur = ring->buf - space;
+                       goto advance_cur_to_end;
+               }
+       }
+       else if (diff < 0) {
+               if (ring->end >= ring->start) {
+                       /* case A */
+advance_cur_to_start:
+                       space = ring->cur - ring->start;
+
+                       if (space <= 0)
+                               return 0;
+
+                       if (-space > diff)
+                               diff = -space;
+                       goto simple_advance;
+               }
+               else {
+                       /* case B */
+                       if (ring->cur >= ring->start)
+                               goto advance_cur_to_start;
+
+                       space = ring->cur - ring->buf;
+                       if (space >= -diff)
+                               goto simple_advance;
+
+                       ring->cur = ring->bufend + space;
+                       goto advance_cur_to_start;
+               }
+       }
+       else {
+               return 0;
+       }
+}
+
+void vmlog_ringbuf_seek(vmlog_ringbuf *ring,vmlog_seq_t seq)
+{
+       vmlog_seq_t diff;
+       
+       vmlog_ringbuf_check_invariants(ring);
+
+       diff = seq - ring->seq;
+       if (abs(diff) < ring->bufsize)
+               diff -= vmlog_ringbuf_advance(ring,(int)diff);
+
+       if (!diff)
+               return;
+
+       vmlog_ringbuf_reset(ring);
+       vmlog_file_seek(&(ring->file),seq * sizeof(vmlog_log_entry));
+       ring->seq = seq;
+
+       vmlog_ringbuf_check_invariants(ring);
+}
+
+vmlog_log_entry * vmlog_ringbuf_next(vmlog_ringbuf *ring,int prefetch)
+{
+       vmlog_ringbuf_check_invariants(ring);
+
+       while (1) {
+               if (ring->end >= ring->start) {
+                       /* case A */
+                       if (ring->cur < ring->end) {
+                               ring->debug_availafter--;
+                               ring->debug_availbefore++;
+                               ring->seq++;
+                               return ring->cur++;
+                       }
+               }
+               else {
+                       /* case B */
+                       if (ring->end >= ring->cur) {
+                               if (ring->cur < ring->end) {
+                                       ring->debug_availafter--;
+                                       ring->debug_availbefore++;
+                                       ring->seq++;
+                                       return ring->cur++;
+                               }
+                       }
+                       else {
+                               if (ring->cur < ring->bufend) {
+                                       vmlog_log_entry *r;
+
+                                       r = ring->cur;
+                                       ring->seq++;
+                                       if (++ring->cur == ring->bufend)
+                                               ring->cur = ring->buf;
+                                       ring->debug_availafter--;
+                                       ring->debug_availbefore++;
+                                       return r;
+                               }
+                       }
+               }       
+
+               if (!vmlog_ringbuf_fill(ring,prefetch))
+                       return NULL;
+       }
+
+       assert(0); /* NOT REACHED */
+       return NULL;
+}
+
+vmlog_log_entry * vmlog_ringbuf_prev(vmlog_ringbuf *ring,int prefetch)
+{
+       vmlog_ringbuf_check_invariants(ring);
+
+       while (1) {
+               if (ring->end >= ring->start) {
+                       /* case A */
+                       if (ring->cur > ring->start) {
+                               ring->debug_availafter++;
+                               ring->debug_availbefore--;
+                               ring->seq--;
+                               return --ring->cur;
+                       }
+               }
+               else {
+                       /* case B */
+                       if (ring->cur >= ring->start) {
+                               if (ring->cur > ring->start) {
+                                       ring->debug_availafter++;
+                                       ring->debug_availbefore--;
+                                       ring->seq--;
+                                       return --ring->cur;
+                               }
+                       }
+                       else {
+                               if (--ring->cur < ring->buf)
+                                       ring->cur = ring->bufend - 1;
+                               ring->seq--;
+                               ring->debug_availafter++;
+                               ring->debug_availbefore--;
+                               return ring->cur;
+                       }
+               }       
+
+               if (!vmlog_ringbuf_fill(ring,-prefetch))
+                       return NULL;
+       }
+
+       assert(0); /* NOT REACHED */
+       return NULL;
+}
+
+/*** option parsing **************************************************/
+
+int vmlog_opt_parse_seq(const char *arg,int len,vmlog_seq_t *seq)
+{
+       char *buf;
+       char *endptr;
+       int r;
+       
+       assert(arg);
+
+       if (len < 1)
+               return 0;
+
+       buf = vmlog_strdup(arg,len);
+       *seq = strtoll(buf,&endptr,10);
+
+       r = (endptr[0] == 0);
+
+       VMLOG_FREE_ARRAY(char,len+1,buf);
+       return r;
+}
+
+int vmlog_opt_parse_range(const char *arg,vmlog_seq_t *start,vmlog_seq_t *end)
+{
+       const char *sep;
+       int len;
+
+       sep = strchr(arg,':');
+       if (!sep) {
+               len = strlen(arg);
+               if (!vmlog_opt_parse_seq(arg,len,start))
+                       return 0;
+               *end = *start;
+               return 1;
+       }
+
+       len = sep - arg;
+       if (!len) {
+               *start = 0;
+       }
+       else {
+               if (!vmlog_opt_parse_seq(arg,len,start))
+                       return 0;
+       }
+
+       len = strlen(arg) - len - 1;
+       if (!len) {
+               *end = VMLOG_SEQ_MAX;
+       }
+       else {
+               if (!vmlog_opt_parse_seq(sep+1,len,end))
+                       return 0;
+       }
+       return 1;
+}
+
+static int vmlog_opt_parse_one_option(vmlog_options *opts, const char *arg, const char *nextarg)
+{
+       int eat;
+
+       if (strncmp(arg,"-vmlog:",7) != 0) {
+               return 0;
+       }
+
+       /* a vmlog option */
+
+       eat = 1;
+       if (strcmp(arg,"-vmlog:prefix") == 0) {
+               if (!nextarg)
+                       vmlog_die("expected a prefix after -vmlog:prefix");
+               opts->prefix = vmlog_strdup(nextarg,strlen(nextarg));
+               eat++;
+       }
+       else if (strcmp(arg,"-vmlog:strings") == 0) {
+               if (!nextarg)
+                       vmlog_die("expected a prefix after -vmlog:strings");
+               opts->stringprefix = vmlog_strdup(nextarg,strlen(nextarg));
+               eat++;
+       }
+       else if (strcmp(arg,"-vmlog:ignore") == 0) {
+               if (!nextarg)
+                       vmlog_die("expected a prefix after -vmlog:ignore");
+               opts->ignoreprefix = vmlog_strdup(nextarg,strlen(nextarg));
+               eat++;
+       }
+       else {
+               vmlog_die("unknown -vmlog:... option: %s",arg);
+       }
+
+       return eat;
+}
+
+vmlog_options *vmlog_opt_parse_cmd_line(int *pargc,char **argv)
+{
+       int i;
+       const char *arg;
+       vmlog_options *opts;
+       int eat;
+       int left;
+
+       assert(pargc);
+
+       VMLOG_XZNEW(opts,vmlog_options);
+
+       if (*pargc && argv[0])
+               opts->progname = vmlog_strdup(argv[0],strlen(argv[0]));
+
+       i = 1;
+       while (i < *pargc) {
+               arg = argv[i];
+               
+               left = *pargc - i - 1;
+
+               eat = vmlog_opt_parse_one_option(opts,arg,
+                               (left) ? argv[i+1] : NULL);
+
+               if (eat == 0) {
+                       i++;
+                       continue;
+               }
+
+               /* remove the option from the command line */
+               
+               memmove(argv + i,argv + i + eat,sizeof(char*) * (*pargc - (i+eat)));
+               *pargc -= eat;
+       }
+
+       return opts;
+}
+
+vmlog_options *vmlog_opt_parse_vmargs(JavaVMInitArgs *vmargs)
+{
+       int i;
+       const char *arg;
+       vmlog_options *opts;
+       int eat;
+
+       assert(vmargs);
+
+       VMLOG_XZNEW(opts,vmlog_options);
+
+       i = 0;
+       while (i < vmargs->nOptions) {
+               arg = vmargs->options[i].optionString;
+
+               eat = vmlog_opt_parse_one_option(opts,arg,
+                               (i+1 < vmargs->nOptions) ? vmargs->options[i+1].optionString : NULL);
+
+               if (eat == 0) {
+                       i++;
+                       continue;
+               }
+               
+               /* remove the option from the command line */
+               
+               memmove(vmargs->options + i,vmargs->options + i + eat,
+                               sizeof(JavaVMOption) * (vmargs->nOptions - (i+eat)));
+               vmargs->nOptions -= eat;
+       }
+
+       return opts;
+}
+
+void vmlog_opt_free(vmlog_options *opts)
+{
+       if (!opts)
+               return;
+
+       if (opts->prefix)
+               VMLOG_FREE_ARRAY(char,strlen(opts->prefix)+1,opts->prefix);
+       if (opts->stringprefix)
+               VMLOG_FREE_ARRAY(char,strlen(opts->stringprefix)+1,opts->stringprefix);
+       if (opts->ignoreprefix)
+               VMLOG_FREE_ARRAY(char,strlen(opts->ignoreprefix)+1,opts->ignoreprefix);
+
+       VMLOG_FREE(vmlog_options,opts);
+}
+
+/* vim: noet ts=8 sw=8
+ */
diff --git a/contrib/vmlog/vmlog.h b/contrib/vmlog/vmlog.h
new file mode 100644 (file)
index 0000000..f93481f
--- /dev/null
@@ -0,0 +1,283 @@
+/* vmlog - high-speed logging for free VMs                  */
+/* Copyright (C) 2006 Edwin Steiner <edwin.steiner@gmx.net> */
+
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _VMLOG_H_
+#define _VMLOG_H_
+
+#define VMLOG_PAD_TO_8
+
+#include <limits.h>
+
+/*** constants *******************************************************/
+
+/* CAUTION: these are indices into the vmlog_tag_definitions table! */
+#define VMLOG_TAG_ENTER  0
+#define VMLOG_TAG_LEAVE  1
+#define VMLOG_TAG_THROW  2
+#define VMLOG_TAG_CATCH  3
+#define VMLOG_TAG_UNWND  4
+#define VMLOG_TAG_SIGNL  5
+#define VMLOG_TAG_UNROL  6
+#define VMLOG_TAG_REROL  7
+
+/*** memory management macros ****************************************/
+
+#ifndef VMLOG_NEW
+#define VMLOG_NEW(type)                (type*)malloc(sizeof(type))
+#endif
+
+#ifndef VMLOG_FREE
+#define VMLOG_FREE(type,ptr)           free(ptr)
+#endif
+
+#ifndef VMLOG_NEW_ARRAY
+#define VMLOG_NEW_ARRAY(type,n)        (type*)malloc(sizeof(type)*(n))
+#endif
+
+#ifndef VMLOG_FREE_ARRAY
+#define VMLOG_FREE_ARRAY(type,n,ptr)   free(ptr)
+#endif
+
+/*** struct typedefs *************************************************/
+
+typedef struct vmlog_tag_definition vmlog_tag_definition;
+typedef struct vmlog_hash_entry vmlog_hash_entry;
+typedef struct vmlog_hash_table vmlog_hash_table;
+typedef struct vmlog_string_entry vmlog_string_entry;
+typedef struct vmlog_log_entry vmlog_log_entry;
+typedef struct vmlog_thread_log vmlog_thread_log;
+typedef struct vmlog_file vmlog_file;
+typedef struct vmlog_frame vmlog_frame;
+typedef struct vmlog_ringbuf vmlog_ringbuf;
+typedef struct vmlog_log vmlog_log;
+typedef struct vmlog_options vmlog_options;
+
+/*** integer types ***************************************************/
+
+typedef long long vmlog_fofs_t;
+typedef long long vmlog_seq_t;
+#define VMLOG_SEQ_FMT   "%lld"
+#define VMLOG_SEQ_FMT_W "%10lld"
+#ifdef LLONG_MAX
+#define VMLOG_SEQ_MAX LLONG_MAX
+#else
+#define VMLOG_SEQ_MAX 9223372036854775807LL
+#endif
+
+#ifndef VMLOG_HAVE_PTRINT
+typedef int ptrint; /* XXX */
+#endif
+
+/*** enums ***********************************************************/
+
+typedef enum {
+       vmlogRead,
+       vmlogAppend,
+       vmlogTruncateAppend
+} vmlog_fmode;
+
+/*** function types **************************************************/
+
+typedef void (*vmlog_hash_entry_destructor)(vmlog_hash_entry *entry);
+typedef void (*vmlog_log_function)(vmlog_log *vml,void *threadid,
+                                  const char *name,int namelen);
+
+/*** structs *********************************************************/
+
+struct vmlog_tag_definition {
+       char             *name;
+       char             *fixname;
+       int               depth;
+};
+
+struct vmlog_hash_entry {
+       unsigned int      hash;
+       int               len;
+       int               index;
+       void             *data;
+       vmlog_hash_entry *hashlink;
+};
+
+struct vmlog_hash_table {
+       int               size;
+       int               nentries;
+       vmlog_hash_entry *table;
+};
+
+struct vmlog_string_entry {
+       vmlog_fofs_t ofs;
+       int          len;
+#if defined(VMLOG_PAD_TO_8)
+       int          dummy;
+#endif
+};
+
+struct vmlog_log_entry {
+       int          tag:8;
+       int          index:24;
+};
+
+struct vmlog_file {
+       int          fd;
+       char        *fname;
+       int          fnamelen;
+       vmlog_fofs_t ofs;
+       vmlog_fofs_t size;
+};
+
+struct vmlog_frame {
+       int                index;
+       vmlog_seq_t        seq;
+};
+
+struct vmlog_thread_log {
+       void              *threadid;
+       int                threadidx;
+       int                depth;
+       int                ignoredepth;
+       vmlog_file         logfile;
+       vmlog_log_entry   *logbuf;
+       vmlog_log_entry   *logbufend;
+       vmlog_log_entry   *logbufptr;
+       int                logbufcap;
+       vmlog_frame       *frames;
+       int                framescap;
+       vmlog_seq_t        seq;
+};
+
+struct vmlog_log {
+       vmlog_hash_table   threadhash;
+       vmlog_hash_table   stringhash;
+       vmlog_file         idxfile;
+       vmlog_file         strfile;
+       char              *prefix;
+       int                prefixlen;
+       int                ignorelistlen;
+};
+
+struct vmlog_ringbuf {
+       vmlog_file         file;
+       vmlog_log_entry   *buf;
+       vmlog_log_entry   *bufend;
+       vmlog_log_entry   *start;
+       vmlog_log_entry   *end;
+       vmlog_log_entry   *cur;
+       int                bufsize;
+       vmlog_seq_t        seq;
+
+       int                debug_availbefore;
+       int                debug_availafter;
+};
+
+struct vmlog_options {
+       char              *progname;
+       char              *prefix;
+       char              *stringprefix;
+       char              *ignoreprefix;
+};
+
+/*** variables *******************************************************/
+
+extern vmlog_tag_definition vmlog_tag_definitions[];
+
+/*** public functions ************************************************/
+
+/* constructor / destructor */
+vmlog_log * vmlog_log_new(const char *prefix,int truncate);
+void vmlog_log_free(vmlog_log *vml);
+
+/* configuration */
+void vmlog_log_load_ignorelist(vmlog_log *vml,const char *prefix);
+
+/* logging */
+void vmlog_log_enter(vmlog_log *vml,void *threadid,const char *name,int namelen);
+void vmlog_log_leave(vmlog_log *vml,void *threadid,const char *name,int namelen);
+void vmlog_log_throw(vmlog_log *vml,void *threadid,const char *name,int namelen);
+void vmlog_log_catch(vmlog_log *vml,void *threadid,const char *name,int namelen);
+void vmlog_log_unwnd(vmlog_log *vml,void *threadid,const char *name,int namelen);
+void vmlog_log_signl(vmlog_log *vml,void *threadid,const char *name,int namelen);
+void vmlog_log_unrol(vmlog_log *vml,void *threadid,const char *name,int namelen);
+void vmlog_log_rerol(vmlog_log *vml,void *threadid,const char *name,int namelen);
+
+/* tag definitions */
+int vmlog_tag_from_name(const char *name,int namelen);
+
+/* thread logging */
+vmlog_thread_log *vmlog_thread_log_new(vmlog_log *vml,void *threadid,int index);
+void vmlog_thread_log_free(vmlog_thread_log *tlog);
+vmlog_frame * vmlog_thread_log_enter(vmlog_thread_log *tlog,int index,vmlog_seq_t seq);
+vmlog_frame * vmlog_thread_log_leave(vmlog_thread_log *tlog,int index,vmlog_seq_t seq);
+
+/* string/index handling */
+int vmlog_get_string_index(vmlog_log *vml,const char *data,int len);
+void vmlog_load_stringhash(vmlog_log *vml,const char *prefix);
+
+/* string helpers */
+char *vmlog_concat3(const char *a,const char *b,const char *c,int *plen);
+char *vmlog_concat4len(const char *a,int alen,const char *b,int blen,
+                      const char *c,int clen,const char *d,int dlen,int *plen);
+
+/* error reporting */
+void vmlog_set_progname(const char *progname);
+void vmlog_die(const char *fmt,...);
+void vmlog_warn(const char *fmt,...);
+void vmlog_die_usage(const char *usage,int error);
+
+/* file handling */
+void vmlog_file_open(vmlog_file *file,const char *fname,vmlog_fmode fmode);
+void vmlog_file_close(vmlog_file *file);
+void vmlog_file_append(vmlog_file *file,const void *data,int len);
+void * vmlog_file_mmap(const char *fname,int *plen);
+void vmlog_file_munmap(void *m,int len);
+void vmlog_file_stat(vmlog_file *file);
+void vmlog_file_seek(vmlog_file *file,vmlog_fofs_t ofs);
+
+/* log entry ring buffer */
+vmlog_ringbuf * vmlog_ringbuf_new(const char *fname,int bufsize);
+void vmlog_ringbuf_free(vmlog_ringbuf *ring);
+void vmlog_ringbuf_seek(vmlog_ringbuf *ring,vmlog_seq_t seq);
+int vmlog_ringbuf_fill(vmlog_ringbuf *ring,int len);
+vmlog_log_entry * vmlog_ringbuf_next(vmlog_ringbuf *ring,int prefetch);
+vmlog_log_entry * vmlog_ringbuf_prev(vmlog_ringbuf *ring,int prefetch);
+
+/* option parsing */
+int vmlog_opt_parse_seq(const char *arg,int len,vmlog_seq_t *seq);
+int vmlog_opt_parse_range(const char *arg,vmlog_seq_t *start,vmlog_seq_t *end);
+vmlog_options *vmlog_opt_parse_cmd_line(int *pargc,char **argv);
+void vmlog_opt_free(vmlog_options *opts);
+
+/*** memory allocation helpers ***************************************/
+
+/* allocate, check, and zero memory */
+#define VMLOG_XZNEW(var,type) \
+       do { var = VMLOG_NEW(type); \
+            if (!(var)) vmlog_die("out of memory"); \
+            memset((var),0,sizeof(type)); \
+       } while (0)
+
+/* allocate, check, and zero memory for array */
+#define VMLOG_XZNEW_ARRAY(var,type,n) \
+       do { var = VMLOG_NEW_ARRAY(type,n); \
+            if (!(var)) vmlog_die("out of memory"); \
+            memset((var),0,(n)*sizeof(type)); \
+       } while (0)
+
+#endif
+
+/* vim: noet ts=8 sw=8
+ */
diff --git a/contrib/vmlog/vmlog_cacao.c b/contrib/vmlog/vmlog_cacao.c
new file mode 100644 (file)
index 0000000..04b9e8f
--- /dev/null
@@ -0,0 +1,167 @@
+/* vmlog - high-speed logging for free VMs                  */
+/* Copyright (C) 2006 Edwin Steiner <edwin.steiner@gmx.net> */
+
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/* vmlog_cacao.c - code to be #included in cacao */
+
+#include <vmlog_cacao.h>
+#define VMLOG_HAVE_PTRINT
+#include <vmlog.h>
+#include <assert.h>
+
+/*** global variables ************************************************/
+
+static vmlog_log *vmlog_global_log = NULL;
+static java_objectheader vmlog_global_lock;
+
+/*** locking *********************************************************/
+
+#define VMLOG_LOCK(vml)    lock_monitor_enter(&vmlog_global_lock)
+#define VMLOG_UNLOCK(vml)  lock_monitor_exit(&vmlog_global_lock)
+
+/*** include the vmlog code ******************************************/
+
+#include <vmlog.c>
+
+/*** internal functions **********************************************/
+
+void vmlog_cacao_init(JavaVMInitArgs *vmargs)
+{
+       vmlog_options *opts;
+
+       opts = vmlog_opt_parse_vmargs(vmargs);
+       
+       if (!opts->prefix)
+               return;
+
+       vmlog_global_log = vmlog_log_new(opts->prefix,1);
+
+       if (opts->ignoreprefix) {
+               vmlog_log_load_ignorelist(vmlog_global_log,
+                               opts->ignoreprefix);
+       }
+
+       if (opts->stringprefix) {
+               vmlog_load_stringhash(vmlog_global_log,
+                               opts->stringprefix);
+       }
+
+       vmlog_opt_free(opts);
+}
+
+void vmlog_cacao_init_lock(void)
+{
+       lock_init_object_lock(&vmlog_global_lock);
+}
+
+static void vmlog_cacao_do_log(vmlog_log_function fun,
+                              methodinfo *m)
+{
+       char *name;
+       int namelen;
+
+       assert(m);
+
+       if (!vmlog_global_log)
+               return;
+
+       name = vmlog_concat4len(m->class->name->text,m->class->name->blength,
+                               ".",1,
+                               m->name->text,m->name->blength,
+                               m->descriptor->text,m->descriptor->blength,
+                               &namelen);
+
+       fun(vmlog_global_log,(void*) THREADOBJECT,name,namelen);
+
+       VMLOG_FREE_ARRAY(char,namelen+1,name);
+}
+
+/*** functions callable from cacao ***********************************/
+
+void vmlog_cacao_enter_method(methodinfo *m)
+{
+       vmlog_cacao_do_log(vmlog_log_enter,m);
+}
+
+void vmlog_cacao_leave_method(methodinfo *m)
+{
+       vmlog_cacao_do_log(vmlog_log_leave,m);
+}
+
+void vmlog_cacao_unrol_method(methodinfo *m)
+{
+       vmlog_cacao_do_log(vmlog_log_unrol,m);
+}
+
+void vmlog_cacao_rerol_method(methodinfo *m)
+{
+       vmlog_cacao_do_log(vmlog_log_rerol,m);
+}
+
+void vmlog_cacao_unwnd_method(methodinfo *m)
+{
+       vmlog_cacao_do_log(vmlog_log_unwnd,m);
+}
+
+void vmlog_cacao_throw(java_objectheader *xptr)
+{
+       classinfo *c;
+       
+       if (!vmlog_global_log)
+               return;
+
+       if (xptr) {
+               c = xptr->vftbl->class;
+               vmlog_log_throw(vmlog_global_log,(void*) THREADOBJECT,
+                               c->name->text,c->name->blength);
+       }
+       else {
+               vmlog_log_throw(vmlog_global_log,(void*) THREADOBJECT,
+                               "unknown Throwable",strlen("unknown Throwable"));
+       }
+}
+
+void vmlog_cacao_catch(java_objectheader *xptr)
+{
+       classinfo *c;
+       
+       if (!vmlog_global_log)
+               return;
+
+       if (xptr) {
+               c = xptr->vftbl->class;
+               vmlog_log_catch(vmlog_global_log,(void*) THREADOBJECT,
+                               c->name->text,c->name->blength);
+       }
+       else {
+               vmlog_log_catch(vmlog_global_log,(void*) THREADOBJECT,
+                               "unknown Throwable",strlen("unknown Throwable"));
+       }
+}
+
+void vmlog_cacao_signl(const char *name)
+{
+       if (!vmlog_global_log)
+               return;
+
+       vmlog_log_signl(vmlog_global_log,(void*) THREADOBJECT,
+                       name, strlen(name));
+}
+
+/* vim: noet ts=8 sw=8
+ */
+
diff --git a/contrib/vmlog/vmlog_cacao.h b/contrib/vmlog/vmlog_cacao.h
new file mode 100644 (file)
index 0000000..c67cca8
--- /dev/null
@@ -0,0 +1,42 @@
+/* vmlog - high-speed logging for free VMs                  */
+/* Copyright (C) 2006 Edwin Steiner <edwin.steiner@gmx.net> */
+
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _VMLOG_CACAO_H_
+#define _VMLOG_CACAO_H_
+
+#include <threads/native/threads.h>
+
+void vmlog_cacao_init(JavaVMInitArgs *vmargs);
+
+void vmlog_cacao_init_lock(void);
+
+void vmlog_cacao_enter_method(methodinfo *m);
+void vmlog_cacao_leave_method(methodinfo *m);
+void vmlog_cacao_unwnd_method(methodinfo *m);
+void vmlog_cacao_unrol_method(methodinfo *m);
+void vmlog_cacao_rerol_method(methodinfo *m);
+
+void vmlog_cacao_throw(java_objectheader *xptr);
+void vmlog_cacao_catch(java_objectheader *xptr);
+void vmlog_cacao_signl(const char *name);
+
+#endif
+
+/* vim: noet ts=8 sw=8
+ */
+
diff --git a/contrib/vmlog/vmlog_cacao.patch b/contrib/vmlog/vmlog_cacao.patch
new file mode 100644 (file)
index 0000000..0849ab4
--- /dev/null
@@ -0,0 +1,235 @@
+diff --git a/src/threads/native/lock.c b/src/threads/native/lock.c
+index 6eeec82..b6909f1 100644
+--- a/src/threads/native/lock.c
++++ b/src/threads/native/lock.c
+@@ -53,6 +53,8 @@
+ # include "vmcore/statistics.h"
+ #endif
++#include <vmlog_cacao.h>
++
+ /* arch.h must be here because it defines USE_FAKE_ATOMIC_INSTRUCTIONS */
+ #include "arch.h"
+@@ -222,6 +224,8 @@ void lock_init(void)
+       pthread_mutex_init(&lock_global_pool_lock, NULL);
+       lock_hashtable_init();
++
++      vmlog_cacao_init_lock();
+ }
+diff --git a/src/vm/builtin.c b/src/vm/builtin.c
+index a2298c9..168d950 100644
+--- a/src/vm/builtin.c
++++ b/src/vm/builtin.c
+@@ -81,6 +81,8 @@
+ #include "vmcore/options.h"
+ #include "vmcore/rt-timing.h"
++#include <vmlog_cacao.h>
++
+ /* include builtin tables *****************************************************/
+@@ -1149,6 +1151,8 @@ java_objectheader *builtin_trace_exception(java_objectheader *xptr,
+       s4    dumpsize;
+       codeinfo *code;
++      return xptr;
++
+       if (opt_verbosecall && indent)
+               methodindent--;
+@@ -1420,6 +1424,9 @@ void builtin_verbosecall_enter(s8 a0, s8 a1,
+       s4          i;
+       s4          pos;
++      vmlog_cacao_enter_method(m);
++      return;
++
+       md = m->parseddesc;
+       /* calculate message length */
+@@ -1591,6 +1598,9 @@ void builtin_verbosecall_exit(s8 l, double d, float f, methodinfo *m)
+       s4          pos;
+       imm_union   val;
++      vmlog_cacao_leave_method(m);
++      return;
++
+       md = m->parseddesc;
+       /* calculate message length */
+@@ -2683,6 +2693,9 @@ java_objectheader *builtin_clone(void *env, java_objectheader *o)
+     return co;
+ }
++#define NDEBUG
++#include <vmlog_cacao.c>
++
+ /*
+  * These are local overrides for various environment variables in Emacs.
+diff --git a/src/vm/exceptions.c b/src/vm/exceptions.c
+index 6754a62..11c3e3c 100644
+--- a/src/vm/exceptions.c
++++ b/src/vm/exceptions.c
+@@ -70,6 +70,8 @@
+ #include "vmcore/loader.h"
+ #include "vmcore/options.h"
++#include <vmlog_cacao.h>
++
+ /* for raising exceptions from native methods *********************************/
+@@ -1771,6 +1773,8 @@ u1 *exceptions_handle_exception(java_objectheader *xptr, u1 *xpc, u1 *pv, u1 *sp
+               builtin_trace_exception(xptr, m, xpc, 1);
+ #endif
++      vmlog_cacao_throw(xptr);
++
+       for (i = 0; i < exceptiontablelength; i++) {
+               /* ATTENTION: keep this here, as we need to decrement the
+            pointer before the loop executes! */
+@@ -1795,6 +1799,8 @@ u1 *exceptions_handle_exception(java_objectheader *xptr, u1 *xpc, u1 *pv, u1 *sp
+ #if !defined(NDEBUG)
+                               /* Print stacktrace of exception when caught. */
++                              vmlog_cacao_catch(xptr);
++
+                               if (opt_verboseexception) {
+                                       exceptions_print_exception(xptr);
+                                       stacktrace_print_trace(xptr);
+@@ -1852,6 +1858,8 @@ u1 *exceptions_handle_exception(java_objectheader *xptr, u1 *xpc, u1 *pv, u1 *sp
+ #if !defined(NDEBUG)
+                               /* Print stacktrace of exception when caught. */
++                              vmlog_cacao_catch(xptr);
++
+                               if (opt_verboseexception) {
+                                       exceptions_print_exception(xptr);
+                                       stacktrace_print_trace(xptr);
+@@ -1884,6 +1892,8 @@ u1 *exceptions_handle_exception(java_objectheader *xptr, u1 *xpc, u1 *pv, u1 *sp
+       /* none of the exceptions catch this one */
++      vmlog_cacao_unwnd_method(m);
++
+       return NULL;
+ }
+ #endif /* defined(ENABLE_JIT) */
+diff --git a/src/vm/jit/codegen-common.c b/src/vm/jit/codegen-common.c
+index 5981383..a5581b3 100644
+--- a/src/vm/jit/codegen-common.c
++++ b/src/vm/jit/codegen-common.c
+@@ -101,6 +101,8 @@
+ # include "vmcore/statistics.h"
++#include <vmlog_cacao.h>
++
+ /* in this tree we store all method addresses *********************************/
+@@ -640,6 +642,8 @@ u1 *codegen_get_pv_from_pc(u1 *pc)
+       if (mte == NULL) {
+               /* No method was found.  Let's dump a stacktrace. */
++              vmlog_cacao_signl("SIGSEGV");
++
+               log_println("We received a SIGSEGV and tried to handle it, but we were");
+               log_println("unable to find a Java method at:");
+               log_println("");
+diff --git a/src/vm/jit/intrp/asmpart.c b/src/vm/jit/intrp/asmpart.c
+index 52ec0d4..e9bcc50 100644
+--- a/src/vm/jit/intrp/asmpart.c
++++ b/src/vm/jit/intrp/asmpart.c
+@@ -53,6 +53,8 @@
+ #include "vmcore/loader.h"
+ #include "vmcore/options.h"
++#include <vmlog_cacao.h>
++
+ static bool intrp_asm_vm_call_method_intern(methodinfo *m, s4 vmargscount,
+                                                                                       vm_arg *vmargs)
+@@ -206,6 +208,8 @@ Inst *intrp_asm_handle_exception(Inst *ip, java_objectheader *o, Cell *fp, Cell
+                 builtin_trace_exception(o, m, ip, 1);
+ #endif
++        vmlog_cacao_throw(o);
++
+         for (i = 0; i < exceptiontablelength; i++) {
+                 ex--;
+@@ -260,6 +264,8 @@ Inst *intrp_asm_handle_exception(Inst *ip, java_objectheader *o, Cell *fp, Cell
+                 if (ip-1 >= (Inst *) ex->startpc && ip-1 < (Inst *) ex->endpc &&
+                         (c == NULL || builtin_instanceof(o, c))) 
+                 {
++                        vmlog_cacao_catch(o);
++
+                         *new_spp = (Cell *)(((u1 *)fp) - framesize - SIZEOF_VOID_P);
+                         *new_fpp = fp;
+                         return (Inst *) (ex->handlerpc);
+@@ -289,6 +295,8 @@ Inst *intrp_asm_handle_exception(Inst *ip, java_objectheader *o, Cell *fp, Cell
+         /* unwind stack frame */
++        vmlog_cacao_unwnd_method(m);
++
+         ip = (Inst *)access_local_cell(-framesize - SIZEOF_VOID_P);
+         fp = (Cell *)access_local_cell(-framesize);
+   }
+diff --git a/src/vm/jit/replace.c b/src/vm/jit/replace.c
+index e16217b..7bbeeb6 100644
+--- a/src/vm/jit/replace.c
++++ b/src/vm/jit/replace.c
+@@ -57,6 +57,7 @@
+ #define REPLACE_PATCH_DYNAMIC_CALL
+ /*#define REPLACE_PATCH_ALL*/
++#include <vmlog_cacao.h>
+ /*** architecture-dependent configuration *************************************/
+@@ -2309,6 +2310,7 @@ sourcestate_t *replace_recover_source_state(rplpoint *rp,
+                          replace_replacement_point_println(rp, 1); );
+               replace_read_executionstate(rp, es, ss, ss->frames == NULL);
++              vmlog_cacao_unrol_method(ss->frames->method);
+ #if defined(REPLACE_STATISTICS)
+               REPLACE_COUNT(stat_frames);
+@@ -2529,6 +2531,7 @@ static void replace_build_execution_state_intern(sourcestate_t *ss,
+               es->code = ss->frames->tocode;
+               prevframe = ss->frames;
++              vmlog_cacao_rerol_method(ss->frames->method);
+               replace_write_executionstate(rp, es, ss, ss->frames->down == NULL);
+               DOLOG( replace_executionstate_println(es); );
+diff --git a/src/vm/vm.c b/src/vm/vm.c
+index 362e2fd..0396243 100644
+--- a/src/vm/vm.c
++++ b/src/vm/vm.c
+@@ -82,6 +82,8 @@
+ # include "native/jvmti/cacaodbg.h"
+ #endif
++#include <vmlog_cacao.h>
++
+ /* Invocation API variables ***************************************************/
+@@ -739,6 +741,8 @@ bool vm_create(JavaVMInitArgs *vm_args)
+       jdwp = agentbypath = false;
+ #endif
++      vmlog_cacao_init(vm_args);
++
+       /* check the JNI version requested */
+       switch (vm_args->version) {
diff --git a/contrib/vmlog/vmlog_jamvm.c b/contrib/vmlog/vmlog_jamvm.c
new file mode 100644 (file)
index 0000000..9226888
--- /dev/null
@@ -0,0 +1,140 @@
+/* vmlog - high-speed logging for free VMs                  */
+/* Copyright (C) 2006 Edwin Steiner <edwin.steiner@gmx.net> */
+
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/* vmlog_jamvm.c - code to be #included in jamvm */
+
+#include <vmlog_jamvm.h>
+#include <vmlog.h>
+#include <assert.h>
+
+/*** global variables ************************************************/
+
+static vmlog_log *vmlog_global_log = NULL;
+static VMLock vmlog_global_lock;
+
+/*** locking *********************************************************/
+
+#define VMLOG_LOCK(vml)    lockVMLock(vmlog_global_lock,threadSelf())
+#define VMLOG_UNLOCK(vml)  unlockVMLock(vmlog_global_lock,threadSelf())
+
+/*** include the vmlog code ******************************************/
+
+#include "vmlog.c"
+
+/*** internal functions **********************************************/
+
+void vmlog_jamvm_init(int *pargc,char **argv)
+{
+       vmlog_options *opts;
+
+       opts = vmlog_opt_parse_cmd_line(pargc,argv);
+       
+       if (!opts->prefix)
+               return;
+
+       vmlog_global_log = vmlog_log_new(opts->prefix,1);
+
+       if (opts->ignoreprefix) {
+               vmlog_log_load_ignorelist(vmlog_global_log,
+                               opts->ignoreprefix);
+       }
+
+       if (opts->stringprefix) {
+               vmlog_load_stringhash(vmlog_global_log,
+                               opts->stringprefix);
+       }
+
+       vmlog_opt_free(opts);
+}
+
+static void vmlog_jamvm_do_log(vmlog_log_function fun,
+                              Object *thread,MethodBlock *mb)
+{
+       char *name;
+       int namelen;
+       ClassBlock *cb;
+       
+       assert(mb);
+
+       if (!vmlog_global_log)
+               return;
+
+       cb = CLASS_CB(mb->class);
+               
+       name = vmlog_concat4len(
+               cb->name,strlen(cb->name),
+               ".",1,
+               mb->name,strlen(mb->name),
+               mb->type,strlen(mb->type),
+               &namelen);
+
+       fun(vmlog_global_log,thread,name,namelen);
+
+       VMLOG_FREE_ARRAY(char,namelen+1,name);
+}
+
+/*** functions callable from jamvm ***********************************/
+
+void vmlog_jamvm_enter_method(Object *thread,MethodBlock *mb)
+{
+       vmlog_jamvm_do_log(vmlog_log_enter,thread,mb);
+}
+
+void vmlog_jamvm_leave_method(Object *thread,MethodBlock *mb)
+{
+       vmlog_jamvm_do_log(vmlog_log_leave,thread,mb);
+}
+
+void vmlog_jamvm_unwnd_method(Object *thread,MethodBlock *mb)
+{
+       vmlog_jamvm_do_log(vmlog_log_unwnd,thread,mb);
+}
+
+void vmlog_jamvm_throw(Object *thread,Object *exp)
+{
+       ClassBlock *cb;
+       
+       assert(exp);
+
+       if (!vmlog_global_log)
+               return;
+
+       cb = CLASS_CB(exp->class);
+       assert(cb);
+
+       vmlog_log_throw(vmlog_global_log,thread,cb->name,strlen(cb->name));
+}
+
+void vmlog_jamvm_catch(Object *thread,Object *exp)
+{
+       ClassBlock *cb;
+       
+       assert(exp);
+
+       if (!vmlog_global_log)
+               return;
+
+       cb = CLASS_CB(exp->class);
+       assert(cb);
+
+       vmlog_log_catch(vmlog_global_log,thread,cb->name,strlen(cb->name));
+}
+
+/* vim: noet ts=8 sw=8
+ */
+
diff --git a/contrib/vmlog/vmlog_jamvm.h b/contrib/vmlog/vmlog_jamvm.h
new file mode 100644 (file)
index 0000000..976f28f
--- /dev/null
@@ -0,0 +1,35 @@
+/* vmlog - high-speed logging for free VMs                  */
+/* Copyright (C) 2006 Edwin Steiner <edwin.steiner@gmx.net> */
+
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _VMLOG_JAMVM_H_
+#define _VMLOG_JAMVM_H_
+
+void vmlog_jamvm_init(int *pargc,char **argv);
+
+void vmlog_jamvm_enter_method(Object *thread,MethodBlock *mb);
+void vmlog_jamvm_leave_method(Object *thread,MethodBlock *mb);
+void vmlog_jamvm_unwnd_method(Object *thread,MethodBlock *mb);
+
+void vmlog_jamvm_throw(Object *thread,Object *exp);
+void vmlog_jamvm_catch(Object *thread,Object *exp);
+
+#endif
+
+/* vim: noet ts=8 sw=8
+ */
+
diff --git a/contrib/vmlog/vmlog_jamvm.patch b/contrib/vmlog/vmlog_jamvm.patch
new file mode 100644 (file)
index 0000000..25c1ba9
--- /dev/null
@@ -0,0 +1,133 @@
+diff -u -r jamvm-1.4.2/src/excep.c vmlog-jamvm-1.4.2/src/excep.c
+--- jamvm-1.4.2/src/excep.c    2005-12-31 08:12:55.000000000 +0100
++++ vmlog-jamvm-1.4.2/src/excep.c      2006-03-12 00:42:34.000000000 +0100
+@@ -23,6 +23,8 @@
+ #include "jam.h"
+ #include "lock.h"
++#include <vmlog_jamvm.h>
++
+ extern char VM_initing;
+ static Class *ste_class, *ste_array_class, *throw_class, *vmthrow_class;
+@@ -83,6 +85,7 @@
+                 }
+                 getExecEnv()->exception = exp;
++              vmlog_jamvm_throw(getExecEnv()->thread,exp);
+             }
+         }
+     }
+@@ -90,11 +93,15 @@
+ void setException(Object *exp) {
+     getExecEnv()->exception = exp;
++    vmlog_jamvm_throw(getExecEnv()->thread,exp);
+ }
+ void clearException() {
+     ExecEnv *ee = getExecEnv();
++    if (ee->exception)
++      vmlog_jamvm_catch(ee->thread,ee->exception);
++
+     if(ee->overflow) {
+         ee->overflow = FALSE;
+         ee->stack_end -= STACK_RED_ZONE_SIZE;
+@@ -157,6 +164,8 @@
+     while(((handler_pc = findCatchBlockInMethod(frame->mb, exception, frame->last_pc)) == NULL)
+                     && (frame->prev->mb != NULL)) {
++      vmlog_jamvm_unwnd_method(getExecEnv()->thread,frame->mb);
++
+         if(frame->mb->access_flags & ACC_SYNCHRONIZED) {
+             Object *sync_ob = frame->mb->access_flags & ACC_STATIC ?
+                     (Object*)frame->mb->class : (Object*)frame->lvars[0];
+diff -u -r jamvm-1.4.2/src/frame.h vmlog-jamvm-1.4.2/src/frame.h
+--- jamvm-1.4.2/src/frame.h    2005-09-05 02:02:45.000000000 +0200
++++ vmlog-jamvm-1.4.2/src/frame.h      2006-03-14 00:26:07.000000000 +0100
+@@ -53,7 +53,12 @@
+                                                                 \
+     new_frame->prev = dummy;                                    \
+     ee->last_frame = new_frame;                                 \
++    vmlog_jamvm_enter_method(ee->thread,mb);                    \
+ }
+ #define POP_TOP_FRAME(ee)                                       \
++    if (ee->exception)                                          \
++        vmlog_jamvm_unwnd_method(ee->thread,mb);                \
++    else                                                        \
++        vmlog_jamvm_leave_method(ee->thread,mb);                \
+     ee->last_frame = ee->last_frame->prev->prev;
+diff -u -r jamvm-1.4.2/src/interp.c vmlog-jamvm-1.4.2/src/interp.c
+--- jamvm-1.4.2/src/interp.c   2006-01-22 23:32:38.000000000 +0100
++++ vmlog-jamvm-1.4.2/src/interp.c     2006-03-13 04:21:48.000000000 +0100
+@@ -59,6 +59,8 @@
+     }                                                                      \
+ }
++#include "vmlog_jamvm.c"
++
+ uintptr_t *executeJava() {
+     CodePntr pc;
+     ExecEnv *ee = getExecEnv();
+@@ -2220,6 +2222,8 @@
+     ee->last_frame = new_frame;
++    vmlog_jamvm_enter_method(ee->thread,new_mb);
++
+     if(new_mb->access_flags & ACC_SYNCHRONIZED) {
+         sync_ob = new_mb->access_flags & ACC_STATIC ? (Object*)new_mb->class : (Object*)*arg1;
+         objectLock(sync_ob);
+@@ -2229,6 +2233,11 @@
+         ostack = (*(uintptr_t *(*)(Class*, MethodBlock*, uintptr_t*))
+                      new_mb->native_invoker)(new_mb->class, new_mb, arg1);
++      if (exceptionOccured0(ee))
++              vmlog_jamvm_unwnd_method(ee->thread,new_mb);
++      else
++              vmlog_jamvm_leave_method(ee->thread,new_mb);
++
+         if(sync_ob)
+             objectUnlock(sync_ob);
+@@ -2262,6 +2271,8 @@
+         return ostack;
+     }
++    vmlog_jamvm_leave_method(ee->thread,mb);
++
+     if(mb->access_flags & ACC_SYNCHRONIZED) {
+         Object *sync_ob = mb->access_flags & ACC_STATIC ? (Object*)mb->class : this;
+         objectUnlock(sync_ob);
+@@ -2294,6 +2305,8 @@
+             return NULL;
+         }
++      vmlog_jamvm_catch(ee->thread,excep);
++
+         /* If we're handling a stack overflow, reduce the stack
+            back past the red zone to enable handling of further
+            overflows */
+diff -u -r jamvm-1.4.2/src/jam.c vmlog-jamvm-1.4.2/src/jam.c
+--- jamvm-1.4.2/src/jam.c      2006-01-17 01:36:59.000000000 +0100
++++ vmlog-jamvm-1.4.2/src/jam.c        2006-03-13 04:32:16.000000000 +0100
+@@ -25,6 +25,8 @@
+ #include "jam.h"
++#include <vmlog_jamvm.h>
++
+ /* Setup default values for command line args */
+ static int noasyncgc = FALSE;
+@@ -282,6 +284,8 @@
+     int status;
+     int i;
++    vmlog_jamvm_init(&argc,argv);
++
+     int class_arg = parseCommandLine(argc, argv);
+     initVM();
diff --git a/contrib/vmlog/vmlogdiff.c b/contrib/vmlog/vmlogdiff.c
new file mode 100644 (file)
index 0000000..fb6076f
--- /dev/null
@@ -0,0 +1,744 @@
+/* vmlog - high-speed logging for free VMs                  */
+/* Copyright (C) 2006 Edwin Steiner <edwin.steiner@gmx.net> */
+
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "vmlog.h"
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#define VMLOGDIFF_RINGBUF_SIZE 1024*1024
+#define VMLOGDIFF_PREFETCH 256
+
+
+#if 0
+#define LOG(args) printf args
+#else
+#define LOG(args)
+#endif
+
+typedef struct vmlogdiff_removed vmlogdiff_removed;
+
+struct vmlogdiff_removed {
+       vmlog_seq_t start;
+       vmlog_seq_t len;
+};
+
+char *opt_prefix;
+char *opt_fname[2];
+int opt_verbose = 0;
+int opt_context = 10;
+
+char *g_idxfname;
+char *g_strfname;
+char *g_cleanedfname[2] = { "vmlogdiff.A.clean", "vmlogdiff.B.clean" };
+char *g_removedfname[2] = { "vmlogdiff.A.removed", "vmlogdiff.B.removed" };
+int g_idxlen;
+int g_strlen;
+int g_nstrings;
+char *g_indentation = "    ";
+vmlog_seq_t g_context_available = 0;
+vmlog_seq_t g_context_requested = 0;
+
+vmlog_string_entry *g_idxmap;
+char *g_strmap;
+vmlogdiff_removed *g_removedmap[2];
+vmlogdiff_removed *g_removedptr[2];
+vmlogdiff_removed *g_removedend[2];
+int g_removedlen[2];
+int g_nremoved[2];
+vmlog_seq_t g_showseq[2] = { 0 };
+
+int g_level = 0;
+
+vmlog_ringbuf *g_ringbuf[2] = { NULL };
+vmlog_ringbuf *g_ringbuf_back[2] = { NULL };
+vmlog_ringbuf *g_ringbuf_show[2] = { NULL };
+vmlog_ringbuf *g_ringbuf_orig[2] = { NULL };
+
+const char *usage = 
+"Usage: vmlogdiff [options] prefix fileA fileB\n"
+"\n"
+"    Options:\n"
+"        -h      display this help message\n"
+"        -v      verbose messages to stderr\n"
+"\n";
+
+/* global variables for diff algorithm */
+
+vmlog_seq_t g_n[2];
+vmlog_seq_t *g_Vforw_buf = NULL;
+vmlog_seq_t *g_Vback_buf = NULL;
+vmlog_seq_t *g_Vforw = NULL;
+vmlog_seq_t *g_Vback = NULL;
+int g_maxD;
+int g_Vsize = 0;
+
+char *g_index_used = NULL;
+
+static void compare(vmlog_seq_t xstart,vmlog_seq_t ystart,vmlog_seq_t xend,vmlog_seq_t yend);
+       
+static void diff_alloc(int maxD)
+{
+       assert(maxD >= 0);
+
+       if (g_Vsize) {
+               VMLOG_FREE_ARRAY(vmlog_seq_t,g_Vsize,g_Vforw_buf);
+               VMLOG_FREE_ARRAY(vmlog_seq_t,g_Vsize,g_Vback_buf);
+       }
+
+       g_maxD = maxD;
+       g_Vsize = 2 * g_maxD + 1;
+
+       g_Vforw_buf = VMLOG_NEW_ARRAY(vmlog_seq_t,g_Vsize);
+       g_Vback_buf = VMLOG_NEW_ARRAY(vmlog_seq_t,g_Vsize);
+
+       g_Vforw = g_Vforw_buf + g_maxD;
+       g_Vback = g_Vback_buf + g_maxD;
+}
+
+void parse_command_line(int argc,char **argv)
+{
+       int r;
+
+       while (1) {
+               r = getopt(argc,argv,"hv");
+               if (r == -1)
+                       break;
+               switch (r) {
+                       case 'h':
+                               vmlog_die_usage(usage,0);
+
+                       case 'v':
+                               opt_verbose++;
+                               break;
+
+                       case '?':
+                               vmlog_die_usage(usage,1);
+               }
+       }
+
+       if (argc - optind < 3)
+               vmlog_die_usage(usage,1);
+       
+       opt_prefix = argv[optind++];
+       opt_fname[0] = argv[optind++];
+       opt_fname[1] = argv[optind++];
+}
+
+static void fprint_string(FILE *file,int index)
+{
+       char *str;
+       vmlog_string_entry *strent;
+       char *buf;
+
+       strent = g_idxmap + index;
+       str = g_strmap + strent->ofs;
+
+       buf = VMLOG_NEW_ARRAY(char,strent->len+1);
+
+       memcpy(buf,str,strent->len);
+       buf[strent->len] = 0;
+
+       fputs(buf,file);
+       
+       VMLOG_FREE_ARRAY(char,strent->len+1,buf);
+}
+
+static void dump_log_entry(vmlog_log_entry *logent,int depth)
+{
+#if 0
+       fprintf(stdout,"%d",depth);
+       fputc(':',stdout);
+       if (depth < 100) {
+               fputc(' ',stdout);
+               if (depth < 10)
+                       fputc(' ',stdout);
+       }
+       for (i=0; i<depth; ++i)
+               fputs(g_indentation,stdout);
+#endif
+       switch(logent->tag) {
+               case VMLOG_TAG_ENTER: fputs("enter ",stdout); break;
+               case VMLOG_TAG_LEAVE: fputs("leave ",stdout); break;
+               case VMLOG_TAG_THROW: fputs("throw ",stdout); break;
+               case VMLOG_TAG_CATCH: fputs("catch ",stdout); break;
+               case VMLOG_TAG_UNWND: fputs("unwnd ",stdout); break;
+       }
+       fprint_string(stdout,logent->index);
+       fputs("\n",stdout);
+}
+
+static void show_context(void)
+{
+       int len;
+       vmlog_log_entry *logent;
+       vmlog_seq_t skipped;
+
+       if (g_context_available > 2*opt_context) {
+               skipped = g_context_available - 2*opt_context;
+               printf("@@ " VMLOG_SEQ_FMT " common entr%s skipped @@\n",skipped,(skipped > 1) ? "ies" : "y");
+               g_context_available = opt_context;
+       }
+       
+       len = opt_context;
+       if (len > g_context_available) {
+               len = g_context_available;
+       }
+
+       if (len <= 0)
+               return;
+
+       vmlog_ringbuf_seek(g_ringbuf_show[0],g_ringbuf_show[0]->seq - len);
+       while (len-- && (logent = vmlog_ringbuf_next(g_ringbuf_show[0],VMLOGDIFF_PREFETCH))) {
+               fputc(' ',stdout);
+               dump_log_entry(logent,0);
+               g_context_available--;
+       }
+}
+
+static void handle_unmatchable(int which,vmlogdiff_removed *rm)
+{
+       int i;
+       vmlog_log_entry *logent;
+       
+       LOG(("unmatchable, only in %d: ofs=%lld len=%lld\n",which,rm->start,rm->len));
+
+       assert(rm->len);
+
+       show_context();
+
+       vmlog_ringbuf_seek(g_ringbuf_orig[which],rm->start);
+       for (i=0; i<rm->len; ++i) {
+               logent = vmlog_ringbuf_next(g_ringbuf_orig[which],1 /* XXX */);
+               fputc((which) ? '+' : '-',stdout);
+               dump_log_entry(logent,0);
+       }
+
+       g_context_requested = opt_context;
+       g_context_available = 0;
+}
+
+static void handle_common(vmlog_seq_t xstart,vmlog_seq_t ystart,vmlog_seq_t len)
+{
+       int i;
+       int j;
+       vmlog_log_entry *logent;
+       
+       LOG(("common: x=%lld y=%lld len=%lld\n",xstart,ystart,len));
+
+       vmlog_ringbuf_seek(g_ringbuf_show[0],xstart);
+       for (i=0; i<len; ++i) {
+               for (j=0; j<2; ++j) {
+                       if (g_removedptr[j] < g_removedend[j] && g_showseq[j] == g_removedptr[j]->start) {
+                               handle_unmatchable(j,g_removedptr[j]);
+                               g_showseq[j] += g_removedptr[j]->len;
+                               g_removedptr[j]++;
+                       }
+               }
+               
+               logent = vmlog_ringbuf_next(g_ringbuf_show[0],VMLOGDIFF_PREFETCH);
+               if (g_context_requested) {
+                       fputc(' ',stdout);
+                       dump_log_entry(logent,0);
+                       g_context_requested--;
+                       assert(!g_context_available);
+               }
+               else {
+                       g_context_available++;
+               }
+
+               g_showseq[0]++;
+               g_showseq[1]++;
+       }
+}
+
+static void handle_only_in(int which,vmlog_seq_t start,vmlog_seq_t len)
+{
+       int i;
+       vmlog_log_entry *logent;
+       
+       LOG(("only in %d: ofs=%lld len=%lld\n",which,start,len));
+
+       assert(len);
+
+       show_context();
+
+       vmlog_ringbuf_seek(g_ringbuf_show[which],start);
+       for (i=0; i<len; ++i) {
+               if (g_removedptr[which] < g_removedend[which] && g_showseq[which] == g_removedptr[which]->start) {
+                       handle_unmatchable(which,g_removedptr[which]);
+                       g_showseq[which] += g_removedptr[which]->len;
+                       g_removedptr[which]++;
+               }
+               logent = vmlog_ringbuf_next(g_ringbuf_show[which],1 /* XXX */);
+               fputc((which) ? '+' : '-',stdout);
+               dump_log_entry(logent,0);
+
+               g_showseq[which]++;
+       }
+
+       g_context_requested = opt_context;
+       g_context_available = 0;
+}
+
+static vmlog_seq_t match_forward(vmlog_seq_t x,vmlog_seq_t y,vmlog_seq_t xend,vmlog_seq_t yend)
+{
+       vmlog_log_entry *enta;
+       vmlog_log_entry *entb;
+       vmlog_seq_t xorigin;
+
+       if (x >= xend || y >= yend)
+               return 0;
+
+       xorigin = x;
+       vmlog_ringbuf_seek(g_ringbuf[0],x);
+       vmlog_ringbuf_seek(g_ringbuf[1],y);
+
+       do {
+               enta = vmlog_ringbuf_next(g_ringbuf[0],VMLOGDIFF_PREFETCH);
+               entb = vmlog_ringbuf_next(g_ringbuf[1],VMLOGDIFF_PREFETCH);
+               assert(enta && entb);
+
+               if (enta->index != entb->index || enta->tag != entb->tag)
+                       break;
+
+               /* a diagonal edge in the edit graph */
+               x++;
+               y++;
+       } while (x < xend && y < yend);
+
+       return x - xorigin;
+}
+
+static vmlog_seq_t match_backward(vmlog_seq_t xstart,vmlog_seq_t ystart,vmlog_seq_t x,vmlog_seq_t y)
+{
+       vmlog_log_entry *enta;
+       vmlog_log_entry *entb;
+       vmlog_seq_t xorigin;
+
+       if (x <= xstart || y <= ystart)
+               return 0;
+
+       xorigin = x;
+       vmlog_ringbuf_seek(g_ringbuf_back[0],x);
+       vmlog_ringbuf_seek(g_ringbuf_back[1],y);
+
+       do {
+               enta = vmlog_ringbuf_prev(g_ringbuf_back[0],VMLOGDIFF_PREFETCH);
+               entb = vmlog_ringbuf_prev(g_ringbuf_back[1],VMLOGDIFF_PREFETCH);
+               assert(enta && entb);
+
+               if (enta->index != entb->index || enta->tag != entb->tag)
+                       break;
+
+               /* a diagonal edge in the edit graph */
+               x--;
+               y--;
+       } while (x > xstart && y > ystart);
+
+       return xorigin - x;
+}
+
+/* pre-condition: X and Y differ at both ends */
+
+static void find_middle_snake(vmlog_seq_t xstart,vmlog_seq_t ystart,vmlog_seq_t xend,vmlog_seq_t yend)
+{
+       int D;
+       int k;
+       int resultD;
+       vmlog_seq_t diagofs;
+       vmlog_seq_t rdiagofs;
+       vmlog_seq_t Delta;
+       vmlog_seq_t x,y;
+       vmlog_seq_t snakex,snakey;
+       vmlog_seq_t snakestartx = 0;
+       vmlog_seq_t snakestarty = 0;
+       vmlog_seq_t snakelen = 0;
+       vmlog_seq_t match;
+       vmlog_seq_t best_forward;
+       vmlog_seq_t best_backward;
+
+       LOG(("find_middle_snake(%lld,%lld,%lld,%lld)\n",
+                       xstart,ystart,xend,yend));
+
+       diagofs = ystart - xstart;
+       rdiagofs = yend - xend;
+
+       Delta = diagofs - rdiagofs;
+       
+       /* fake vertical edge (0,-1) -> (0,0) ending on 0-diagonal */
+       g_Vforw[1] = xstart;
+
+       /* fake vertical edge (N,M+1) -> (N,M) ending on 0-reverse-diagonal */
+       g_Vback[-1] = xend;
+
+       best_forward = 0;
+       best_backward = xend;
+       
+       for (D=0; D <= g_maxD; ++D) {
+               LOG(("D = %d\n",D));
+
+               if (opt_verbose && D && (D % 1000 == 0))
+                       fprintf(stderr,"%d. (" VMLOG_SEQ_FMT_W ":" VMLOG_SEQ_FMT_W " entries, " VMLOG_SEQ_FMT_W " left) D = %d\n",
+                                       g_level,xend-xstart,yend-ystart,best_backward - best_forward,D);
+
+               for (k = -D; k <= D; k += 2) {
+                       LOG(("    k = %d, forward\n",k));
+
+                       /* we take a (D-1)-path that ends on a          */
+                       /* neighbouring diagonal and extend it with a   */
+                       /* vertical or horizontal edge (whichever takes */
+                       /* us further)                                  */
+
+                       if (k == -D || (k != +D && g_Vforw[k-1] < g_Vforw[k+1])) {
+                               /* vertical edge from diagonal k+1 down to k */
+                               x = g_Vforw[k+1];
+                       }
+                       else {
+                               /* horizontal edge from diagonal k-1 right to k */
+                               x = g_Vforw[k-1] + 1;
+                       }
+
+                       /* calculate y using the k-diagonal equation */
+                       
+                       y = x - k + diagofs;
+
+                       /* append the trailing snake to the D-path */
+
+                       snakex = x;
+                       snakey = y;
+
+                       match = match_forward(x,y,xend,yend);
+                       x += match;
+                       y += match;
+
+                       if (match) {
+                               LOG(("match from (%lld,%lld) to (%lld,%lld)\n",
+                                               snakex,snakey,x-1,y-1));
+                       }
+
+                       if (D > 4000) {
+                               resultD = 2*D - 1;
+                               snakestartx = (xstart + xend) / 2;;
+                               snakestarty = snakestartx;
+                               if (snakestarty < ystart)
+                                       snakestarty = ystart;
+                               else if (snakestarty > yend)
+                                       snakestarty = yend;
+                               snakelen = 0;
+                               goto found_middle_snake;
+                       }
+
+                       /* this is the furthest reaching D-path on the k-diagonal */
+
+                       g_Vforw[k] = x;
+                       if (x > best_forward)
+                               best_forward = x;
+
+                       LOG(("\tlongest %d-path on %d-diagonal ends at x=%lld\n",
+                                       D,k,x));
+
+                       /* check overlap with furthest reaching reverse D-1 path */
+
+                       if ((Delta & 1) && k - Delta >= -(D-1) && k - Delta <= +(D-1)) {
+                               if (x >= g_Vback[k - Delta]) {
+                                       LOG(("length of SES is %d\n",2*D - 1));
+
+                                       resultD = 2*D - 1;
+                                       snakestartx = snakex;
+                                       snakestarty = snakey;
+                                       snakelen = x - snakex;
+                                       goto found_middle_snake;
+                               }
+                       }
+               }
+
+               for (k = -D; k <= +D; k += 2) {
+
+                       LOG(("    k = %d, backward\n",k));
+
+                       /* we take a reverse (D-1)-path that ends on a  */
+                       /* neighbouring diagonal and extend it with a   */
+                       /* vertical or horizontal edge (whichever takes */
+                       /* us further)                                  */
+
+                       if (k == +D || (k != -D && g_Vback[k-1] < g_Vback[k+1])) {
+                               /* vertical edge from reverse diagonal k-1 up to k */
+                               x = g_Vback[k-1];
+                       }
+                       else {
+                               /* horizontal edge from reverse diagonal k+1 left to k */
+                               x = g_Vback[k+1] - 1;
+                       }
+
+                       /* calculate y using the k-reverse-diagonal equation */
+                       
+                       y = x - k + rdiagofs;
+
+                       /* append the trailing snake to the D-path */
+
+                       snakex = x;
+                       snakey = y;
+                       match = match_backward(xstart,ystart,x,y);
+                       x -= match;
+                       y -= match;
+
+                       if (match)
+                               LOG(("match from (%lld,%lld) to (%lld,%lld)\n",
+                                               x,y,snakex-1,snakey-1));
+
+                       /* this is the furthest reaching reverse D-path on the k-diagonal */
+
+                       g_Vback[k] = x;
+                       if (x < best_backward)
+                               best_backward = x;
+
+                       LOG(("\tlongest reverse %d-path on %d-diagonal ends at x=%lld\n",
+                                       D,k,x));
+
+                       /* check overlap with forward reaching D path */
+
+                       if (!(Delta & 1) && k + Delta >= -D && k + Delta <= +D) {
+                               if (x <= g_Vforw[k + Delta]) {
+                                       LOG(("length of SES is %d\n",2*D));
+
+                                       resultD = 2*D;
+                                       snakestartx = x;
+                                       snakestarty = y;
+                                       snakelen = snakex - x;
+                                       goto found_middle_snake;
+                               }
+                       }
+               }
+       }
+
+       vmlog_die("length of shortest editing script is > %d\n",g_maxD);
+
+found_middle_snake:
+       if (opt_verbose && g_level == 0) {
+               fprintf(stderr,"shortest editing script D = %d\n",resultD);
+       }
+       
+       if (resultD > 1) {
+               g_level++;
+               compare(xstart,ystart,snakestartx,snakestarty);
+       
+               LOG(("middle snake: x=%lld y=%lld len=%lld\n",
+                               snakestartx,snakestarty,snakelen));
+
+               if (snakelen) {
+                       handle_common(snakestartx,snakestarty,snakelen);
+               }
+
+               compare(snakestartx + snakelen,snakestarty + snakelen,xend,yend);
+               g_level--;
+       }
+       else {
+               vmlog_seq_t lendiff = (yend - ystart) - (xend - xstart);
+               
+               LOG(("snakestartx=%lld snakestarty=%lld snakelen=%lld\n",
+                               snakestartx,snakestarty,snakelen));
+               assert(snakelen == 0);
+               assert(lendiff);
+
+               if (lendiff > 0) {
+                       handle_only_in(1,ystart,lendiff);
+               }
+               else {
+                       handle_only_in(0,xstart,-lendiff);
+               }
+       }
+}
+
+static void compare(vmlog_seq_t xstart,vmlog_seq_t ystart,vmlog_seq_t xend,vmlog_seq_t yend)
+{
+       vmlog_seq_t prefix;
+       vmlog_seq_t suffix;
+       vmlog_seq_t xdiffend;
+       vmlog_seq_t ydiffend;
+
+       LOG(("compare(%lld,%lld,%lld,%lld)\n",
+                       xstart,ystart,xend,yend));
+
+       /* process and strip common prefix */
+
+       prefix = match_forward(xstart,ystart,xend,yend);
+       if (opt_verbose && g_level == 0) {
+               fprintf(stderr,"common prefix = " VMLOG_SEQ_FMT "\n",prefix);
+       }
+       if (prefix) {
+               handle_common(xstart,ystart,prefix);
+               xstart += prefix;
+               ystart += prefix;
+       }
+
+       /* strip common suffix */
+
+       suffix = match_backward(xstart,ystart,xend,yend);
+       if (opt_verbose && g_level == 0) {
+               fprintf(stderr,"common suffix = " VMLOG_SEQ_FMT "\n",suffix);
+       }
+       xdiffend = xend - suffix;
+       ydiffend = yend - suffix;
+       
+
+       /* handle differences */
+
+       if (xstart < xdiffend || ystart < ydiffend) {
+               if (xstart == xdiffend) {
+                       handle_only_in(1,ystart,ydiffend - ystart);
+               }
+               else if (ystart == ydiffend) {
+                       handle_only_in(0,xstart,xdiffend - xstart);
+               }
+               else {
+                       find_middle_snake(xstart,ystart,xdiffend,ydiffend);
+               }
+       }
+
+       /* process common suffix */
+
+       if (suffix) {
+               handle_common(xdiffend,ydiffend,suffix);
+       }
+}
+
+static void unmatchable_entries(void)
+{
+       vmlog_log_entry *logent;
+       int i;
+       int mark;
+       vmlog_file cleaned;
+       vmlog_file seqs;
+       vmlog_seq_t count;
+       vmlog_seq_t seq;
+       vmlogdiff_removed removed;
+       
+       VMLOG_XZNEW_ARRAY(g_index_used,char,g_nstrings);
+
+       for (i=0; i<2; ++i) {
+               mark = 1<<i;
+               
+               vmlog_ringbuf_seek(g_ringbuf[i],0);
+               while ((logent = vmlog_ringbuf_next(g_ringbuf[i],VMLOGDIFF_PREFETCH))) {
+                       g_index_used[logent->index] |= mark;
+               }
+       }
+
+       for (i=0; i<2; ++i) {
+               vmlog_file_open(&cleaned,g_cleanedfname[i],vmlogTruncateAppend);
+               vmlog_file_open(&seqs,g_removedfname[i],vmlogTruncateAppend);
+               
+               vmlog_ringbuf_seek(g_ringbuf[i],0);
+               count = 0;
+               seq = 0;
+               removed.start = 0;
+               removed.len = 0;
+               while ((logent = vmlog_ringbuf_next(g_ringbuf[i],VMLOGDIFF_PREFETCH))) {
+                       if (g_index_used[logent->index] == 3) {
+                               vmlog_file_append(&cleaned,logent,sizeof(vmlog_log_entry));
+                               if (removed.len)
+                                       vmlog_file_append(&seqs,&removed,sizeof(vmlogdiff_removed));
+                               removed.len = 0;
+                               removed.start = seq + 1;
+                       }
+                       else {
+                               count++;
+                               removed.len++;
+                       }
+                       seq++;
+               }
+               if (removed.len)
+                       vmlog_file_append(&seqs,&removed,sizeof(vmlogdiff_removed));
+
+               vmlog_file_close(&seqs);
+               vmlog_file_close(&cleaned);
+
+               if (opt_verbose)
+                       fprintf(stderr,"removed " VMLOG_SEQ_FMT " unmatchable entries from %s\n",
+                                       count,g_ringbuf[i]->file.fname);
+
+               g_removedmap[i] = vmlog_file_mmap(g_removedfname[i],&(g_removedlen[i]));
+               g_nremoved[i] = g_removedlen[i] / sizeof(vmlogdiff_removed);
+               g_removedptr[i] = g_removedmap[i];
+               g_removedend[i] = g_removedmap[i] + g_nremoved[i];
+       }
+}
+
+static void diff_logs(void)
+{
+       int i;
+
+       for (i=0; i<2; ++i) {
+               g_ringbuf[i] = vmlog_ringbuf_new(opt_fname[i],VMLOGDIFF_RINGBUF_SIZE);
+       }
+
+       unmatchable_entries();
+       diff_alloc(200000);
+
+       for (i=0; i<2; ++i) {
+               g_ringbuf_orig[i] = g_ringbuf[i];
+               g_ringbuf[i] = vmlog_ringbuf_new(g_cleanedfname[i],VMLOGDIFF_RINGBUF_SIZE);
+               g_ringbuf_back[i] = vmlog_ringbuf_new(g_cleanedfname[i],VMLOGDIFF_RINGBUF_SIZE);
+               g_ringbuf_show[i] = vmlog_ringbuf_new(g_cleanedfname[i],VMLOGDIFF_RINGBUF_SIZE);
+       }
+
+       g_n[0] = g_ringbuf[0]->file.size / sizeof(vmlog_log_entry);
+       g_n[1] = g_ringbuf[1]->file.size / sizeof(vmlog_log_entry);
+
+       compare(0,0,g_n[0],g_n[1]);
+}
+
+int main(int argc,char **argv)
+{
+       if (argc)
+               vmlog_set_progname(argv[0]);
+       parse_command_line(argc,argv);
+
+       g_idxfname = vmlog_concat3(opt_prefix,"",".idx",NULL);
+       g_strfname = vmlog_concat3(opt_prefix,"",".str",NULL);
+
+       g_idxmap = (vmlog_string_entry *) vmlog_file_mmap(g_idxfname,&g_idxlen);
+       g_nstrings = g_idxlen / sizeof(vmlog_string_entry);
+       g_strmap = (char *) vmlog_file_mmap(g_strfname,&g_strlen);
+
+       diff_logs();
+       
+       vmlog_ringbuf_free(g_ringbuf[0]);
+       vmlog_ringbuf_free(g_ringbuf[1]);
+       g_ringbuf[0] = NULL;
+       g_ringbuf[1] = NULL;
+       vmlog_file_munmap(g_strmap,g_strlen);
+       vmlog_file_munmap(g_idxmap,g_idxlen);
+       vmlog_file_munmap(g_removedmap[0],g_removedlen[0]);
+       vmlog_file_munmap(g_removedmap[1],g_removedlen[1]);
+
+       return 0;
+}
+
+/* vim: noet ts=8 sw=8
+ */
+
diff --git a/contrib/vmlog/vmlogdump.c b/contrib/vmlog/vmlogdump.c
new file mode 100644 (file)
index 0000000..6f18917
--- /dev/null
@@ -0,0 +1,512 @@
+/* vmlog - high-speed logging for free VMs                  */
+/* Copyright (C) 2006 Edwin Steiner <edwin.steiner@gmx.net> */
+
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "vmlog.h"
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#define VMLOGDUMP_RINGBUF_SIZE 1024*16
+#define VMLOGDUMP_PREFETCH 1024
+
+/*** options *********************************************************/
+
+char *opt_prefix;
+int opt_threadidx = 0;
+int opt_dumpstrings = 0;
+int opt_countcalls = 0;
+int opt_show_zero_counts = 0;
+int opt_open_frames = 0;
+int opt_prefix_open_frames = 0;
+int opt_show_depth = 1;
+int opt_show_seqns = 0;
+int opt_indent = 4;
+int opt_reldepth = 0;
+int opt_strip_desc = 0;
+int opt_maxdepth = INT_MAX;
+
+char *opt_tag = NULL;
+char *opt_string = NULL;
+vmlog_log_entry opt_needle;
+
+vmlog_seq_t opt_range_start = 0;
+vmlog_seq_t opt_range_end = VMLOG_SEQ_MAX;
+
+/*** global variables ************************************************/
+
+char *g_idxfname;
+char *g_strfname;
+char *g_logfname;
+int g_logfd;
+int g_idxlen;
+int g_strlen;
+int g_nstrings;
+char *g_indentation;
+
+vmlog_string_entry *g_idxmap;
+char *g_strmap;
+
+vmlog_ringbuf *g_ringbuf = NULL;
+
+int *g_counters = NULL;
+
+/*** usage ***********************************************************/
+
+const char *usage =
+"Usage: vmlogdump [options] prefix\n"
+"\n"
+"    Options:\n"
+"        -c      count call frequency of methods\n"
+"        -D      don't show depth numbers\n"
+"           -z   show methods with zero call count\n"
+"        -gT:S   select entries with given Tag and String\n"
+"        -h      display this help message\n"
+"        -iNUM   indentation width\n"
+"        -lNUM   limit max depth to NUM\n"
+"        -n      show sequence numbers / string indices\n"
+"        -o      show open frames\n"
+"        -O      show open frames before dump\n"
+"        -P      strip method parameter and return types\n"
+"        -rN:M   limit output to a range of sequence numbers\n"
+"        -R      show depth relative to minimum depth in range\n"
+"        -s      dump strings\n"
+"        -tNUM   use log of thread NUM (default=0)\n"
+"\n";
+
+/*********************************************************************/
+
+static void parse_tag_and_string(const char *arg)
+{
+       const char *sep;
+       int len;
+
+       sep = strchr(arg,':');
+       if (!sep)
+               vmlog_die_usage(usage,1);
+
+       len = sep - arg;
+       VMLOG_XZNEW_ARRAY(opt_tag,char,len+1);
+       memcpy(opt_tag,arg,len);
+
+       opt_needle.tag = vmlog_tag_from_name(opt_tag,len);
+       if (opt_needle.tag == -1) {
+               vmlog_die("unknown tag identifier: %s",opt_tag);
+       }
+       
+       len = strlen(arg) - len - 1;
+       VMLOG_XZNEW_ARRAY(opt_string,char,len+1);
+       memcpy(opt_string,sep+1,len);
+}
+
+void parse_command_line(int argc,char **argv)
+{
+       int r;
+
+       while (1) {
+               r = getopt(argc,argv,"cDg:hi:l:noOPr:Rst:z");
+               if (r == -1)
+                       break;
+               switch (r) {
+                       case 'c':
+                               opt_countcalls = 1;
+                               break;
+
+                       case 'D':
+                               opt_show_depth = 0;
+                               break;
+
+                       case 'g':
+                               parse_tag_and_string(optarg);
+                               break;
+
+                       case 'h':
+                               vmlog_die_usage(usage,0);
+
+                       case 'i':
+                               opt_indent = atoi(optarg);
+                               if (opt_indent < 0)
+                                       vmlog_die_usage(usage,1);
+                               break;
+
+                       case 'l':
+                               opt_maxdepth = atoi(optarg);
+                               if (opt_maxdepth < 0)
+                                       vmlog_die_usage(usage,1);
+                               break;
+
+                       case 'n':
+                               opt_show_seqns = 1;
+                               break;
+
+                       case 'o':
+                               opt_open_frames = 1;
+                               break;
+
+                       case 'O':
+                               opt_prefix_open_frames = 1;
+                               break;
+
+                       case 'P':
+                               opt_strip_desc = 1;
+                               break;
+
+                       case 'r':
+                               if (!vmlog_opt_parse_range(optarg,&opt_range_start,&opt_range_end))
+                                       vmlog_die("invalid range: %s",optarg);
+                               break;
+
+                       case 'R':
+                               opt_reldepth = 1;
+                               break;
+                               
+                       case 's':
+                               opt_dumpstrings = 1;
+                               break;
+                               
+                       case 't':
+                               opt_threadidx = atoi(optarg);
+                               break;
+
+                       case 'z':
+                               opt_show_zero_counts = 1;
+                               break;
+                               
+                       case '?': vmlog_die_usage(usage,1);
+               }
+       }
+
+       if (argc - optind < 1)
+               vmlog_die_usage(usage,1);
+       
+       opt_prefix = argv[optind++];
+
+       g_indentation = VMLOG_NEW_ARRAY(char,opt_indent+1);
+       memset(g_indentation,' ',opt_indent);
+       g_indentation[opt_indent] = 0;
+}
+
+static void fprint_string(FILE *file,int index)
+{
+       char *str;
+       vmlog_string_entry *strent;
+       char *buf;
+       int len;
+
+       strent = g_idxmap + index;
+       str = g_strmap + strent->ofs;
+       len = strent->len;
+
+       if (opt_strip_desc) {
+               char *desc;
+
+               desc = (char*) memchr(str,'(',len);
+               if (desc)
+                       len = desc - str;
+       }
+
+       buf = VMLOG_NEW_ARRAY(char,len+1);
+
+       memcpy(buf,str,len);
+       buf[len] = 0;
+
+       fputs(buf,file);
+       
+       VMLOG_FREE_ARRAY(char,len+1,buf);
+}
+
+static void dump_log_entry(vmlog_log_entry *logent,int depth,vmlog_seq_t seq)
+{
+       int i;
+
+       if (opt_show_depth) {
+               if (opt_reldepth)
+                       fputc('R',stdout);
+               fprintf(stdout,"%d",depth);
+               fputc(':',stdout);
+               if (depth < 100) {
+                       fputc(' ',stdout);
+                       if (depth < 10)
+                               fputc(' ',stdout);
+               }
+       }
+       if (opt_show_seqns) {
+               fprintf(stdout,"(" VMLOG_SEQ_FMT_W ")",seq);
+       }
+       for (i=0; i<depth; ++i)
+               fputs(g_indentation,stdout);
+       switch(logent->tag) {
+               case VMLOG_TAG_ENTER: fputs("enter ",stdout); break;
+               case VMLOG_TAG_LEAVE: fputs("leave ",stdout); break;
+               case VMLOG_TAG_THROW: fputs("throw ",stdout); break;
+               case VMLOG_TAG_CATCH: fputs("catch ",stdout); break;
+               case VMLOG_TAG_UNWND: fputs("unwnd ",stdout); break;
+               case VMLOG_TAG_SIGNL: fputs("signl ",stdout); break;
+               case VMLOG_TAG_UNROL: fputs("unrol ",stdout); break;
+               case VMLOG_TAG_REROL: fputs("rerol ",stdout); break;
+               default: fputs("<UNKNOWN TAG> ",stdout);
+       }
+       fprint_string(stdout,logent->index);
+       fputs("\n",stdout);
+}
+
+static void dump_log_entries(void)
+{
+       vmlog_log_entry *logent;
+       int depth = 0;
+       int mindepth = INT_MAX;
+       vmlog_seq_t seq;
+
+       if (opt_reldepth) {
+               vmlog_ringbuf_seek(g_ringbuf,opt_range_start);
+               while ((g_ringbuf->seq <= opt_range_end)
+                       && (logent = vmlog_ringbuf_next(g_ringbuf,VMLOGDUMP_PREFETCH)))
+               {
+                       int depthchange = vmlog_tag_definitions[logent->tag].depth;
+
+                       depth += depthchange;
+                       if (depthchange < 0) {
+                               if (depth < mindepth)
+                                       mindepth = depth;
+                       }
+               }
+
+               vmlog_ringbuf_seek(g_ringbuf,opt_range_start);
+               if (mindepth != INT_MAX)
+                       depth = -mindepth;
+               else
+                       depth = 0;
+       }
+
+       while ((g_ringbuf->seq <= opt_range_end)
+               && (logent = vmlog_ringbuf_next(g_ringbuf,VMLOGDUMP_PREFETCH)))
+       {
+               int depthchange = vmlog_tag_definitions[logent->tag].depth;
+
+               if (depthchange < 0) {
+                       depth += depthchange;
+                       if (depth < mindepth)
+                               mindepth = depth;
+               }
+
+               seq = g_ringbuf->seq - 1;
+
+               if ((!opt_string || (logent->tag == opt_needle.tag && logent->index == opt_needle.index))
+                       && (seq >= opt_range_start)
+                       && (depth <= opt_maxdepth))
+               {
+                       dump_log_entry(logent,depth,seq);
+               }
+
+               if (depthchange > 0)
+                       depth += depthchange;
+       }
+}
+
+static void dump_strings(void)
+{
+       int i;
+
+       for (i=0; i<g_nstrings; ++i) {
+               if (opt_show_seqns) {
+                       fprintf(stdout,"%8d: ",i);
+               }
+               fprint_string(stdout,i);
+               fputc('\n',stdout);
+       }
+}
+
+static int compare_by_counter(const void *a,const void *b)
+{
+       return g_counters[*(int*)b] - g_counters[*(int*)a];
+}
+
+static void count_calls(void)
+{
+       int *indices;
+       int i;
+       int index;
+       vmlog_log_entry *logent;
+
+       g_counters = VMLOG_NEW_ARRAY(int,g_nstrings);
+       assert(g_counters);
+       memset(g_counters,0,sizeof(int)*g_nstrings);
+
+       while (1) {
+               logent = vmlog_ringbuf_next(g_ringbuf,VMLOGDUMP_PREFETCH);
+               if (!logent)
+                       break;
+
+               if (logent->tag == VMLOG_TAG_ENTER) {
+                       g_counters[logent->index]++;
+               }
+       }
+
+       indices = VMLOG_NEW_ARRAY(int,g_nstrings);
+       assert(indices);
+       for (i=0; i<g_nstrings; ++i)
+               indices[i] = i;
+
+       qsort(indices,g_nstrings,sizeof(int),compare_by_counter);
+
+       for (i=0; i<g_nstrings; ++i) {
+               index = indices[i];
+               if (!g_counters[index] && !opt_show_zero_counts)
+                       break;
+               fprintf(stdout,"%12d ",g_counters[index]);
+               fprint_string(stdout,index);
+               fputc('\n',stdout);
+       }
+       
+       VMLOG_FREE_ARRAY(int,g_nstrings,indices);
+       VMLOG_FREE_ARRAY(int,g_nstrings,g_counters);
+}
+
+static void dump_open_frames(void)
+{
+       vmlog_thread_log *tlog;
+       vmlog_log_entry *logent;
+       vmlog_frame *frame;
+       vmlog_seq_t start;
+       vmlog_seq_t end;
+       int i;
+       int maxdepth = 0;
+
+       tlog = vmlog_thread_log_new(NULL,NULL,0);
+
+       if (opt_prefix_open_frames) {
+               start = 0;
+               end = opt_range_start - 1;
+       }
+       else {
+               start = opt_range_start;
+               end = opt_range_end;
+       }
+
+       if (start > 0)
+               vmlog_ringbuf_seek(g_ringbuf,start);
+
+       while (g_ringbuf->seq <= end 
+               && (logent = vmlog_ringbuf_next(g_ringbuf,VMLOGDUMP_PREFETCH)))
+       {
+               int depthchange = vmlog_tag_definitions[logent->tag].depth;
+               if (depthchange < 0) {
+                       if (!vmlog_thread_log_leave(tlog,logent->index,g_ringbuf->seq - 1))
+                               vmlog_warn("unmatched leave");
+               }
+               else if (depthchange > 0) {
+                       vmlog_thread_log_enter(tlog,logent->index,g_ringbuf->seq - 1);
+                       if (tlog->depth > maxdepth)
+                               maxdepth = tlog->depth;
+               }
+       }
+
+       if (tlog->depth < 0) {
+               vmlog_warn("thread ends with negative call frame depth: %i",tlog->depth);
+               vmlog_warn("frame buffer will be dumped, expect bogus results:");
+               tlog->depth = tlog->framescap;
+               if (tlog->depth > maxdepth)
+                       tlog->depth = maxdepth;
+       }
+
+       for (i=0; i<tlog->depth; ++i) {
+               frame = tlog->frames + i;
+               fprintf(stdout,"%3d: (" VMLOG_SEQ_FMT_W ") ",i,frame->seq);
+               fprint_string(stdout,frame->index);
+               fputc('\n',stdout);
+       }
+
+       vmlog_thread_log_free(tlog);
+}
+
+int main(int argc,char **argv)
+{
+       char buf[20];
+       int need_logfile = 1;
+       int need_vml = 0;
+       vmlog_log *vml;
+       
+       if (argc)
+               vmlog_set_progname(argv[0]);
+       parse_command_line(argc,argv);
+
+       sprintf(buf,"%d",opt_threadidx);
+       g_idxfname = vmlog_concat3(opt_prefix,"",".idx",NULL);
+       g_strfname = vmlog_concat3(opt_prefix,"",".str",NULL);
+       g_logfname = vmlog_concat4len(opt_prefix,strlen(opt_prefix),
+                       ".",1,buf,strlen(buf),".log",4,NULL);
+
+       if (opt_dumpstrings)
+               need_logfile = 0;
+
+       if (opt_string)
+               need_vml = 1;
+
+       if (need_logfile) {
+               g_ringbuf = vmlog_ringbuf_new(g_logfname,VMLOGDUMP_RINGBUF_SIZE);
+       }
+
+       g_idxmap = (vmlog_string_entry *) vmlog_file_mmap(g_idxfname,&g_idxlen);
+       g_nstrings = g_idxlen / sizeof(vmlog_string_entry);
+       g_strmap = (char *) vmlog_file_mmap(g_strfname,&g_strlen);
+
+       if (need_vml) {
+               vml = vmlog_log_new(NULL,0);
+               vmlog_load_stringhash(vml,opt_prefix);
+               if (opt_string) {
+                       opt_needle.index = vmlog_get_string_index(vml,opt_string,strlen(opt_string));
+                       fprintf(stdout,"needle '%s:%s' tag=%d index=%d\n",
+                                       opt_tag,opt_string,opt_needle.tag,opt_needle.index);
+               }
+       }
+       
+       if (opt_countcalls) {
+               count_calls();
+       }
+       else if (opt_dumpstrings) {
+               dump_strings();
+       }
+       else if (opt_open_frames) {
+               dump_open_frames();
+       }
+       else {
+               if (opt_prefix_open_frames) {
+                       dump_open_frames();
+                       fputs("---\n",stdout);
+               }
+               dump_log_entries();
+       }
+
+       vmlog_ringbuf_free(g_ringbuf);
+       g_ringbuf = NULL;
+       munmap(g_strmap,g_strlen);
+       munmap(g_idxmap,g_idxlen);
+
+       return 0;
+}
+
+/* vim: noet ts=8 sw=8
+ */
+
diff --git a/contrib/vmlog/vmlogindex.c b/contrib/vmlog/vmlogindex.c
new file mode 100644 (file)
index 0000000..b48daa1
--- /dev/null
@@ -0,0 +1,121 @@
+/* vmlog - high-speed logging for free VMs                  */
+/* Copyright (C) 2006 Edwin Steiner <edwin.steiner@gmx.net> */
+
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "vmlog.h"
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+char *opt_listfname;
+char *opt_prefix;
+
+char *g_idxfname;
+char *g_strfname;
+
+const char *usage =
+"Usage: vmlogindex prefix listfile\n";
+
+#define VMLOG_MAXLINELENGTH  1024*16
+
+void parse_command_line(int argc,char **argv)
+{
+       int r;
+
+       while (1) {
+               r = getopt(argc,argv,"");
+               if (r == -1)
+                       break;
+               switch (r) {
+                       case '?': vmlog_die("invalid option");
+               }
+       }
+
+       if (argc - optind < 2)
+               vmlog_die_usage(usage,1);
+
+       opt_prefix = argv[optind++];
+       opt_listfname = argv[optind++];
+}
+
+int main(int argc,char **argv)
+{
+       int i;
+       int len;
+       vmlog_log *vml;
+       char *buf;
+       FILE *listfile;
+       char *p;
+       int warnws;
+       
+       if (argc)
+               vmlog_set_progname(argv[0]);
+       parse_command_line(argc,argv);
+
+       listfile = fopen(opt_listfname,"r");
+       if (!listfile)
+               vmlog_die("could not open file: %s: %s",opt_listfname,strerror(errno));
+       
+       vml = vmlog_log_new(opt_prefix,1);
+
+       buf = VMLOG_NEW_ARRAY(char,VMLOG_MAXLINELENGTH+1);
+       while (!feof(listfile)) {
+               p = fgets(buf,VMLOG_MAXLINELENGTH,listfile);
+               if (!p) {
+                       if (ferror(listfile))
+                               vmlog_die("reading from file: %s: %s",opt_listfname,strerror(errno));
+                       assert(feof(listfile));
+                       break;
+               }
+
+               len = strlen(p);
+               if (p[len-1] != '\n') {
+                       vmlog_warn("line without newline or overlong line read");
+               }
+               else {
+                       /* chop of newline */
+                       p[--len] = 0;
+               }
+               warnws = 0;
+               while (isspace(p[len-1])) {
+                       p[--len] = 0;
+                       warnws = 1;
+               }
+               if (warnws) {
+                       vmlog_warn("removed trailing whitespace from line");
+               }
+               i = vmlog_get_string_index(vml,buf,len);
+       }
+
+       vmlog_log_free(vml);
+       fclose(listfile);
+
+       return 0;
+}
+
+/* vim: noet ts=8 sw=8
+ */
+