Profiler patch

Mike McCormack mike at codeweavers.com
Fri Oct 10 12:54:23 CDT 2003


Hi,

This is my latest attempt at merging Codeweavers' profiler (written by 
Charles Loep and merged into wine by myself).  This patch removes the 
gtk dependency from the profiler.

To test it out, compile wine like this:

./configure --with-profiler
make depend
make

When running wine, profiles will be created in /tmp.  These profiles can 
be analysed using the wineprof tool from wine/tools/wineprof.

Mike


ChangeLog:
Charles Loep <charles at codeweavers.com>
Mike McCormack <mike at codeweavers.com>
* add a profiler to wine

-------------- next part --------------
Index: configure.ac
===================================================================
RCS file: /home/wine/wine/configure.ac,v
retrieving revision 1.185
diff -u -r1.185 configure.ac
--- configure.ac	8 Oct 2003 04:28:08 -0000	1.185
+++ configure.ac	9 Oct 2003 04:19:32 -0000
@@ -20,6 +20,18 @@
 AC_ARG_WITH(curses,    AC_HELP_STRING([--without-curses],[do not use curses]))
 AC_ARG_WITH(nptl,      AC_HELP_STRING([--with-nptl],[use glibc NPTL threading support]))
 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 -DWINE_NO_PROFILE=\"__attribute__((no_instrument_function))\""
+  PROFILE_LIBS="-L\$(TOPOBJDIR)/libs/wprof -lwineprof -lm"
+  PROFILE_LIBNAME="libwineprof"
+  AC_DEFINE(HAVE_PROFILER,1,[Define to use glibc NPTL threading support.])
+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)")
@@ -1548,6 +1560,7 @@
 libs/uuid/Makefile
 libs/wine/Makefile
 libs/wpp/Makefile
+libs/wprof/Makefile
 miscemu/Makefile
 programs/Makefile
 programs/avitools/Makefile
@@ -1586,6 +1599,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.162
diff -u -r1.162 Make.rules.in
--- Make.rules.in	3 Oct 2003 05:01:34 -0000	1.162
+++ Make.rules.in	9 Oct 2003 04:19:32 -0000
@@ -54,7 +54,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: dlls/Makedll.rules.in
===================================================================
RCS file: /home/wine/wine/dlls/Makedll.rules.in,v
retrieving revision 1.58
diff -u -r1.58 Makedll.rules.in
--- dlls/Makedll.rules.in	24 Sep 2003 19:01:44 -0000	1.58
+++ dlls/Makedll.rules.in	9 Oct 2003 04:19:32 -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	9 Oct 2003 04:19:33 -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: miscemu/Makefile.in
===================================================================
RCS file: /home/wine/wine/miscemu/Makefile.in,v
retrieving revision 1.21
diff -u -r1.21 Makefile.in
--- miscemu/Makefile.in	22 May 2003 03:40:41 -0000	1.21
+++ miscemu/Makefile.in	9 Oct 2003 04:19:33 -0000
@@ -11,10 +11,13 @@
 
 @MAKE_RULES@
 
+PROFILE_CFLAGS = @PROFILE_CFLAGS@
+PROFILE_LIBS = @PROFILE_LIBS@
+
 LDEXECFLAGS = @LDEXECFLAGS@
 
 $(MODULE): $(OBJS) Makefile.in $(DLLDIR)/libntdll.dll.$(LIBEXT)
-	$(CC) -o $@ $(LDEXECFLAGS) $(OBJS) -L$(DLLDIR) -lntdll.dll $(LIBWINE) $(LIBUNICODE) $(LIBPORT) $(LDFLAGS)
+	$(CC) -o $@ $(LDEXECFLAGS) $(OBJS) -L$(DLLDIR) -lntdll.dll $(LIBWINE) $(LIBUNICODE) $(LIBPORT) $(LDFLAGS) $(PROFILE_LIBS)
 
 install:: $(MODULE)
 	$(MKINSTALLDIRS) $(bindir)
Index: programs/Makeprog.rules.in
===================================================================
RCS file: /home/wine/wine/programs/Makeprog.rules.in,v
retrieving revision 1.30
diff -u -r1.30 Makeprog.rules.in
--- programs/Makeprog.rules.in	24 Sep 2003 19:01:44 -0000	1.30
+++ programs/Makeprog.rules.in	9 Oct 2003 04:19:33 -0000
@@ -9,11 +9,14 @@
 # plus all variables required by the global Make.rules.in
 #
 
+PROFILE_LIBS   = @PROFILE_LIBS@
+PROFILE_CFLAGS = @PROFILE_CFLAGS@
+
 DEFS        = $(EXTRADEFS)
 DLLFLAGS    = @DLLFLAGS@
 LDDLLFLAGS  = @LDDLLFLAGS@
 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.90
diff -u -r1.90 Makefile.in
--- dlls/ntdll/Makefile.in	8 Oct 2003 04:24:50 -0000	1.90
+++ dlls/ntdll/Makefile.in	9 Oct 2003 04:19:33 -0000
@@ -54,6 +54,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-04 05:49:25.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-05-30 17:19:45.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-04 03:25:07.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-04 03:34:58.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-04 03:35:07.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-04 06:05:22.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-04 06:05:45.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-patches mailing list