Question about simple profiling implementation

Mike McCormack mike at codeweavers.com
Tue Dec 2 16:56:39 CST 2003


I've been maintaining Charles's wine profiler patch in my Wine tree, but 
haven't tested it for a while.  Here's roughly what I have at the 
moment... I'm in a bit of a rush right now, so it may be incomplete and 
I'll check it again tonight.

Mike


Alexandre Julliard wrote:

>>What do you think?
> 
> 
> I'd suggest you look into Charles' profiler support that was posted
> some time ago on wine-patches (and that I'll get around to merge
> someday, promised <g>).
> 
-------------- next part --------------
Index: configure.ac
===================================================================
RCS file: /home/wine/wine/configure.ac,v
retrieving revision 1.214
diff -u -r1.214 configure.ac
--- configure.ac	2 Dec 2003 04:11:09 -0000	1.214
+++ configure.ac	2 Dec 2003 22:42:12 -0000
@@ -19,6 +19,18 @@
 AC_ARG_WITH(opengl,    AC_HELP_STRING([--without-opengl],[do not use OpenGL]))
 AC_ARG_WITH(curses,    AC_HELP_STRING([--without-curses],[do not use curses]))
 AC_ARG_WITH(wine-tools,AC_HELP_STRING([--with-wine-tools=<dir>],[use Wine tools from directory <dir>]))
+AC_ARG_WITH(profiler,AC_HELP_STRING([--with-profiler],[build the profiler]))
+
+if test "x$with_profiler" = "xyes"
+then
+  PROFILE_CFLAGS="-finstrument-functions  -D__NO_STRING_INLINES"
+  PROFILE_LIBS="-L\$(TOPOBJDIR)/libs/wprof -lwineprof -lm"
+  PROFILE_LIBNAME="libwineprof"
+  AC_DEFINE(WINE_NO_PROFILE,__attribute__((no_instrument_function)))
+fi
+AC_SUBST(PROFILE_CFLAGS)
+AC_SUBST(PROFILE_LIBS)
+AC_SUBST(PROFILE_LIBNAME)
 
 AC_SUBST(WIN16_FILES,"\$(WIN16_FILES)")
 AC_SUBST(WIN16_INSTALL,"\$(WIN16_INSTALL)")
@@ -1632,6 +1645,7 @@
 libs/uuid/Makefile
 libs/wine/Makefile
 libs/wpp/Makefile
+libs/wprof/Makefile
 loader/Makefile
 programs/Makefile
 programs/avitools/Makefile
@@ -1667,6 +1681,7 @@
 tools/winebuild/Makefile
 tools/winedump/Makefile
 tools/winegcc/Makefile
+tools/wineprof/Makefile
 tools/wmc/Makefile
 tools/wrc/Makefile])
 
Index: Make.rules.in
===================================================================
RCS file: /home/wine/wine/Make.rules.in,v
retrieving revision 1.163
diff -u -r1.163 Make.rules.in
--- Make.rules.in	11 Oct 2003 01:05:18 -0000	1.163
+++ Make.rules.in	2 Dec 2003 22:42:12 -0000
@@ -55,7 +55,8 @@
 LINTFLAGS = @LINTFLAGS@
 INCLUDES     = -I$(SRCDIR) -I. -I$(TOPSRCDIR)/include -I$(TOPOBJDIR)/include $(EXTRAINCL)
 EXTRACFLAGS  = @EXTRACFLAGS@
-ALLCFLAGS    = $(INCLUDES) $(DEFS) $(DLLFLAGS) $(EXTRACFLAGS) $(CPPFLAGS) $(CFLAGS)
+CFLAGSNOPROF = $(INCLUDES) $(DEFS) $(DLLFLAGS) $(EXTRACFLAGS) $(CPPFLAGS) $(CFLAGS)
+ALLCFLAGS    = $(CFLAGSNOPROF) $(PROFILE_CFLAGS)
 ALLLINTFLAGS = $(INCLUDES) $(DEFS) $(LINTFLAGS)
 IDLFLAGS     = $(INCLUDES) $(DEFS) $(EXTRAIDLFLAGS)
 MKINSTALLDIRS= $(TOPSRCDIR)/tools/mkinstalldirs -m 755
Index: include/config.h.in
===================================================================
RCS file: /home/wine/wine/include/config.h.in,v
retrieving revision 1.174
diff -u -r1.174 config.h.in
--- include/config.h.in	25 Nov 2003 03:31:26 -0000	1.174
+++ include/config.h.in	2 Dec 2003 22:42:12 -0000
@@ -866,6 +866,9 @@
    `char[]'. */
 #undef YYTEXT_POINTER
 
+/* define WINE_NO_PROFILE if we're using the profiler */
+#undef WINE_NO_PROFILE
+
 /* Set this to 64 to enable 64-bit file support on Linux */
 #undef _FILE_OFFSET_BITS
 
Index: dlls/Makedll.rules.in
===================================================================
RCS file: /home/wine/wine/dlls/Makedll.rules.in,v
retrieving revision 1.61
diff -u -r1.61 Makedll.rules.in
--- dlls/Makedll.rules.in	11 Oct 2003 01:05:18 -0000	1.61
+++ dlls/Makedll.rules.in	2 Dec 2003 22:42:12 -0000
@@ -9,6 +9,9 @@
 # plus all variables required by the global Make.rules.in
 #
 
+PROFILE_LIBS    = @PROFILE_LIBS@
+PROFILE_CFLAGS  = @PROFILE_CFLAGS@
+
 DEFS        = -D__WINESRC__ $(EXTRADEFS)
 DLLFLAGS    = @DLLFLAGS@
 DLLEXT      = @DLLEXT@
@@ -16,7 +19,7 @@
 SPEC_DEF    = $(MAINSPEC).def
 WIN16_FILES = $(SPEC_SRCS16:.spec=.spec.o) $(C_SRCS16:.c=.o) $(EXTRA_OBJS16)
 ALL_OBJS    = @WIN16_FILES@ $(OBJS) $(MODULE).dbg.o
-ALL_LIBS    = $(LIBWINE) $(EXTRALIBS) $(LIBPORT) $(LDFLAGS) $(LIBS)
+ALL_LIBS    = $(LIBWINE) $(EXTRALIBS) $(LIBPORT) $(LDFLAGS) $(LIBS) $(PROFILE_LIBS)
 IMPORTLIBS  = $(DELAYIMPORTS:%=$(DLLDIR)/lib%.$(IMPLIBEXT)) $(IMPORTS:%=$(DLLDIR)/lib%.$(IMPLIBEXT))
 
 all: $(MODULE)$(DLLEXT) $(SUBDIRS)
Index: libs/Makefile.in
===================================================================
RCS file: /home/wine/wine/libs/Makefile.in,v
retrieving revision 1.5
diff -u -r1.5 Makefile.in
--- libs/Makefile.in	1 May 2003 03:16:21 -0000	1.5
+++ libs/Makefile.in	2 Dec 2003 22:42:12 -0000
@@ -4,12 +4,15 @@
 VPATH     = @srcdir@
 MODULE    = none
 
+PROFILE_LIBNAME = @PROFILE_LIBNAME@
+
 SUBDIRS = \
 	port \
 	unicode \
 	uuid \
 	wine \
-	wpp
+	wpp \
+	wprof
 
 INSTALLSUBDIRS = \
 	unicode \
@@ -21,7 +24,8 @@
 	libwine_port.a \
 	libwine_unicode.$(LIBEXT) \
 	libwine_uuid.a \
-	libwpp.a
+	libwpp.a \
+	$(PROFILE_LIBNAME:%=%.$(LIBEXT))
 
 @MAKE_RULES@
 
@@ -51,6 +55,9 @@
 
 libwpp.a: wpp/libwpp.a
 	$(RM) $@ && $(LN_S) wpp/$@ $@
+
+libwineprof.so libwineprof.so.1 libwineprof.a: wprof/libwineprof.$(LIBEXT)
+	$(RM) $@ && $(LN_S) wprof/$@ $@
 
 # Directory dependencies
 
Index: loader/Makefile.in
===================================================================
RCS file: /home/wine/wine/loader/Makefile.in,v
retrieving revision 1.14
diff -u -r1.14 Makefile.in
--- loader/Makefile.in	22 Nov 2003 00:08:26 -0000	1.14
+++ loader/Makefile.in	2 Dec 2003 22:42:12 -0000
@@ -16,6 +16,9 @@
 WINE_BINARIES = @WINE_BINARIES@
 MAIN_BINARY   = @MAIN_BINARY@
 
+PROFILE_CFLAGS = @PROFILE_CFLAGS@
+PROFILE_LIBS = @PROFILE_LIBS@
+
 all: $(WINE_BINARIES) $(MODULE)
 
 @MAKE_RULES@
@@ -27,10 +30,10 @@
 	$(CC) -o $@ $(LDEXECFLAGS) glibc.o $(LIBWINE) $(LIBPORT) $(LIBPTHREAD) $(EXTRALIBS) $(LDFLAGS)
 
 wine-kthread: $(KTHREAD_OBJS) Makefile.in
-	$(CC) -o $@ $(LDEXECFLAGS) $(KTHREAD_OBJS) $(LIBWINE) $(LIBPORT) $(EXTRALIBS) $(LDFLAGS)
+	$(CC) -o $@ $(LDEXECFLAGS) $(KTHREAD_OBJS) $(LIBWINE) $(LIBPORT) $(EXTRALIBS) $(LDFLAGS) $(PROFILE_LIBS)
 
 wine-pthread: $(PTHREAD_OBJS) Makefile.in
-	$(CC) -o $@ $(LDEXECFLAGS) $(PTHREAD_OBJS) $(LIBWINE) $(LIBPORT) $(LIBPTHREAD) $(EXTRALIBS) $(LDFLAGS)
+	$(CC) -o $@ $(LDEXECFLAGS) $(PTHREAD_OBJS) $(LIBWINE) $(LIBPORT) $(LIBPTHREAD) $(EXTRALIBS) $(LDFLAGS) $(PROFILE_LIBS)
 
 $(MODULE): $(MAIN_BINARY)
 	$(RM) $(MODULE) && $(LN_S) $(MAIN_BINARY) $(MODULE)
Index: programs/Makeprog.rules.in
===================================================================
RCS file: /home/wine/wine/programs/Makeprog.rules.in,v
retrieving revision 1.31
diff -u -r1.31 Makeprog.rules.in
--- programs/Makeprog.rules.in	11 Oct 2003 01:05:18 -0000	1.31
+++ programs/Makeprog.rules.in	2 Dec 2003 22:42:12 -0000
@@ -9,10 +9,13 @@
 # plus all variables required by the global Make.rules.in
 #
 
+PROFILE_LIBS   = @PROFILE_LIBS@
+PROFILE_CFLAGS = @PROFILE_CFLAGS@
+
 DEFS        = $(EXTRADEFS)
 DLLFLAGS    = @DLLFLAGS@
 ALL_OBJS    = $(OBJS) $(MODULE).dbg.o
-ALL_LIBS    = $(LIBWINE) $(EXTRALIBS) $(LIBPORT) $(LDFLAGS) $(LIBS)
+ALL_LIBS    = $(LIBWINE) $(EXTRALIBS) $(LIBPORT) $(LDFLAGS) $(LIBS) $(PROFILE_LIBS)
 BASEMODULE  = $(MODULE:.exe=)
 TESTIMPORTS = $(DELAYIMPORTS) $(IMPORTS)
 RUNTESTFLAGS= -q -P wine -T $(TOPOBJDIR) $(PLTESTPROGRAM:%=-p %)
Index: dlls/ntdll/Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/ntdll/Makefile.in,v
retrieving revision 1.94
diff -u -r1.94 Makefile.in
--- dlls/ntdll/Makefile.in	11 Nov 2003 22:21:29 -0000	1.94
+++ dlls/ntdll/Makefile.in	2 Dec 2003 22:42:13 -0000
@@ -50,6 +50,9 @@
 
 @MAKE_DLL_RULES@
 
+$(TOPOBJDIR)/scheduler/pthread.o: $(TOPOBJDIR)/scheduler/pthread.c
+	$(CC) -c $(CFLAGSNOPROF) -o $@ $<
+
 relay32.s: $(WINEBUILD)
 	$(WINEBUILD) $(DEFS) $(DLLFLAGS) -o $@ --relay32
 
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ libs/wprof/.cvsignore	2003-10-04 06:08:00.000000000 +0900
@@ -0,0 +1,2 @@
+Makefile
+libwprof.so
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ libs/wprof/Makefile.in	2003-10-30 20:07:48.000000000 +0900
@@ -0,0 +1,34 @@
+# Makefile for wineprof
+
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = none
+
+PROFILE_LIBNAME = @PROFILE_LIBNAME@
+
+ at MAKE_RULES@
+
+TARGETS=$(PROFILE_LIBNAME:%=%.$(LIBEXT))
+
+CFLAGS += -ggdb -fPIC #-DDEBUG
+#LIBS += 
+
+all: $(TARGETS)
+
+proftest: proftest.c
+	$(CC) -O2 -ggdb proftest.c -o proftest -finstrument-functions $(LIBS) \
+	-lwineprof -lpthread
+
+proftest_pg: proftest.c
+	$(CC) -O2 -ggdb -pg proftest.c -o proftest_pg $(LIBS)
+
+LIBWPROF_OBJ = profiler.o wrappers.o string.o
+
+$(PROFILE_LIBNAME).$(LIBEXT): $(LIBWPROF_OBJ)
+	$(CC) -static --shared $< -o $@ -Wl,-soname,libwineprof.so
+
+clean::
+	rm -f proftest proftest_pg
+
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ libs/wprof/atomicity.h	2003-05-30 17:19:45.000000000 +0900
@@ -0,0 +1,57 @@
+/* Low-level functions for atomic operations.  ix86 version, x >= 4.
+   Copyright (C) 1997, 2000 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef _ATOMICITY_H
+#define _ATOMICITY_H	1
+
+#include <inttypes.h>
+
+
+static inline uint32_t
+__attribute__ ((unused))
+exchange_and_add (volatile uint32_t *mem, uint32_t val)
+{
+  register uint32_t result;
+  __asm__ __volatile__ ("lock; xaddl %0,%2"
+			: "=r" (result) : "0" (val), "m" (*mem) : "memory");
+  return result;
+}
+
+static inline void
+__attribute__ ((unused))
+atomic_add (volatile uint32_t *mem, int val)
+{
+  __asm__ __volatile__ ("lock; addl %0,%1"
+			: : "ir" (val), "m" (*mem) : "memory");
+}
+
+static inline char
+__attribute__ ((unused))
+compare_and_swap (volatile long int *p, long int oldval, long int newval)
+{
+  char ret;
+  long int readval;
+
+  __asm__ __volatile__ ("lock; cmpxchgl %3, %1; sete %0"
+                        : "=q" (ret), "=m" (*p), "=a" (readval)
+                        : "r" (newval), "m" (*p), "a" (oldval));
+  return ret;
+}
+
+#endif /* atomicity.h */
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ libs/wprof/profiler.c	2003-10-04 05:50:33.000000000 +0900
@@ -0,0 +1,557 @@
+/* Wine Profiler
+ *
+ * Copyright 2000-2003 Codeweavers, Charles Loep
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+ * It's unlikely that this works on anything other than linux/i386
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "atomicity.h"
+#include "wine/profiler.h"
+
+
+#define WPROF_MAX_CALL_DEPTH 1024
+#define WPROF_MAX_ARC        1000000
+
+#define WPROF_CURRENT        getpid()
+#define WPROF_WMON_PREFIX    "/tmp/wmon.out."
+
+
+#define DEBUG
+
+#ifdef DEBUG
+#define db_printf(args...) printf(args)
+#else
+#define db_printf(args...)
+#endif
+
+
+typedef struct cstack_s {
+    arc_t      *current;
+    counter64_t entry_time;
+} cstack_t;
+
+
+typedef struct ptinfo_s {
+    pid_t pid;
+    arc_t *root;
+    int map_fd;
+    int map_size;
+    wprof_header_t *header;
+    cstack_t cstack[WPROF_MAX_CALL_DEPTH];
+    int cstackp;
+    long state;
+    struct ptinfo_s *next;
+} ptinfo_t;
+
+
+#define PTINFO_FINALIZED ((void *)-1)
+#define PTINFO_TOTAL 32768
+static ptinfo_t *ptinfo_map[PTINFO_TOTAL];
+
+
+typedef enum {
+    WPROF_STATE_OFF = 0,
+    WPROF_STATE_ON,
+    WPROF_STATE_BUSY
+} WPROF_STATE;
+
+
+/* global data */
+static pid_t wprof_master = 0;
+static counter32_t wprof_cpu_hz = 0;
+static long wprof_global_state;
+static int wprof_thread_count = 0;
+
+void __attribute__ ((constructor)) wprof_init();
+void __attribute__ ((destructor)) wprof_exit();
+
+
+static void wprof_finalize_thread(ptinfo_t *pt);
+static arc_t *wprof_alloc_arc(ptinfo_t *pt, address32_t addr);
+
+
+/*
+ * determine the number of ticks per second
+ */
+static unsigned long wprof_get_hz()
+{
+    FILE *fp;
+    double ticks = 0.0;
+
+    fp = fopen("/proc/cpuinfo", "r");
+    while(!feof(fp))
+        {
+
+            char line[1024];
+
+            if(!fgets(line, 1024, fp))
+                break;
+            if(sscanf(line, "cpu MHz         : %lf", &ticks) != 1)
+                continue;
+            db_printf("WPROF: ticks per second: %.0lf\n", (ticks * 1000000.0));
+            break;
+        }
+    fclose(fp);
+
+    if(ticks == 0.0)
+	{
+	    printf("WPROF: unable to determine HZ (%lf)\n", ticks);
+	    _exit(1);
+	}
+
+    return (unsigned long)(ticks * 1000000.0);
+}
+
+static counter64_t get_tsc() 
+{
+        counter64_t value;
+        __asm__ __volatile__("rdtsc" : "=A" (value));
+	return value;
+}
+
+
+
+/*
+ * constructor
+ */
+void wprof_init()
+{
+    wprof_master = WPROF_CURRENT;
+    wprof_cpu_hz = wprof_get_hz();
+ 
+    db_printf("WPROF: initialized!\n");
+
+    wprof_global_state = WPROF_STATE_ON;
+}
+
+/*
+ * destructor
+ */
+void wprof_exit()
+{
+    int i;
+
+    wprof_global_state = WPROF_STATE_OFF;
+    db_printf("WPROF: unloading %d (%d threads)\n", WPROF_CURRENT, wprof_thread_count);
+
+#if 1
+    // finalize any threads that didnt we didn't catch before
+    for(i = 0; i < PTINFO_TOTAL; i++) {
+	if(ptinfo_map[i]) {
+	    ptinfo_t *pt = ptinfo_map[i];
+
+	    if(pt == PTINFO_FINALIZED) {
+		    db_printf("WPROF: finalizing %d (skipped)\n", i);
+		    continue;
+	    }
+
+	    db_printf("WPROF: finalizing %d\n", pt->pid);
+	    wprof_finalize_thread(pt);
+	    ptinfo_map[i] = 0;
+	}
+    }
+#endif
+
+}
+
+
+static char digits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
+static void strintcat(char *str, int i)
+{
+    int pos = 11;
+    char buf[12];
+        
+    buf[11] = 0;
+    do {
+	buf[--pos] = digits[i % 10];
+	i = i / 10;
+    } while(i != 0);
+    
+    strcat(str, &buf[pos]);
+}
+
+
+/*
+ * map the profile output file for the current thread
+ */
+static ptinfo_t *wprof_map_output()
+{
+    ptinfo_t *pt = NULL;
+    char filename[256] = WPROF_WMON_PREFIX;
+    unsigned char *addr;
+    int map_size, map_fd;
+
+    map_size = sizeof(wprof_header_t) + (WPROF_MAX_ARC * sizeof(arc_t)) + sizeof(ptinfo_t);
+
+    strintcat(filename, WPROF_CURRENT);
+    db_printf("WPROF: creating %s\n", filename);
+
+    map_fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0664);
+    ftruncate(map_fd, map_size);
+    addr = mmap(NULL, map_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0);
+
+    pt = (ptinfo_t *)(addr + map_size - sizeof(ptinfo_t));
+    pt->map_size = map_size;
+    pt->map_fd = map_fd;
+    pt->header = (wprof_header_t *)addr;
+    pt->header->vaddr = (unsigned int)addr;
+
+    return pt;
+}
+
+/* 
+ * allocate a thread info struct
+ */
+static ptinfo_t *wprof_alloc_ptinfo()
+{
+    ptinfo_t *pt;
+    arc_t *arc;
+    pid_t current = WPROF_CURRENT;
+
+    db_printf("WPROF: new thread; pid %d\n", current);
+
+    pt = wprof_map_output();
+    pt->pid  = current;
+    pt->root = NULL;
+    pt->next = NULL;
+
+    pt->header->magic   = WPROF_MAGIC;
+    pt->header->version = WPROF_VERSION;
+    pt->header->pid     = pt->pid;
+    pt->header->cpu_hz  = wprof_cpu_hz;
+    gettimeofday(&pt->header->time_start, NULL);
+    gettimeofday(&pt->header->time_stop, NULL);
+
+    arc = wprof_alloc_arc(pt, 0);
+    pt->root = arc;
+    pt->cstackp = 0;
+    pt->cstack[pt->cstackp].current = pt->root;
+    pt->cstack[pt->cstackp].entry_time = get_tsc();
+
+    ptinfo_map[current] = pt;
+    pt->state = WPROF_STATE_ON;
+
+    wprof_thread_count++;
+
+    return pt;
+}
+
+
+/*
+ * retrieve or allocate a thread info struct
+ */
+static ptinfo_t *wprof_get_ptinfo()
+{
+    ptinfo_t *pt;
+    pid_t current = WPROF_CURRENT;
+
+    pt = ptinfo_map[current];
+    
+    if(pt == PTINFO_FINALIZED)
+	    return NULL;
+
+    if(!pt)
+	    pt = wprof_alloc_ptinfo();
+    return pt;
+}
+
+
+static void wprof_unlink_ptinfo(ptinfo_t *ptrem)
+{
+	ptinfo_map[ptrem->pid] = PTINFO_FINALIZED;
+}
+
+/*
+static int wprof_has_ptinfo()
+{
+    pid_t current = WPROF_CURRENT;
+
+    if(ptinfo_map[current])
+	return 1;
+    return 0;
+}
+*/
+
+/*
+ * allocate new arc
+ * FIXME: make sure there's enough free space left for the allocation
+ */
+static arc_t *wprof_alloc_arc(ptinfo_t *pt, address32_t addr)
+{
+    arc_t *arc;
+
+    arc = &pt->header->root[pt->header->arc_count++];
+    arc->addr = addr;
+
+    //db_printf("WPROF: allocating arc for %x\n", addr);
+    return arc;
+}
+
+
+/*
+ * append arc to the list
+ */
+static arc_t *wprof_arc_list_append(arc_t *root, arc_t *arc)
+{
+    arc_t *tmp;
+
+    if(!root)
+	return arc;
+
+    tmp = root;
+    while(tmp->next)
+	tmp = tmp->next;
+    tmp->next = arc;
+
+    return root;
+}
+
+
+/*
+ * find arc with address addr or allocate a new one
+ */
+static arc_t *wprof_find_arc(ptinfo_t *pt, arc_t *current, address32_t addr)
+{
+    arc_t *arc;
+
+    for(arc = current->children; arc; arc = arc->next)
+	{
+	    if(arc->addr == addr)
+		return arc;
+	}
+
+    arc = wprof_alloc_arc(pt, addr);
+    current->children = wprof_arc_list_append(current->children, arc);
+
+    return arc;
+}
+
+#define CHECKPT(pt)      do { if(!pt) return; if(pt == PTINFO_FINALIZED) return; } while(0)
+
+
+/*
+ * profile entry point
+ * this is called first thing in a profiled function
+ */
+void __cyg_profile_func_enter(address32_t this_fn, address32_t call_site)
+{
+    ptinfo_t *pt;
+    cstack_t *top;
+    arc_t *arc;
+
+    pt = wprof_get_ptinfo();
+    CHECKPT(pt);
+
+    if(pt->state == WPROF_STATE_OFF)
+	    return;
+
+    /* FIXME
+     * what to do when a signal arrives in the middle of this?
+     * for now we'll just ignore it and return
+     */
+    if(!compare_and_swap(&pt->state, WPROF_STATE_ON, WPROF_STATE_BUSY))
+	{
+	    db_printf("WPROF: warning; re-entry in __menter (state=%ld)\n", pt->state);
+	    return;
+	}
+
+    top = &pt->cstack[pt->cstackp++];
+    arc = wprof_find_arc(pt, top->current, this_fn);
+    arc->count++;
+
+    top++;
+    top->current = arc;
+    top->entry_time = get_tsc();
+    
+    compare_and_swap(&pt->state, WPROF_STATE_BUSY, WPROF_STATE_ON);
+}
+
+
+/*
+ * called at exit of a profiled function
+ */
+void __cyg_profile_func_exit(address32_t this_fn, address32_t call_site)
+{
+    counter64_t after;
+    ptinfo_t *pt;
+    cstack_t *top;
+    arc_t *arc;
+
+    after = get_tsc();
+    pt = wprof_get_ptinfo();
+    CHECKPT(pt);
+
+    if(pt->state == WPROF_STATE_OFF)
+	    return;
+
+    /*
+     * this happens when __menter was re-entered (by a signal)
+     * ignore the signal handler exit
+     */
+    if(pt->state == WPROF_STATE_BUSY)
+	{
+            db_printf("WPROF: warning; BUSY in __mexit\n");
+	    return;
+	}
+    top = &pt->cstack[pt->cstackp];
+    arc = top->current;
+    arc->ticks += (after - top->entry_time);
+
+    pt->cstackp--;
+}
+
+
+static void sim_func_exit(ptinfo_t *pt)
+{
+	counter64_t after;
+	cstack_t *top;
+	arc_t *arc;
+
+	after = get_tsc();
+
+	top = &pt->cstack[pt->cstackp];
+	arc = top->current;
+	arc->ticks += (after - top->entry_time);
+
+	pt->cstackp--;
+}
+
+
+
+/*
+ * grab a copy of /proc/self/maps and save the relevant entries to 
+ * the profile output file.
+ * FIXME: cant use /proc/self/.. since this may be called from a different process
+ */
+static void wprof_write_maps(ptinfo_t *pt, char *addr)
+{
+    FILE *fp;
+
+    fp = fopen("/proc/self/maps", "r");
+    if(!fp)
+	return;
+    
+    while(!feof(fp))
+	{
+	    wprof_map_t map;
+	    char tmp[1024], soname[256], perm[16];
+	    int start, end, offset, res;
+
+	    if(!fgets(tmp, 1024, fp))
+		break;
+
+	    res = sscanf(tmp, "%x-%x %s %x %*s %*d %[^\n]\n", 
+			 &start, &end, perm, &offset, soname);
+	    if(res != 5)
+		continue;
+	
+	    /* only save entries mapped as executable */
+	    if(!strstr(perm, "x"))
+		continue;
+	
+	    memset(&map, 0, sizeof(wprof_map_t));
+	    map.addr_from = start;
+	    map.addr_to   = end;
+	    map.offset    = offset;
+	    strncpy(map.filename, soname, sizeof(map.filename));
+
+	    memcpy(addr, &map, sizeof(wprof_map_t));
+	    addr += sizeof(wprof_map_t);
+	    pt->header->map_count++;
+	}
+
+    fclose(fp);
+}
+
+
+/*
+ * finalize a threads output file
+ */
+static void wprof_finalize_thread(ptinfo_t *pt)
+{
+    size_t end;
+
+    CHECKPT(pt);
+
+    while(pt->cstackp > 0)
+	{
+		//db_printf("WPROF: unwind cstack %d\n", pt->cstackp);
+	    sim_func_exit(pt);
+	}
+
+    pt->state = WPROF_STATE_OFF;
+    
+    db_printf("[%d] profile_exit\n", pt->pid);
+    db_printf("[%d] arc_count=%d (mem=%d)\n",
+	   pt->pid, pt->header->arc_count, 
+	   pt->header->arc_count * sizeof(arc_t));
+    
+    end = sizeof(wprof_header_t) + (pt->header->arc_count * sizeof(arc_t));
+    
+    if(lseek(pt->map_fd, end, SEEK_SET) != -1) {
+	    void *addr = (void *)pt->header;
+	    int map_fd = pt->map_fd;
+	    int map_size = pt->map_size;
+	    
+	    wprof_write_maps(pt, (char *)(pt->header->vaddr + end));
+	    pt->header->flags |= WFLAG_CLOSED;
+            wprof_unlink_ptinfo(pt);
+	    
+	    ftruncate(map_fd, end + (pt->header->map_count * sizeof(wprof_map_t)));
+	    
+	    munmap(addr, map_size);
+	    close(map_fd);
+    } else {
+	    printf("WPROF: lseek failed\n");
+    }
+}
+
+
+/*
+ * exit the CURRENT thread
+ */
+void wprof_exit_thread(int status)
+{
+	ptinfo_t *pt;
+
+	db_printf("WPROF: wprof_exit_thread(%d)\n", status);
+
+	pt = wprof_get_ptinfo();
+	CHECKPT(pt);
+
+	/* record exit code */
+	pt->header->exitcode = status;
+
+	/* store some useful info */
+	pt->header->flags |= WFLAG_HAS_RUSAGE;
+	getrusage(RUSAGE_SELF, &pt->header->rusage);
+	gettimeofday(&pt->header->time_stop, NULL);
+
+	wprof_finalize_thread(pt);
+}
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ libs/wprof/proftest.c	2003-05-30 17:19:45.000000000 +0900
@@ -0,0 +1,95 @@
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <fcntl.h>
+
+#include <pthread.h>
+
+void handler()
+{
+    printf("ping %d\n", getpid());
+}
+
+int bar(int a)
+{
+     return foo(!a);
+}
+
+int foo(int a)
+{
+    switch(a)
+	{
+	case 0:
+	    return 12345;
+	case 1:
+	    return bar(a);
+	default:
+	    return 0;
+	}
+}
+
+void blah()
+{
+    foo(2);
+}
+
+void set_timer()
+{
+    struct itimerval it;
+    struct sigaction sa;
+
+    memset(&sa, 0, sizeof(sa));
+    sa.sa_handler = handler;
+    sa.sa_flags   = SA_RESTART;
+    sigaction(SIGALRM, &sa, NULL);
+
+    memset(&it, 0, sizeof(it));
+    it.it_value.tv_usec    = 100000;
+    it.it_interval.tv_usec = 100000;
+    setitimer(ITIMER_REAL, &it, NULL);
+
+
+}
+
+int get_random(int q)
+{
+    return random() % q;
+}
+
+
+void *runner(void *unused)
+{
+    int i;
+
+    set_timer();
+
+    for(i = 0; i < 1000000; i++)
+        foo(get_random(2));
+}
+
+
+void test1()
+{
+  int fd = open("/dev/null", O_RDONLY);
+  close(fd);
+}
+
+int main()
+{
+#if 1
+    pthread_t t1, t2;
+
+    pthread_create(&t1, NULL, runner, NULL);
+    pthread_create(&t2, NULL, runner, NULL);
+
+    pthread_join(t1, 0);
+    pthread_join(t2, 0);
+#else
+    //runner(0);
+    test1();
+#endif
+	printf("proftest %d exiting\n", getpid());
+    exit(101);
+}
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ libs/wprof/string.c	2003-05-30 17:19:45.000000000 +0900
@@ -0,0 +1,10 @@
+
+#define __STRING_INLINE
+#include <string.h>
+
+long double __sqrtl(long double __x)
+{
+    register long double __result;
+    __asm __volatile__("fsqrt" : "=t" (__result) : "0" (__x));
+    return __result;
+}
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ libs/wprof/wrappers.c	2003-09-09 11:32:21.000000000 +0900
@@ -0,0 +1,104 @@
+/* Wine Profiler
+ *
+ * Copyright 2000-2003 Codeweavers, Charles Loep
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define __USE_GNU
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <dlfcn.h>
+
+#include "wine/profiler.h"
+
+
+#ifndef RTLD_NEXT
+#define RTLD_NEXT ((void *)-1L)
+#endif
+
+
+#ifdef DEBUG
+#define db_printf(args...) printf(args)
+#else
+#define db_printf(args...)
+#endif
+
+
+#define WPROF_DETECT_EXIT
+
+
+#ifdef WPROF_DETECT_EXIT
+
+static int wrappers_initialized = 0;
+static void *(*real_dlopen)(const char *filename, int mode) = 0;
+static int (*real_dlclose)(void *handle) = 0;
+static void (*real_exit)(int status) __attribute__((__noreturn__)) = 0;
+static void (*real__exit)(int status) = 0;
+
+
+
+static void init_wrappers()
+{
+	real_dlopen = dlsym(RTLD_NEXT, "dlopen");
+	real_dlclose = dlsym(RTLD_NEXT, "dlclose");
+	real_exit = dlsym(RTLD_NEXT, "exit");
+	real__exit = dlsym(RTLD_NEXT, "_exit");
+}
+
+
+void *dlopen(const char *filename, int flag)
+{
+	if(!wrappers_initialized)
+		init_wrappers();
+
+        db_printf("[%d] dlopen(%s, %d)\n", getpid(), filename, flag);
+        return real_dlopen(filename, flag);
+}
+
+int dlclose(void *handle)
+{
+	if(!wrappers_initialized)
+		init_wrappers();
+	
+	db_printf("[%d] dlclose(%p)\n", getpid(), handle);
+	return real_dlclose(handle);
+}
+
+void exit(int status)
+{
+	if(!wrappers_initialized)
+		init_wrappers();
+
+	db_printf("[%d] exit(%d)\n", getpid(), status);
+	wprof_exit_thread(status);
+
+	real_exit(status);
+}
+
+void _exit(int status)
+{
+	if(!wrappers_initialized)
+		init_wrappers();
+
+	db_printf("[%d] _exit(%d)\n", getpid(), status);
+	wprof_exit_thread(status);
+
+	real__exit(status);
+}
+
+#endif /*WPROF_DETECT_EXIT*/
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ include/wine/profiler.h	2003-10-30 20:07:47.000000000 +0900
@@ -0,0 +1,87 @@
+/* Wine Profiler
+ *
+ * Copyright 2000-2003 Codeweavers, Charles Loep
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __WPROF_H__
+#define __WPROF_H__
+
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#define XSTDCALL __attribute((__stdcall__))
+#define XNOPROF  __attribute((no_instrument_function))
+
+#define WPROF_MAGIC   0x4E4F4D57
+#define WPROF_VERSION 0x02
+
+
+typedef unsigned long long counter64_t;
+typedef unsigned long      counter32_t;
+
+typedef void * address32_t;
+
+
+typedef struct arc_s {
+    address32_t   addr;
+    counter32_t   count;
+    counter64_t   ticks;
+    struct arc_s *children;
+    struct arc_s *next;
+} arc_t;
+
+
+enum {
+	WFLAG_NONE       = 0,
+	WFLAG_CLOSED     = 1,
+	WFLAG_HAS_RUSAGE = 2,
+};
+
+typedef struct wprof_header_s {
+    unsigned int   magic;
+    unsigned char  version;
+    unsigned char  flags;
+    unsigned char  resv[2];
+    pid_t          pid;
+    struct timeval time_start;
+    struct timeval time_stop;
+    struct rusage  rusage;
+    int            exitcode;
+    counter32_t    cpu_hz;
+    unsigned int   vaddr;
+    size_t         map_count;
+    unsigned int   map_start;
+    size_t         arc_count;
+    arc_t          root[0];
+} wprof_header_t;
+
+
+
+typedef struct wprof_map_s {
+    unsigned int addr_from;
+    unsigned int addr_to;
+    unsigned int offset;
+    char         filename[256];
+} wprof_map_t;
+
+
+
+void wprof_exit_thread(int status);
+
+
+#endif /* __WPROF_H__ */
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ tools/wineprof/.cvsignore	2003-10-04 06:06:15.000000000 +0900
@@ -0,0 +1,2 @@
+Makefile
+wineprof
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ tools/wineprof/Makefile.in	2003-10-30 20:07:48.000000000 +0900
@@ -0,0 +1,29 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+EXEEXT    = @EXEEXT@
+
+PROGRAMS = wineprof$(EXEEXT)
+
+MODULE   = none
+
+C_SRCS = \
+	hashlist.c \
+	wineprof.c
+
+all: $(PROGRAMS)
+
+ at MAKE_RULES@
+
+wineprof$(EXEEXT): $(OBJS)
+	$(CC) $(CFLAGS) -o wineprof$(EXEEXT) $(OBJS) $(LIBPORT) $(LDFLAGS)
+
+install:: $(PROGRAMS)
+	$(MKINSTALLDIRS) $(bindir)
+	$(INSTALL_PROGRAM) wineprof$(EXEEXT) $(bindir)/wineprof$(EXEEXT)
+
+uninstall::
+	$(RM) $(bindir)/wineprof$(EXEEXT)
+
+### Dependencies:
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ tools/wineprof/wineprof.c	2003-10-30 20:07:48.000000000 +0900
@@ -0,0 +1,857 @@
+/* Wine Profiler
+ *
+ * Copyright 2000-2003 Codeweavers, Charles Loep
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#include <getopt.h>
+
+#include "wine/profiler.h"
+#include "hashlist.h"
+#include "wineprof.h"
+
+
+/*--------------------------------------------------------*
+ * options
+ *--------------------------------------------------------*/
+ 
+#define OPT_ON   1
+#define OPT_OFF  0
+
+struct options {
+    int debug;
+    int flat;
+    int callers;
+    int calltree;
+    int soprefix;
+    int merge;
+    int listso;
+    int source;
+    char *sort;
+    int precision;
+    List *ignore;
+    int compress;
+};
+
+struct options opt = {
+    debug     : OPT_OFF,
+    flat      : OPT_ON,
+    callers   : OPT_OFF,
+    calltree  : OPT_OFF,
+    soprefix  : OPT_OFF,
+    merge     : OPT_OFF,
+    listso    : OPT_OFF,
+    source    : OPT_OFF,
+    sort      : "self",
+    precision : 3,
+    /* ignore    : NULL, */
+    compress  : OPT_ON,
+};
+
+
+static const struct option long_options[] =
+{
+    { "flat",           no_argument,       &opt.flat,     OPT_ON  },
+    { "no-flat",        no_argument,       &opt.flat,     OPT_OFF },
+    { "callers",        no_argument,       &opt.callers,  OPT_ON },
+    { "no-callers",     no_argument,       &opt.callers,  OPT_OFF },
+    { "calltree",       no_argument,       &opt.calltree, OPT_ON  },
+    { "no-calltree",    no_argument,       &opt.calltree, OPT_OFF },
+
+    { "dso-prefix",     no_argument,       &opt.soprefix, OPT_ON  },
+    { "no-dso-prefix",  no_argument,       &opt.soprefix, OPT_OFF },
+
+    { "merge",          no_argument,       &opt.merge,    OPT_ON  },
+    { "no-merge",       no_argument,       &opt.merge,    OPT_OFF },
+    { "list-dso",       no_argument,       &opt.listso,   OPT_ON  },
+    { "no-list-dso",    no_argument,       &opt.listso,   OPT_OFF },
+    { "source",         no_argument,       &opt.source,   OPT_ON  },
+    { "no-source",      no_argument,       &opt.source,   OPT_OFF },
+
+    { "compress",       no_argument,       &opt.compress, OPT_ON  },
+    { "no-compress",    no_argument,       &opt.compress, OPT_OFF },
+
+    /* { "ignore",         required_argument, NULL,          'i' }, */
+    { "sort",           required_argument, NULL,          's' },
+    { "precision",      required_argument, NULL,          'p' },
+
+    { "debug",          no_argument,       &opt.debug,    OPT_ON  },
+    { "help",           no_argument,       NULL,          'h' },
+};
+
+
+/*--------------------------------------------------------*/
+// prototypes
+/*--------------------------------------------------------*/
+ 
+void usage();
+void view_profile_flat(profile_t *prof);
+void view_profile_calltree(profile_t *prof);
+
+
+
+/*--------------------------------------------------------*/
+// the code!
+/*--------------------------------------------------------*/
+
+static unsigned long wprof_get_hz()
+{
+    FILE *fp;
+    double ticks = 0.0;
+
+    fp = fopen("/proc/cpuinfo", "r");
+    while(!feof(fp))
+        {
+
+            char line[1024];
+
+            if(!fgets(line, 1024, fp))
+                break;
+            if(sscanf(line, "cpu MHz         : %lf", &ticks) != 1)
+                continue;
+            printf("WPROF: ticks per second: %.0lf\n", (ticks * 1000000.0));
+            break;
+        }
+    fclose(fp);
+
+    if(ticks == 0.0)
+        {
+            printf("WPROF: unable to determine CPU HZ\n");
+	    exit(1);
+        }
+
+    return (long)(ticks * 1000000.0);
+}
+
+
+static size_t file_size_for_fd(int fd)
+{
+    struct stat st;
+    fstat(fd, &st);
+    return st.st_size;
+}
+
+static char *basename(char *file)
+{
+    char *p = strrchr(file, '/');
+    return p ? ++p : file;
+}
+
+static char *fullname(symbol_t *sym)
+{
+    static char tmp[1024];
+
+    if(opt.soprefix)
+	sprintf(tmp, "%s:%s", basename(sym->soname), sym->name);
+    else
+	return sym->name;
+
+    return tmp;
+}
+
+/*
+ * static counter64_t msec(counter64_t hz, counter64_t count)
+ * {
+ *     double v;
+ *
+ *     v = (double)count / ((double)hz / 1000.0);
+ *     return (counter64_t)v;
+ * }
+ */
+
+static double dsec(counter64_t hz, counter64_t count)
+{
+    double v;
+
+    v = (double)count / (double)hz;
+    return v;
+}
+
+static double tv_to_double(struct timeval *tv)
+{
+	return (double)tv->tv_sec + ((double)tv->tv_usec / 1000000.0);
+}
+
+
+static char *repeat_char(char ch, int count)
+{
+	static char tmp[1024];
+	memset(tmp, ch, count);
+	tmp[count] = 0;
+	return tmp;
+}
+
+
+
+/*
+ * allocate a symbol
+ */
+symbol_t *symbol_new(char *name, address32_t addr)
+{
+    symbol_t *sym = malloc( sizeof(symbol_t)*1 );
+    sym->name = strdup(name);
+    sym->addr = (unsigned int)addr;
+
+    return sym;
+}
+
+
+/*
+ * fetch all symbols from soname
+ * <lazy> we use nm for now </lazy>
+ */
+static int read_symtab(HashTable *hash, unsigned int base, char *soname)
+{
+    FILE *fp;
+    char cmd[512], line[256];
+
+    if(opt.source)
+	sprintf(cmd, "nm --defined-only --line-numbers '%s' 2> /dev/null", soname);
+    else
+	sprintf(cmd, "nm --defined-only '%s' 2> /dev/null", soname);
+
+    fp = popen(cmd, "r");
+    if(!fp)
+	return 0;
+
+    soname = strdup(soname);
+
+    while(!feof(fp))
+	{
+	    unsigned int addr = 0;
+	    char type = 0;
+	    char symbol[256], source[512] = { 0 };
+	    symbol_t *sym;
+	    int res = 0;
+
+	    if(!fgets(line, 256, fp))
+		break;
+
+	    if(opt.source)
+		res = sscanf(line, "%x %c %s %s\n", &addr, &type, symbol, source);
+	
+	    else if(!opt.source || res != 4)
+		{
+		    if(sscanf(line, "%x %c %s\n", &addr, &type, symbol) != 3)
+			continue;
+		}
+	    
+	    if(addr < base)
+		addr += base;
+
+	    //printf("%c %x %s\n", type, addr, symbol);
+
+	    if(!hash_table_lookup(hash, (void*)addr) || type == 'T')
+		{
+		    sym = symbol_new(symbol, (address32_t)addr);
+		    sym->soname = soname;
+		    if(strlen(source))
+			sym->source = strdup(source);
+		    hash_table_insert(hash, (void*)addr, sym);
+		}
+	}
+    pclose(fp);
+
+    return 1;
+}
+
+/*
+ * returns true if sym's name is in the ingore list
+ */
+static int symbol_is_ignored(symbol_t *sym)
+{
+    List *e;
+
+    if(!sym)
+	return 0;
+    //FIXME this should be a hashtable
+    for(e = opt.ignore; e; e = e->next)
+	{
+	    char *str = e->data;
+	    if(!strcmp(str, sym->name))
+		return 1;
+	}
+    return 0;
+}
+
+
+/*
+ * helper for symbol_add_caller
+ */
+static int cmp_callinfo_sym(void* a, void* b)
+{
+    callinfo_t *ci = (callinfo_t *)a;
+    symbol_t *sym = (symbol_t *)b;
+
+    return !(ci->caller == sym);
+}
+
+/*
+ * add a caller to sym
+ */
+static void symbol_add_caller(symbol_t *sym, symbol_t *caller, arc_t *arc)
+{
+    callinfo_t *ci = 0;
+
+    if(opt.compress)
+	{
+	    List *e = list_find_custom(sym->callers, caller, cmp_callinfo_sym);
+	    if(e)
+		ci = e->data;
+	}
+
+    if(!ci)
+	{
+	    ci = malloc( sizeof(callinfo_t) );
+	    ci->caller = caller;
+	    sym->callers = list_append(sym->callers, ci);
+	}
+
+    ci->calls += arc->count;
+    ci->ticks += arc->ticks;
+}
+
+counter64_t calc_all_ticks(arc_t *root, HashTable *symtab, symbol_t *caller)
+{
+    counter64_t total_time = 0;
+    counter64_t children_time = 0;
+    arc_t *arc;
+
+    if(!root)
+        return 0;
+
+    for(arc = root->children; arc; arc = arc->next)
+        {
+            symbol_t *sym;
+
+            sym = hash_table_lookup(symtab, (void*)arc->addr);
+	    if(!sym) { 
+		    char name[32];
+		    sprintf(name, "(0x%X)", (unsigned int)arc->addr);
+		    sym = symbol_new(strdup(name), arc->addr);
+		    //printf("SYMBOL NOT FOUND %x\n", arc->addr); 
+                    hash_table_insert(symtab, (void*)arc->addr, sym);
+	    }
+
+            children_time = calc_all_ticks(arc, symtab, sym);
+	    //printf("children_time = %llu\n", children_time);
+            total_time += children_time;
+	    if(!symbol_is_ignored(sym)) {
+		    total_time += (arc->ticks - children_time);
+		    
+		    sym->count += arc->count;
+                    sym->ticks += arc->ticks;
+                    sym->ticks_kids += children_time;
+                    sym->ticks_self += arc->ticks - children_time;
+
+		    if(caller)
+			{
+			    symbol_add_caller(sym, caller, arc);
+			    //printf("SYM %-20s  CALLER %s\n", sym->name, caller->name);
+			}
+		}
+        }
+
+    return total_time;
+}
+
+
+static HashTable *process_maps(wprof_map_t *mp, int num_maps)
+{
+    HashTable *hash;
+    int i;
+
+    hash = hash_table_new(direct_hash, direct_equal);
+
+    for(i = 0; i < num_maps; i++)
+        {
+	    if(opt.listso)
+		printf("%08x-%08x %08x %s  \n", mp->addr_from, mp->addr_to, mp->offset, mp->filename);
+	    read_symtab(hash, mp->addr_from, mp->filename);
+            mp++;
+        }
+    return hash;
+}
+
+
+profile_t *profile_open(char *filename)
+{
+    profile_t *prof;
+    wprof_header_t header, *hp;
+    double idle, systime, usertime;
+    void *addr;
+    int fd;
+
+    fd = open(filename, O_RDONLY);
+    read(fd, &header, sizeof(header));
+    if(header.magic != WPROF_MAGIC)
+        {
+            printf("WPROF: invalid magic number in %s\n", filename);
+            close(fd);
+            return 0;
+        }
+    if(header.version != WPROF_VERSION)
+	{
+	    printf("WPROF: version mismatch in %s\n", filename);
+	    close(fd);
+	    return 0;
+	}
+
+    if(!(header.flags & WFLAG_CLOSED))
+    {
+	printf("WPROF: profile not properly closed.\n");
+	//close(fd);
+	//return 0;
+      }
+
+    prof = malloc( sizeof(profile_t) );
+
+    usertime = tv_to_double(&header.rusage.ru_utime);
+    systime  = tv_to_double(&header.rusage.ru_stime);
+
+    idle = tv_to_double(&header.time_stop) - tv_to_double(&header.time_start);
+    idle -= tv_to_double(&header.rusage.ru_utime) + tv_to_double(&header.rusage.ru_stime);
+    
+    if(opt.debug)
+	{
+	    printf("WPROF pid:   %d\n", header.pid);
+	    printf("WPROF start: %s", ctime(&header.time_start.tv_sec));
+	    printf("WPROF stop:  %s", ctime(&header.time_stop.tv_sec));
+	    
+	    printf("WPROF utime: %.3lf sec\n", usertime);
+	    printf("WPROF stime: %.3lf sec\n", systime);
+	    printf("WPROF idle:  %.3lf sec\n", idle);
+	    
+	    printf("WPROF exit:  %d\n", header.exitcode);
+	    printf("WPROF clock: %lu\n", header.cpu_hz);
+	    printf("WPROF addr:  %x\n", header.vaddr);
+	    printf("WPROF #arc:  %d\n", header.arc_count);
+	    printf("WPROF #map:  %d\n", header.map_count);
+	}
+
+    if(header.flags & WFLAG_HAS_RUSAGE) {
+	    printf("pid   utime    stime    idle     total\n");
+	    printf("----- -------- -------- -------- --------\n");
+	    printf("%5d %8.3lf %8.3lf %8.3lf %8.3lf\n\n",
+		   header.pid,
+		   usertime,
+		   systime,
+		   idle,
+		   usertime + systime + idle
+		    );
+    } else {
+	    printf("WPROF: no rusage available.\n");
+    }
+
+    addr = mmap((void *)header.vaddr, file_size_for_fd(fd), PROT_READ,
+                MAP_SHARED, fd, 0);
+    if(addr == NULL || addr != (void *)header.vaddr)
+	{
+	    /* FIXME
+	     * <lazy> we should relocate the file if it can't be mapped at the
+	     * address specified in the header </lazy>
+	     */
+	    perror("mmap");
+	    printf("Oops! failed to map profile at %x - bug Charles to fix it if this ever happens.\n", header.vaddr);
+	    exit(1);
+	}
+    if(opt.debug)
+	printf("WPROF mapped at: %p\n", addr);
+
+    hp = (wprof_header_t *)addr;
+
+    prof->fd     = fd;
+    prof->header = hp;
+    prof->symtab = process_maps((wprof_map_t *)( ((char*)addr) + sizeof(wprof_header_t) + (hp->arc_count * sizeof(arc_t))), hp->map_count);
+    prof->total_ticks = calc_all_ticks(&hp->root[0], prof->symtab, 0);
+
+    return prof;
+}
+
+
+void profile_close(profile_t *prof)
+{
+    //FIXME: free profile
+}
+
+
+
+static void do_make_list(void* key, void* value, void* user_data)
+{
+    List *list = *(List **)user_data;
+    *(List **)user_data = list_prepend(list, value);
+}
+
+List *symtab_to_symlist(HashTable *symtab)
+{
+    List *list = 0;
+    hash_table_foreach(symtab, do_make_list, &list);
+
+    return list;
+}
+
+
+
+/*--------------------------------------------------------*/
+// sorting functions
+/*--------------------------------------------------------*/
+
+
+#define COMPARE(a, b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1)  
+
+#define DEFINE_SORT(TYPE, FIELD, NAME) \
+int NAME (void* a, void* b) \
+{ \
+  TYPE *val_a = (TYPE *)a; \
+  TYPE *val_b = (TYPE *)b; \
+  return COMPARE(val_b->FIELD, val_a->FIELD); \
+}
+
+DEFINE_SORT(symbol_t, count, do_sort_by_calls);
+DEFINE_SORT(symbol_t, ticks, do_sort_by_ticks);
+DEFINE_SORT(symbol_t, ticks_self, do_sort_by_ticks_self);
+DEFINE_SORT(symbol_t, ticks_kids, do_sort_by_ticks_kids);
+DEFINE_SORT(symbol_t, name, do_sort_by_name);
+
+
+static List *sort_symlist(List *symlist)
+{
+    if(!strcmp(opt.sort, "name"))
+	return list_sort(symlist, do_sort_by_name);
+
+    if(!strcmp(opt.sort, "calls"))
+        return list_sort(symlist, do_sort_by_calls);
+
+    if(!strcmp(opt.sort, "ticks"))
+        return list_sort(symlist, do_sort_by_ticks);
+
+    if(!strcmp(opt.sort, "self"))
+        return list_sort(symlist, do_sort_by_ticks_self);
+
+    if(!strcmp(opt.sort, "child"))
+        return list_sort(symlist, do_sort_by_ticks_kids);
+
+    return symlist;
+}
+
+DEFINE_SORT(callinfo_t, caller->name, do_sort_ci_by_name);
+DEFINE_SORT(callinfo_t, calls, do_sort_ci_by_calls);
+DEFINE_SORT(callinfo_t, ticks, do_sort_ci_by_ticks);
+
+
+static List *sort_callinfo(List *callers)
+{
+    if(!strcmp(opt.sort, "name"))
+	return list_sort(callers, do_sort_ci_by_name);
+
+    if(!strcmp(opt.sort, "calls"))
+	return list_sort(callers, do_sort_ci_by_calls);
+    
+    return list_sort(callers, do_sort_ci_by_ticks);
+}
+
+
+/*
+ * Display the flat profile
+ */
+void view_profile_flat(profile_t *prof)
+{
+    List *list, *e;
+    counter64_t total;
+    counter64_t current = 0;
+    counter64_t hz = prof->header->cpu_hz;
+
+    list = symtab_to_symlist(prof->symtab);
+    list = sort_symlist(list);
+
+    total = prof->total_ticks;
+
+    printf("\n\nFlat Profile:\n\n");
+
+    if(opt.debug)
+	{
+	    printf("CPU HZ: %llu\n", hz);
+	    printf("Total ticks: %llu  (%lf sec)\n", total, dsec(hz, total));
+	}
+
+    /* FIXME: add support for variable precision to other fields too */
+
+    printf("\n");
+    printf("%-*.*s cmul %% calls    self sec all sec  avg self avg all  function\n",
+	   opt.precision + 4, opt.precision + 4, "% time");
+    printf("%s ------ -------- -------- -------- -------- -------- --------\n", 
+	   repeat_char('-', opt.precision + 4));
+
+    for(e = list; e; e = e->next)
+	{
+	    symbol_t *sym = e->data;
+	    double percent, cmul;
+	    double sec_self, sec_all, sec_self_avg, sec_all_avg;
+
+	    if(sym->count == 0)
+		continue;
+	    if(symbol_is_ignored(sym))
+		continue;
+	    //printf("sym->ticks_self = %lf  total = %lf\n", (double)sym->ticks_self, (double)total);
+	    percent = (double)sym->ticks_self / (double)total * 100.0;
+
+	    current += sym->ticks_self;
+	    cmul = (double)current / (double)total * 100.0;
+
+	    sec_self = dsec(hz, sym->ticks_self);
+	    sec_all  = dsec(hz, sym->ticks_self + sym->ticks_kids);
+	    
+	    sec_self_avg = dsec(hz, sym->ticks_self / sym->count);
+	    sec_all_avg  = dsec(hz, (sym->ticks_self + sym->ticks_kids) / sym->count);
+
+
+	    printf("%*.*lf %6.2lf %8lu %8.3lf %8.3lf %8.3lf %8.3lf %s\n",
+		   opt.precision + 4, opt.precision, percent,
+		   cmul,
+		   sym->count,
+		   sec_self,
+		   sec_all,
+		   sec_self_avg,
+		   sec_all_avg,
+		   fullname(sym));
+
+	    if(opt.source && sym->source)
+		printf("- %s\n", sym->source);
+
+#if 0
+	    if(sym->callers)
+		{
+		    List *ce;
+		    int line = 0;
+		    for(ce = sym->callers; ce; ce = ce->next)
+			{
+			    callinfo_t *ci = ce->data;
+			    printf("%*s %8lu %s\n",
+				   opt.precision+4+6+1, "",
+				   ci->calls, 
+				   ci->caller->name);
+			    line++;
+			}
+		}
+#endif
+
+	}
+}
+
+/*
+ * Display the callers profile
+ */
+void view_profile_callers(profile_t *prof)
+{
+    List *list, *e;
+    counter64_t total;
+    counter64_t hz = wprof_get_hz(); //prof->header->cpu_hz;
+
+    list = symtab_to_symlist(prof->symtab);
+    list = sort_symlist(list);
+
+    total = prof->total_ticks;
+
+    printf("\n\nCallers Profile:\n\n");
+
+    for(e = list; e; e = e->next)
+	{
+            symbol_t *sym = e->data;
+
+	    if(sym->count == 0)
+		continue;
+	    
+	    printf("%s  (%lu call%s, %.3lf sec) called by:\n", 
+		   fullname(sym),
+		   sym->count,
+		   sym->count == 1 ? "" : "s",
+		   dsec(hz, sym->ticks_kids + sym->ticks_self)
+		   );
+	    
+	                                                          
+	    printf("-calls----------- -time-------- -caller-------------\n");
+	    if(sym->callers)
+		{
+		    List *ce;
+		    for(ce = sort_callinfo(sym->callers); ce; ce = ce->next)
+			{
+			    callinfo_t *ci = ce->data;
+			    symbol_t *caller = ci->caller;
+
+#if 1
+			    printf("    %8lu %3.0f%% %8.3lf %3.0f%% %s\n",
+				   ci->calls,
+				   100.0 * ci->calls / sym->count,
+				   dsec(hz, ci->ticks),
+				   100.0 * ci->ticks / (sym->ticks_kids + sym->ticks_self),
+				   caller->name);
+#else
+			    printf("    %s for %lu calls, %.3lf sec\n",
+				   fullname(caller),
+				   ci->calls,
+				   dsec(hz, ci->ticks)
+				   );
+#endif
+			}
+		}
+	    else
+		printf("                                <unknown>\n");
+
+	    printf("\n");
+
+	}
+}
+
+
+static void do_view_profile_calltree(arc_t *root, HashTable *symtab, int idx)
+{
+	arc_t *arc;
+
+	if(!root)
+		return;
+
+	for(arc = root->children; arc; arc = arc->next)
+        {
+		symbol_t *sym;
+		int i;
+
+		sym = hash_table_lookup(symtab, (void*)arc->addr);
+
+		for(i = 0; i < idx; i++)
+			printf("  ");
+
+		if(sym)
+			printf("%s  (%lu calls, %llu ticks)\n", fullname(sym), arc->count, arc->ticks);
+		else
+			printf("%x  (%lu calls, %llu ticks)\n", (unsigned int)arc->addr, arc->count, arc->ticks);
+
+		do_view_profile_calltree(arc, symtab, idx + 1);
+        }
+}
+
+/*
+ * Display the calltree
+ */
+void view_profile_calltree(profile_t *prof)
+{
+        printf("\n\nCalltree:\n\n");
+        do_view_profile_calltree(&prof->header->root[0], prof->symtab, 0);
+}
+
+
+
+
+/*--------------------------------------------------------*/
+// MAIN
+/*--------------------------------------------------------*/
+
+
+int main(int argc, char **argv)
+{
+    int option_index = 0;
+
+    while(1)
+	{
+	    int c = getopt_long (argc, argv, "h?i:s:",
+				 long_options, &option_index);
+	    if(c == -1)
+		break;
+
+	    switch(c)
+		{
+		case 0:
+		    break;
+
+		case 's':
+		    opt.sort = strdup(optarg);
+		    break;
+		    
+		case 'p':
+		    opt.precision = atoi(optarg);
+		    break;
+
+		case '?':
+		case 'h':
+		    usage();
+		    exit(0);
+		    break;
+		default:
+		    printf("Something is messed up ...\n");
+		    break;
+		}
+	}
+
+
+    if(optind >= argc)
+	{
+	    usage();
+	    printf("\nError: no files specified.\n");
+	    exit(1);
+	}
+
+    while(optind < argc)
+	{
+	    profile_t *prof;
+	    char *filename = argv[optind++];
+
+	    prof = profile_open(filename);
+	    if(!prof)
+		continue;
+
+	    if(opt.flat)
+		view_profile_flat(prof);
+
+	    if(opt.callers)
+		view_profile_callers(prof);
+
+	    if(opt.calltree)
+		view_profile_calltree(prof);
+
+	    profile_close(prof);
+	}
+
+    exit(0);
+}
+
+
+void usage()
+{
+	static const char usage_msg[] = "Usage: wprof [OPTION]... [FILE]...\n\n"
+		"  --flat            display flat profile\n"
+		"  --callers         display callers profile\n"
+		"  --calltree        display entire calltree (can be huge!)\n"
+		"! --merge           merge specified profile files\n"
+		"  --list-dso        list shared objects used by the program\n"
+		"  --dso-prefix      prefix symbols with the name of the object they reside in\n"
+/*		"  --ignore SYM      ignore symbol SYM (comma separated list)\n" */
+		"  --source          display source files and line numbers\n"
+		"  --compress        compress callers with the same name into a single entry\n"
+		"  --sort KEY        sort output by KEY (name, calls, ticks, self, child)\n"
+		"  --help            display this help text\n\n"
+		"Most options can be prefixed with 'no' to disable it, e.g. --no-flat\n";
+	
+	printf("%s", usage_msg);
+}
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ tools/wineprof/wineprof.h	2003-10-30 20:07:48.000000000 +0900
@@ -0,0 +1,56 @@
+/* Wine Profiler
+ *
+ * Copyright 2000-2003 Codeweavers, Charles Loep
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __WINEPROF_H__
+#define __WINEPROF_H__
+
+struct callinfo_s;
+struct symbol_s;
+struct profile_s;
+
+
+typedef struct callinfo_s {
+	counter32_t      calls;
+	counter64_t      ticks;
+	struct symbol_s *caller;
+} callinfo_t;
+
+
+typedef struct symbol_s {
+	char         *name;
+	char         *soname;
+	char         *source;
+	unsigned int  addr;
+	counter32_t   count;
+	counter64_t   ticks;
+	counter64_t   ticks_self;
+	counter64_t   ticks_kids;
+	List         *callers;
+} symbol_t;
+
+typedef struct profile_s {
+	int             fd;
+	wprof_header_t *header;
+	HashTable      *symtab;
+	List           *symlist;
+	counter64_t     total_ticks;
+} profile_t;
+
+
+#endif /*__WINEPROF_H__*/
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ tools/wineprof/hashlist.c	2003-10-30 20:07:48.000000000 +0900
@@ -0,0 +1,246 @@
+/* Wine Profiler
+ *
+ * Copyright 2000-2003 Codeweavers, Charles Loep
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdlib.h>
+#include "hashlist.h"
+
+
+#define NEW0(TYPE,N)   ((TYPE *)calloc((N), sizeof(TYPE)))
+#define NEW(TYPE)      ((TYPE *)malloc(sizeof(TYPE)))
+
+
+/* basic linked list */
+
+
+/*
+ * prepend VALUE to LIST, returns the new list
+ */
+List *list_prepend(List *list, void *value)
+{
+	List *e = NEW0(List, 1);
+	e->next = list;
+	e->data = value;
+	if(list)
+		list->prev = e;
+	return e;
+}
+
+/*
+ * append VALUE to LIST, returns the new list
+ */
+List *list_append(List *list, void *value)
+{
+	List *e = list;
+	if(!e) {
+		return list_prepend(list, value);
+	}
+
+	while(1) {
+		if(!e->next) {
+			e->next = NEW0(List, 1);
+			e->next->prev = e;
+			e->next->data = value;
+			return list;
+		}
+		e = e->next;
+	}
+
+	/* not reached */
+	return 0;
+}
+
+/*
+ * find VALUE in LIST using FUNC to compare elements
+ */
+List *list_find_custom(List *list, void *value, CompareFunc func)
+{
+	List *e = list;
+	while(e) {
+		if(func(value, e->data) == 0)
+			return e;
+	}
+	return 0;
+}
+
+/*
+ * quick & dirty linked list qsort, probably not the most efficient, but it appears to work
+ */
+static void llswap(List *a, List *b)
+{
+	void *tmp = a->data;
+	a->data = b->data;
+	b->data = tmp;
+}
+
+static void llqsort(List *l, List *r, CompareFunc func)
+{
+	List *ll, *lx;
+
+	if(l == r)
+		return;
+	
+	lx = l;
+	for(ll = l->next; ll != r; ll = ll->next) {
+		if(func(ll->data, l->data) < 0) {
+                        lx = lx->next;
+			llswap(ll, lx);
+		}
+	}
+
+	llswap(l, lx);
+
+	llqsort(l, lx, func);
+	llqsort(lx->next, r, func);
+}
+
+/*
+ * sort LIST using FUNC to compare elements (like strcmp)
+ */
+List *list_sort(List *list, CompareFunc func)
+{
+	llqsort(list, 0, func);
+	return list;
+}
+
+
+
+
+
+/* basic hash table */
+
+#define HASH_PRIME 1973
+
+
+unsigned int direct_hash(void *element)
+{
+	return ((unsigned int)element);
+}
+
+int direct_equal(void *a, void *b)
+{
+	return (int)b - (int)a;
+}
+
+
+
+/*
+ * allocate a new hash table
+ */
+HashTable *hash_table_new(HashFunc hfun, CompareFunc comp)
+{
+	HashTable *hash = NEW0(HashTable, 1);
+	hash->hashfunc = hfun;
+	hash->compfunc = comp;
+	hash->entries  = NEW0(HashEntry *, HASH_PRIME);
+
+	return hash;
+}
+
+/*
+ * return VALUE of KEY, or 0 if not found
+ */
+void *hash_table_lookup(HashTable *hash, void *key)
+{
+        unsigned int h = hash->hashfunc(key) % HASH_PRIME;
+	HashEntry *e = hash->entries[h];
+	while(e) {
+		if(hash->compfunc(e->key, key) == 0)
+			return e->value;
+		e = e->next;
+	}
+	return 0;
+}
+
+/*
+ * insert VALUE with KEY into HASH
+ */
+void hash_table_insert(HashTable *hash, void *key, void *value)
+{
+	unsigned int h = hash->hashfunc(key) % HASH_PRIME;
+	HashEntry *e = NEW0(HashEntry, 1);
+	e->key = key;
+	e->value = value;
+	e->next = hash->entries[h];
+	hash->entries[h] = e;
+}
+
+/* execute FUNC for every element in HASH */
+void hash_table_foreach(HashTable *hash, HFunc func, void *info)
+{
+	int i;
+	for(i = 0; i < HASH_PRIME; i++) {
+		HashEntry *e = hash->entries[i];
+		while(e) {
+			func(e->key, e->value, info);
+			e = e->next;
+		}
+	}
+}
+
+
+
+
+#ifdef TEST /* this can be removed when no longer needed */
+#include <stdio.h>
+#include <string.h>
+
+void dump_hash(char *key, char *value, void *arg)
+{
+	printf("key=%s  value=%s\n", key, value);
+}
+
+int main(int argc, char **argv)
+{
+	HashTable *hash;
+	List *list = 0;
+	List *e;
+
+        list = list_append(list, "zzz");
+	list = list_append(list, "foo");
+	list = list_append(list, "bar");
+        list = list_append(list, "baz");
+        list = list_append(list, "qblah");
+        list = list_append(list, "foobar");
+        list = list_append(list, "hello");
+        list = list_append(list, "charles");
+	list = list_append(list, "12345");
+        list = list_append(list, "abc");
+        list = list_append(list, "a should come first");
+
+	list = list_sort(list, (CompareFunc)strcmp);
+
+	for(e = list; e != 0; e = e->next) {
+		printf("%s\n", e->data);
+	}
+
+
+	hash = hash_table_new(direct_hash, direct_equal);
+	hash_table_insert(hash, "foo", "bar");
+	hash_table_insert(hash, "blah", "foobar");
+        hash_table_insert(hash, "blah1", "foobar");
+        hash_table_insert(hash, "blah2", "foobaz");
+        hash_table_insert(hash, "blah3", "fooblah");
+
+	printf("foo=%s\n", hash_table_lookup(hash, "foo"));
+        printf("blah2=%s\n", hash_table_lookup(hash, "blah2"));
+
+	hash_table_foreach(hash, (HFunc)dump_hash, 0);
+}
+#endif
+
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ tools/wineprof/hashlist.h	2003-10-30 20:07:48.000000000 +0900
@@ -0,0 +1,70 @@
+/* Wine Profiler
+ *
+ * Copyright 2000-2003 Codeweavers, Charles Loep
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __hashlist_h__
+#define __hashlist_h__
+
+/* basic linked list */
+
+typedef struct _List List;
+struct _List {
+	void *data;
+	List *prev;
+	List *next;
+};
+
+typedef int (*CompareFunc)(void *a, void *b);
+
+List *list_prepend(List *list, void *value);
+List *list_append(List *list, void *value);
+List *list_find_custom(List *list, void *value, CompareFunc func);
+List *list_sort(List *list, CompareFunc func);
+
+
+
+/* basic hashtable */
+
+typedef unsigned int (*HashFunc)(void *element);
+typedef void (*HFunc)(void *key, void *value, void *data);
+
+
+typedef struct _HashEntry HashEntry;
+struct _HashEntry {
+	void *key;
+	void *value;
+	HashEntry *next;
+};
+
+typedef struct _HashTable HashTable;
+struct _HashTable {
+	HashFunc hashfunc;
+	CompareFunc compfunc;
+	HashEntry **entries;
+};
+
+unsigned int direct_hash(void *element);
+int direct_equal(void *a, void *b);
+
+HashTable *hash_table_new(HashFunc hash, CompareFunc comp);
+void *hash_table_lookup(HashTable *hash, void *name);
+void hash_table_insert(HashTable *hash, void *name, void *value);
+void hash_table_foreach(HashTable *hash, HFunc func, void *info);
+
+
+#endif /*__hashlist_h__*/


More information about the wine-devel mailing list