Profiler (take 3)

Mike McCormack mike at codeweavers.com
Thu Aug 14 22:15:52 CDT 2003


Hi,

This patch adds a simple profiler to Wine.  Charles has added some fixes 
since I last posted this, so it should work properly now.

Profiles are generated in /tmp/wmon.out.<pid>, and can be examined with 
the wprof tool from wine/libs/wprof/wprof.

I haven't tested this, but it is working in the Crossover tree, where I 
merged it from. Let me know of any success or failures.

Mike


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

-------------- next part --------------
Index: configure.ac
===================================================================
RCS file: /home/wine/wine/configure.ac,v
retrieving revision 1.171
diff -u -r1.171 configure.ac
--- configure.ac	13 Aug 2003 01:27:48 -0000	1.171
+++ configure.ac	15 Aug 2003 02:49:30 -0000
@@ -20,6 +20,25 @@
 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
+  GLIBC_THREAD_CFLAGS=`glib-config --cflags gthread`
+  GLIBC_THREAD_LIBS=`glib-config --libs gthread`
+  if test "x$GLIBC_THREAD_CFLAGS" != x
+  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
+fi
+AC_SUBST(PROFILE_CFLAGS)
+AC_SUBST(PROFILE_LIBS)
+AC_SUBST(PROFILE_LIBNAME)
+AC_SUBST(GLIBC_THREAD_CFLAGS)
+AC_SUBST(GLIBC_THREAD_LIBS)
 
 AC_SUBST(WIN16_FILES,"\$(WIN16_FILES)")
 AC_SUBST(WIN16_INSTALL,"\$(WIN16_INSTALL)")
@@ -1543,6 +1564,7 @@
 libs/uuid/Makefile
 libs/wine/Makefile
 libs/wpp/Makefile
+libs/wprof/Makefile
 miscemu/Makefile
 programs/Makefile
 programs/avitools/Makefile
Index: Make.rules.in
===================================================================
RCS file: /home/wine/wine/Make.rules.in,v
retrieving revision 1.159
diff -u -r1.159 Make.rules.in
--- Make.rules.in	21 Jul 2003 22:01:07 -0000	1.159
+++ Make.rules.in	15 Aug 2003 02:49:30 -0000
@@ -54,7 +54,7 @@
 LINTFLAGS = @LINTFLAGS@
 INCLUDES     = -I$(SRCDIR) -I. -I$(TOPSRCDIR)/include -I$(TOPOBJDIR)/include $(EXTRAINCL)
 EXTRACFLAGS  = @EXTRACFLAGS@
-ALLCFLAGS    = $(INCLUDES) $(DEFS) $(EXTRACFLAGS) $(CPPFLAGS) $(CFLAGS)
+ALLCFLAGS    = $(INCLUDES) $(DEFS) $(EXTRACFLAGS) $(CPPFLAGS) $(CFLAGS) $(PROFILE_CFLAGS)
 ALLLINTFLAGS = $(INCLUDES) $(DEFS) $(LINTFLAGS)
 MKINSTALLDIRS= $(TOPSRCDIR)/tools/mkinstalldirs -m 755
 WINAPI_CHECK = $(TOPSRCDIR)/tools/winapi_check/winapi_check
Index: dlls/Makedll.rules.in
===================================================================
RCS file: /home/wine/wine/dlls/Makedll.rules.in,v
retrieving revision 1.56
diff -u -r1.56 Makedll.rules.in
--- dlls/Makedll.rules.in	13 Jun 2003 23:26:02 -0000	1.56
+++ dlls/Makedll.rules.in	15 Aug 2003 02:49:30 -0000
@@ -9,13 +9,16 @@
 # plus all variables required by the global Make.rules.in
 #
 
+PROFILE_LIBS    = @PROFILE_LIBS@
+PROFILE_CFLAGS  = @PROFILE_CFLAGS@
+
 DEFS        = @DLLFLAGS@ -D__WINESRC__ $(EXTRADEFS)
 DLLEXT      = @DLLEXT@
 MAINSPEC    = $(MODULE:%.dll=%).spec
 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	15 Aug 2003 02:49:30 -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	15 Aug 2003 02:49:30 -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.29
diff -u -r1.29 Makeprog.rules.in
--- programs/Makeprog.rules.in	13 Jun 2003 23:26:01 -0000	1.29
+++ programs/Makeprog.rules.in	15 Aug 2003 02:49:31 -0000
@@ -9,10 +9,13 @@
 # plus all variables required by the global Make.rules.in
 #
 
+PROFILE_LIBS   = @PROFILE_LIBS@
+PROFILE_CFLAGS = @PROFILE_CFLAGS@
+
 DEFS        = @DLLFLAGS@ $(EXTRADEFS)
 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: scheduler/pthread.c
===================================================================
RCS file: /home/wine/wine/scheduler/pthread.c,v
retrieving revision 1.35
diff -u -r1.35 pthread.c
--- scheduler/pthread.c	12 Aug 2003 23:50:55 -0000	1.35
+++ scheduler/pthread.c	15 Aug 2003 02:49:31 -0000
@@ -60,6 +60,10 @@
 
 #define PSTR(str) __ASM_NAME(#str)
 
+#ifndef WINE_NO_PROFILE
+#define WINE_NO_PROFILE
+#endif
+
 /* adapt as necessary (a construct like this is used in glibc sources) */
 #define strong_alias(orig, alias) \
  asm(".globl " PSTR(alias) "\n" \
@@ -136,14 +140,14 @@
 static int *(*libc_pthread_init)( const struct pthread_functions *funcs );
 static int *libc_multiple_threads;
 
-void PTHREAD_init_done(void)
+WINE_NO_PROFILE void PTHREAD_init_done(void)
 {
     init_done = 1;
     if (!libc_fork) libc_fork = dlsym( RTLD_NEXT, "fork" );
     if (!libc_sigaction) libc_sigaction = dlsym( RTLD_NEXT, "sigaction" );
 }
 
-void PTHREAD_init_thread(void)
+WINE_NO_PROFILE void PTHREAD_init_thread(void)
 {
     static int first = 1;
 
@@ -165,7 +169,7 @@
 }
 
 /* redefine this to prevent libpthread from overriding our function pointers */
-int *__libc_pthread_init( const struct pthread_functions *funcs )
+WINE_NO_PROFILE int *__libc_pthread_init( const struct pthread_functions *funcs )
 {
     return libc_multiple_threads;
 }
@@ -196,11 +200,11 @@
   void *arg;
 } *wine_cleanup;
 
-void __pthread_initialize(void)
+WINE_NO_PROFILE void __pthread_initialize(void)
 {
 }
 
-pthread_descr __pthread_thread_self(void)
+WINE_NO_PROFILE pthread_descr __pthread_thread_self(void)
 {
     struct pthread_descr_struct *descr = NtCurrentTeb()->pthread_data;
     if (!descr) return &initial_descr;
@@ -213,14 +217,14 @@
 	 void* arg;
 };
 
-static DWORD CALLBACK pthread_thread_start(LPVOID data)
+WINE_NO_PROFILE static DWORD CALLBACK pthread_thread_start(LPVOID data)
 {
   struct pthread_thread_init init = *(struct pthread_thread_init*)data;
   HeapFree(GetProcessHeap(),0,data);
   return (DWORD)init.start_routine(init.arg);
 }
 
-int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void*
+WINE_NO_PROFILE int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void*
         (*start_routine)(void *), void* arg)
 {
   HANDLE hThread;
@@ -243,7 +247,7 @@
   return 0;
 }
 
-int pthread_cancel(pthread_t thread)
+WINE_NO_PROFILE int pthread_cancel(pthread_t thread)
 {
   HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD)thread);
 
@@ -258,7 +262,7 @@
   return 0;             /* return success */
 }
 
-int pthread_join(pthread_t thread, void **value_ptr)
+WINE_NO_PROFILE int pthread_join(pthread_t thread, void **value_ptr)
 {
   HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD)thread);
 
@@ -274,7 +278,7 @@
 }
 
 /*FIXME: not sure what to do with this one... */
-int pthread_detach(pthread_t thread)
+WINE_NO_PROFILE int pthread_detach(pthread_t thread)
 {
   P_OUTPUT("FIXME:pthread_detach\n");
   return 0;
@@ -282,28 +286,28 @@
 
 /* FIXME: we have no equivalents in win32 for the policys */
 /* so just keep this as a stub */
-int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy)
+WINE_NO_PROFILE int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy)
 {
   P_OUTPUT("FIXME:pthread_attr_setschedpolicy\n");
   return 0;
 }
 
 /* FIXME: no win32 equivalent for scope */
-int pthread_attr_setscope(pthread_attr_t *attr, int scope)
+WINE_NO_PROFILE int pthread_attr_setscope(pthread_attr_t *attr, int scope)
 {
   P_OUTPUT("FIXME:pthread_attr_setscope\n");
   return 0; /* return success */
 }
 
 /* FIXME: no win32 equivalent for schedule param */
-int pthread_attr_setschedparam(pthread_attr_t *attr,
+WINE_NO_PROFILE int pthread_attr_setschedparam(pthread_attr_t *attr,
     const struct sched_param *param)
 {
   P_OUTPUT("FIXME:pthread_attr_setschedparam\n");
   return 0; /* return success */
 }
 
-int __pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
+WINE_NO_PROFILE int __pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
 {
   static pthread_once_t the_once = PTHREAD_ONCE_INIT;
   LONG once_now;
@@ -315,7 +319,7 @@
 }
 strong_alias(__pthread_once, pthread_once);
 
-void __pthread_kill_other_threads_np(void)
+WINE_NO_PROFILE void __pthread_kill_other_threads_np(void)
 {
     /* we don't need to do anything here */
 }
@@ -340,7 +344,7 @@
 static atfork_handler atfork_child[MAX_ATFORK];
 static int atfork_count;
 
-int __pthread_atfork(void (*prepare)(void),
+WINE_NO_PROFILE int __pthread_atfork(void (*prepare)(void),
 		     void (*parent)(void),
 		     void (*child)(void))
 {
@@ -355,7 +359,7 @@
 }
 strong_alias(__pthread_atfork, pthread_atfork);
 
-pid_t __fork(void)
+WINE_NO_PROFILE pid_t __fork(void)
 {
     pid_t pid;
     int i;
@@ -384,7 +388,7 @@
 
 /***** MUTEXES *****/
 
-int __pthread_mutex_init(pthread_mutex_t *mutex,
+WINE_NO_PROFILE int __pthread_mutex_init(pthread_mutex_t *mutex,
                         const pthread_mutexattr_t *mutexattr)
 {
   /* glibc has a tendency to initialize mutexes very often, even
@@ -398,7 +402,7 @@
 }
 strong_alias(__pthread_mutex_init, pthread_mutex_init);
 
-static void mutex_real_init( pthread_mutex_t *mutex )
+WINE_NO_PROFILE static void mutex_real_init( pthread_mutex_t *mutex )
 {
   CRITICAL_SECTION *critsect = HeapAlloc(GetProcessHeap(), 0, sizeof(CRITICAL_SECTION));
   RtlInitializeCriticalSection(critsect);
@@ -410,7 +414,7 @@
   }
 }
 
-int __pthread_mutex_lock(pthread_mutex_t *mutex)
+WINE_NO_PROFILE int __pthread_mutex_lock(pthread_mutex_t *mutex)
 {
   if (!init_done) return 0;
   if (!((wine_mutex)mutex)->critsect)
@@ -421,7 +425,7 @@
 }
 strong_alias(__pthread_mutex_lock, pthread_mutex_lock);
 
-int __pthread_mutex_trylock(pthread_mutex_t *mutex)
+WINE_NO_PROFILE int __pthread_mutex_trylock(pthread_mutex_t *mutex)
 {
   if (!init_done) return 0;
   if (!((wine_mutex)mutex)->critsect)
@@ -435,7 +439,7 @@
 }
 strong_alias(__pthread_mutex_trylock, pthread_mutex_trylock);
 
-int __pthread_mutex_unlock(pthread_mutex_t *mutex)
+WINE_NO_PROFILE int __pthread_mutex_unlock(pthread_mutex_t *mutex)
 {
   if (!((wine_mutex)mutex)->critsect) return 0;
   RtlLeaveCriticalSection(((wine_mutex)mutex)->critsect);
@@ -443,7 +447,7 @@
 }
 strong_alias(__pthread_mutex_unlock, pthread_mutex_unlock);
 
-int __pthread_mutex_destroy(pthread_mutex_t *mutex)
+WINE_NO_PROFILE int __pthread_mutex_destroy(pthread_mutex_t *mutex)
 {
   if (!((wine_mutex)mutex)->critsect) return 0;
   if (((wine_mutex)mutex)->critsect->RecursionCount) {
@@ -465,38 +469,38 @@
 /***** MUTEX ATTRIBUTES *****/
 /* just dummies, since critical sections are always recursive */
 
-int __pthread_mutexattr_init(pthread_mutexattr_t *attr)
+WINE_NO_PROFILE int __pthread_mutexattr_init(pthread_mutexattr_t *attr)
 {
   return 0;
 }
 strong_alias(__pthread_mutexattr_init, pthread_mutexattr_init);
 
-int __pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
+WINE_NO_PROFILE int __pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
 {
   return 0;
 }
 strong_alias(__pthread_mutexattr_destroy, pthread_mutexattr_destroy);
 
-int __pthread_mutexattr_setkind_np(pthread_mutexattr_t *attr, int kind)
+WINE_NO_PROFILE int __pthread_mutexattr_setkind_np(pthread_mutexattr_t *attr, int kind)
 {
   return 0;
 }
 strong_alias(__pthread_mutexattr_setkind_np, pthread_mutexattr_setkind_np);
 
-int __pthread_mutexattr_getkind_np(pthread_mutexattr_t *attr, int *kind)
+WINE_NO_PROFILE int __pthread_mutexattr_getkind_np(pthread_mutexattr_t *attr, int *kind)
 {
   *kind = PTHREAD_MUTEX_RECURSIVE;
   return 0;
 }
 strong_alias(__pthread_mutexattr_getkind_np, pthread_mutexattr_getkind_np);
 
-int __pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind)
+WINE_NO_PROFILE int __pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind)
 {
   return 0;
 }
 strong_alias(__pthread_mutexattr_settype, pthread_mutexattr_settype);
 
-int __pthread_mutexattr_gettype(pthread_mutexattr_t *attr, int *kind)
+WINE_NO_PROFILE int __pthread_mutexattr_gettype(pthread_mutexattr_t *attr, int *kind)
 {
   *kind = PTHREAD_MUTEX_RECURSIVE;
   return 0;
@@ -506,7 +510,7 @@
 
 /***** THREAD-SPECIFIC VARIABLES (KEYS) *****/
 
-int __pthread_key_create(pthread_key_t *key, void (*destr_function)(void *))
+WINE_NO_PROFILE int __pthread_key_create(pthread_key_t *key, void (*destr_function)(void *))
 {
   static LONG keycnt = FIRST_KEY;
   *key = InterlockedExchangeAdd(&keycnt, 1);
@@ -514,13 +518,13 @@
 }
 strong_alias(__pthread_key_create, pthread_key_create);
 
-int __pthread_key_delete(pthread_key_t key)
+WINE_NO_PROFILE int __pthread_key_delete(pthread_key_t key)
 {
   return 0;
 }
 strong_alias(__pthread_key_delete, pthread_key_delete);
 
-int __pthread_setspecific(pthread_key_t key, const void *pointer)
+WINE_NO_PROFILE int __pthread_setspecific(pthread_key_t key, const void *pointer)
 {
     pthread_descr descr = __pthread_thread_self();
     descr->key_data[key] = pointer;
@@ -528,7 +532,7 @@
 }
 strong_alias(__pthread_setspecific, pthread_setspecific);
 
-void *__pthread_getspecific(pthread_key_t key)
+WINE_NO_PROFILE void *__pthread_getspecific(pthread_key_t key)
 {
     pthread_descr descr = __pthread_thread_self();
     return (void *)descr->key_data[key];
@@ -537,20 +541,20 @@
 
 /* these are not exported, they are only used in the pthread_functions structure */
 
-static int pthread_internal_tsd_set( int key, const void *pointer )
+WINE_NO_PROFILE static int pthread_internal_tsd_set( int key, const void *pointer )
 {
     pthread_descr descr = __pthread_thread_self();
     descr->tsd_data[key] = pointer;
     return 0;
 }
 
-static void *pthread_internal_tsd_get( int key )
+WINE_NO_PROFILE static void *pthread_internal_tsd_get( int key )
 {
     pthread_descr descr = __pthread_thread_self();
     return (void *)descr->tsd_data[key];
 }
 
-static void ** __attribute__((const)) pthread_internal_tsd_address( int key )
+WINE_NO_PROFILE static void ** __attribute__((const)) pthread_internal_tsd_address( int key )
 {
     pthread_descr descr = __pthread_thread_self();
     return (void **)&descr->tsd_data[key];
@@ -560,28 +564,28 @@
 /***** "EXCEPTION" FRAMES *****/
 /* not implemented right now */
 
-void _pthread_cleanup_push(struct _pthread_cleanup_buffer *buffer, void (*routine)(void *), void *arg)
+WINE_NO_PROFILE void _pthread_cleanup_push(struct _pthread_cleanup_buffer *buffer, void (*routine)(void *), void *arg)
 {
   ((wine_cleanup)buffer)->routine = routine;
   ((wine_cleanup)buffer)->arg = arg;
 }
 
-void _pthread_cleanup_pop(struct _pthread_cleanup_buffer *buffer, int execute)
+WINE_NO_PROFILE void _pthread_cleanup_pop(struct _pthread_cleanup_buffer *buffer, int execute)
 {
   if (execute) (*(((wine_cleanup)buffer)->routine))(((wine_cleanup)buffer)->arg);
 }
 
-void _pthread_cleanup_push_defer(struct _pthread_cleanup_buffer *buffer, void (*routine)(void *), void *arg)
+WINE_NO_PROFILE void _pthread_cleanup_push_defer(struct _pthread_cleanup_buffer *buffer, void (*routine)(void *), void *arg)
 {
   _pthread_cleanup_push(buffer, routine, arg);
 }
 
-void _pthread_cleanup_pop_restore(struct _pthread_cleanup_buffer *buffer, int execute)
+WINE_NO_PROFILE void _pthread_cleanup_pop_restore(struct _pthread_cleanup_buffer *buffer, int execute)
 {
   _pthread_cleanup_pop(buffer, execute);
 }
 
-void __pthread_cleanup_upto(jmp_buf target, char *frame)
+WINE_NO_PROFILE void __pthread_cleanup_upto(jmp_buf target, char *frame)
 {
     /* FIXME */
 }
@@ -589,42 +593,42 @@
 /***** CONDITIONS *****/
 /* not implemented right now */
 
-int __pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
+WINE_NO_PROFILE int __pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
 {
   P_OUTPUT("FIXME:pthread_cond_init\n");
   return 0;
 }
 strong_alias(__pthread_cond_init, pthread_cond_init);
 
-int __pthread_cond_destroy(pthread_cond_t *cond)
+WINE_NO_PROFILE int __pthread_cond_destroy(pthread_cond_t *cond)
 {
   P_OUTPUT("FIXME:pthread_cond_destroy\n");
   return 0;
 }
 strong_alias(__pthread_cond_destroy, pthread_cond_destroy);
 
-int __pthread_cond_signal(pthread_cond_t *cond)
+WINE_NO_PROFILE int __pthread_cond_signal(pthread_cond_t *cond)
 {
   P_OUTPUT("FIXME:pthread_cond_signal\n");
   return 0;
 }
 strong_alias(__pthread_cond_signal, pthread_cond_signal);
 
-int __pthread_cond_broadcast(pthread_cond_t *cond)
+WINE_NO_PROFILE int __pthread_cond_broadcast(pthread_cond_t *cond)
 {
   P_OUTPUT("FIXME:pthread_cond_broadcast\n");
   return 0;
 }
 strong_alias(__pthread_cond_broadcast, pthread_cond_broadcast);
 
-int __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
+WINE_NO_PROFILE int __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
 {
   P_OUTPUT("FIXME:pthread_cond_wait\n");
   return 0;
 }
 strong_alias(__pthread_cond_wait, pthread_cond_wait);
 
-int __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
+WINE_NO_PROFILE int __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
 {
   P_OUTPUT("FIXME:pthread_cond_timedwait\n");
   return 0;
@@ -634,12 +638,12 @@
 /**** CONDITION ATTRIBUTES *****/
 /* not implemented right now */
 
-int pthread_condattr_init(pthread_condattr_t *attr)
+WINE_NO_PROFILE int pthread_condattr_init(pthread_condattr_t *attr)
 {
   return 0;
 }
 
-int pthread_condattr_destroy(pthread_condattr_t *attr)
+WINE_NO_PROFILE int pthread_condattr_destroy(pthread_condattr_t *attr)
 {
   return 0;
 }
@@ -647,7 +651,7 @@
 #if (__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 2)
 /***** READ-WRITE LOCKS *****/
 
-static void rwlock_real_init(pthread_rwlock_t *rwlock)
+WINE_NO_PROFILE static void rwlock_real_init(pthread_rwlock_t *rwlock)
 {
   RTL_RWLOCK *lock = HeapAlloc(GetProcessHeap(), 0, sizeof(RTL_RWLOCK));
   RtlInitializeResource(lock);
@@ -659,14 +663,14 @@
   }
 }
 
-int __pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *rwlock_attr)
+WINE_NO_PROFILE int __pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *rwlock_attr)
 {
   ((wine_rwlock)rwlock)->lock = NULL;
   return 0;
 }
 strong_alias(__pthread_rwlock_init, pthread_rwlock_init);
 
-int __pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
+WINE_NO_PROFILE int __pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
 {
   if (!((wine_rwlock)rwlock)->lock) return 0;
   RtlDeleteResource(((wine_rwlock)rwlock)->lock);
@@ -675,7 +679,7 @@
 }
 strong_alias(__pthread_rwlock_destroy, pthread_rwlock_destroy);
 
-int __pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
+WINE_NO_PROFILE int __pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
 {
   if (!init_done) return 0;
   if (!((wine_rwlock)rwlock)->lock)
@@ -687,7 +691,7 @@
 }
 strong_alias(__pthread_rwlock_rdlock, pthread_rwlock_rdlock);
 
-int __pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)
+WINE_NO_PROFILE int __pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)
 {
   if (!init_done) return 0;
   if (!((wine_rwlock)rwlock)->lock)
@@ -701,7 +705,7 @@
 }
 strong_alias(__pthread_rwlock_tryrdlock, pthread_rwlock_tryrdlock);
 
-int __pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
+WINE_NO_PROFILE int __pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
 {
   if (!init_done) return 0;
   if (!((wine_rwlock)rwlock)->lock)
@@ -713,7 +717,7 @@
 }
 strong_alias(__pthread_rwlock_wrlock, pthread_rwlock_wrlock);
 
-int __pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)
+WINE_NO_PROFILE int __pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)
 {
   if (!init_done) return 0;
   if (!((wine_rwlock)rwlock)->lock)
@@ -727,7 +731,7 @@
 }
 strong_alias(__pthread_rwlock_trywrlock, pthread_rwlock_trywrlock);
 
-int __pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
+WINE_NO_PROFILE int __pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
 {
   if (!((wine_rwlock)rwlock)->lock) return 0;
   RtlReleaseResource( ((wine_rwlock)rwlock)->lock );
@@ -738,24 +742,24 @@
 /**** READ-WRITE LOCK ATTRIBUTES *****/
 /* not implemented right now */
 
-int pthread_rwlockattr_init(pthread_rwlockattr_t *attr)
+WINE_NO_PROFILE int pthread_rwlockattr_init(pthread_rwlockattr_t *attr)
 {
   return 0;
 }
 
-int __pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr)
+WINE_NO_PROFILE int __pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr)
 {
   return 0;
 }
 strong_alias(__pthread_rwlockattr_destroy, pthread_rwlockattr_destroy);
 
-int pthread_rwlockattr_getkind_np(const pthread_rwlockattr_t *attr, int *pref)
+WINE_NO_PROFILE int pthread_rwlockattr_getkind_np(const pthread_rwlockattr_t *attr, int *pref)
 {
   *pref = 0;
   return 0;
 }
 
-int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref)
+WINE_NO_PROFILE int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref)
 {
   return 0;
 }
@@ -763,29 +767,29 @@
 
 /***** MISC *****/
 
-pthread_t pthread_self(void)
+WINE_NO_PROFILE pthread_t pthread_self(void)
 {
   return (pthread_t)GetCurrentThreadId();
 }
 
-int pthread_equal(pthread_t thread1, pthread_t thread2)
+WINE_NO_PROFILE int pthread_equal(pthread_t thread1, pthread_t thread2)
 {
   return (DWORD)thread1 == (DWORD)thread2;
 }
 
-void __pthread_do_exit(void *retval, char *currentframe)
+WINE_NO_PROFILE void __pthread_do_exit(void *retval, char *currentframe)
 {
   /* FIXME: pthread cleanup */
   ExitThread((DWORD)retval);
 }
 
-void __pthread_exit(void *retval)
+WINE_NO_PROFILE void __pthread_exit(void *retval)
 {
     __pthread_do_exit( retval, NULL );
 }
 strong_alias(__pthread_exit, pthread_exit);
 
-int pthread_setcanceltype(int type, int *oldtype)
+WINE_NO_PROFILE int pthread_setcanceltype(int type, int *oldtype)
 {
   if (oldtype) *oldtype = PTHREAD_CANCEL_ASYNCHRONOUS;
   return 0;
@@ -794,7 +798,7 @@
 /***** ANTI-OVERRIDES *****/
 /* pthreads tries to override these, point them back to libc */
 
-int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
+WINE_NO_PROFILE int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
 {
     if (!libc_sigaction)
     {
Index: dlls/ntdll/sysdeps.c
===================================================================
RCS file: /home/wine/wine/dlls/ntdll/sysdeps.c,v
retrieving revision 1.6
diff -u -r1.6 sysdeps.c
--- dlls/ntdll/sysdeps.c	12 Aug 2003 18:59:13 -0000	1.6
+++ dlls/ntdll/sysdeps.c	15 Aug 2003 02:49:31 -0000
@@ -67,6 +67,10 @@
     int   status;
 };
 
+#ifndef WINE_NO_PROFILE
+#define WINE_NO_PROFILE
+#endif
+
 /* temporary stacks used on thread exit */
 #define TEMP_STACK_SIZE 1024
 #define NB_TEMP_STACKS  8
@@ -375,27 +379,27 @@
 #ifndef HAVE_NPTL
 
 /* default errno before threading is initialized */
-static int *default_errno_location(void)
+WINE_NO_PROFILE static int *default_errno_location(void)
 {
     static int static_errno;
     return &static_errno;
 }
 
 /* default h_errno before threading is initialized */
-static int *default_h_errno_location(void)
+WINE_NO_PROFILE static int *default_h_errno_location(void)
 {
     static int static_h_errno;
     return &static_h_errno;
 }
 
 /* errno once threading is working */
-static int *thread_errno_location(void)
+WINE_NO_PROFILE static int *thread_errno_location(void)
 {
     return &NtCurrentTeb()->thread_errno;
 }
 
 /* h_errno once threading is working */
-static int *thread_h_errno_location(void)
+WINE_NO_PROFILE static int *thread_h_errno_location(void)
 {
     return &NtCurrentTeb()->thread_h_errno;
 }
@@ -408,18 +412,18 @@
  *
  * Get the per-thread errno location.
  */
-int *__errno_location(void) { return errno_location_ptr(); }  /* Linux */
-int *__error(void)          { return errno_location_ptr(); }  /* FreeBSD */
-int *__errno(void)          { return errno_location_ptr(); }  /* NetBSD */
-int *___errno(void)         { return errno_location_ptr(); }  /* Solaris */
-int *__thr_errno(void)      { return errno_location_ptr(); }  /* UnixWare */
+WINE_NO_PROFILE int *__errno_location(void) { return errno_location_ptr(); }  /* Linux */
+WINE_NO_PROFILE int *__error(void)          { return errno_location_ptr(); }  /* FreeBSD */
+WINE_NO_PROFILE int *__errno(void)          { return errno_location_ptr(); }  /* NetBSD */
+WINE_NO_PROFILE int *___errno(void)         { return errno_location_ptr(); }  /* Solaris */
+WINE_NO_PROFILE int *__thr_errno(void)      { return errno_location_ptr(); }  /* UnixWare */
 
 /***********************************************************************
  *           __h_errno_location
  *
  * Get the per-thread h_errno location.
  */
-int *__h_errno_location(void)
+WINE_NO_PROFILE int *__h_errno_location(void)
 {
     return h_errno_location_ptr();
 }
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ libs/wprof/Makefile.in	2003-05-30 18:04:36.000000000 +0900
@@ -0,0 +1,39 @@
+# 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 @GLIBC_THREAD_CFLAGS@ -fPIC #-DDEBUG
+LIBS += @GLIBC_THREAD_LIBS@
+
+all: $(TARGETS)
+
+$(TARGETS): wprof
+
+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)
+
+wprof: wineprof.o
+	$(CC) wineprof.o -o wprof $(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 wprof 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-08-15 11:27:31.000000000 +0900
@@ -0,0 +1,555 @@
+/* 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 "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;
+    void *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/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
+++ 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/wineprof.c	2003-05-30 17:19:45.000000000 +0900
@@ -0,0 +1,871 @@
+/* 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 <glib.h>
+
+#include "profiler.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;
+    GList *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 = g_new0(symbol_t, 1);
+    sym->name = g_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(GHashTable *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 = g_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(!g_hash_table_lookup(hash, (gpointer)addr) || type == 'T')
+		{
+		    sym = symbol_new(symbol, (address32_t)addr);
+		    sym->soname = soname;
+		    if(strlen(source))
+			sym->source = g_strdup(source);
+		    g_hash_table_insert(hash, (gpointer)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)
+{
+    GList *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(gconstpointer a, gconstpointer 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)
+	{
+	    GList *e = g_list_find_custom(sym->callers, caller, cmp_callinfo_sym);
+	    if(e)
+		ci = e->data;
+	}
+
+    if(!ci)
+	{
+	    ci = g_new0(callinfo_t, 1);
+	    ci->caller = caller;
+	    sym->callers = g_list_append(sym->callers, ci);
+	}
+
+    ci->calls += arc->count;
+    ci->ticks += arc->ticks;
+}
+
+counter64_t calc_all_ticks(arc_t *root, GHashTable *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 = g_hash_table_lookup(symtab, (gpointer)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); 
+                    g_hash_table_insert(symtab, (gpointer)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 GHashTable *process_maps(wprof_map_t *mp, int num_maps)
+{
+    GHashTable *hash;
+    int i;
+
+    hash = g_hash_table_new(g_direct_hash, g_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 = g_new0(profile_t, 1);
+
+    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 *)(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(gpointer key, gpointer value, gpointer user_data)
+{
+    GList *list = *(GList **)user_data;
+    *(GList **)user_data = g_list_prepend(list, value);
+}
+
+GList *symtab_to_symlist(GHashTable *symtab)
+{
+    GList *list = 0;
+    g_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) \
+gint NAME (gconstpointer a, gconstpointer 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 GList *sort_symlist(GList *symlist)
+{
+    if(!strcmp(opt.sort, "name"))
+	return g_list_sort(symlist, do_sort_by_name);
+
+    if(!strcmp(opt.sort, "calls"))
+        return g_list_sort(symlist, do_sort_by_calls);
+
+    if(!strcmp(opt.sort, "ticks"))
+        return g_list_sort(symlist, do_sort_by_ticks);
+
+    if(!strcmp(opt.sort, "self"))
+        return g_list_sort(symlist, do_sort_by_ticks_self);
+
+    if(!strcmp(opt.sort, "child"))
+        return g_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 GList *sort_callinfo(GList *callers)
+{
+    if(!strcmp(opt.sort, "name"))
+	return g_list_sort(callers, do_sort_ci_by_name);
+
+    if(!strcmp(opt.sort, "calls"))
+	return g_list_sort(callers, do_sort_ci_by_calls);
+    
+    return g_list_sort(callers, do_sort_ci_by_ticks);
+}
+
+
+/*
+ * Display the flat profile
+ */
+void view_profile_flat(profile_t *prof)
+{
+    GList *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)
+		{
+		    GList *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)
+{
+    GList *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)
+		{
+		    GList *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, GHashTable *symtab, int idx)
+{
+	arc_t *arc;
+
+	if(!root)
+		return;
+
+	for(arc = root->children; arc; arc = arc->next)
+        {
+		symbol_t *sym;
+		int i;
+
+		sym = g_hash_table_lookup(symtab, (gpointer)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;
+
+		    /*FIXME not sure if this option is useful/works correctly */
+		case 'i':
+		    {
+			gchar **strv = g_strsplit(optarg, ",", 0);
+			int i = 0;
+			while(strv[i])
+			    {
+				opt.ignore = g_list_append(opt.ignore, g_strdup(strv[i]));
+				i++;
+			    }
+			g_strfreev(strv);
+		    }
+		    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
+++ libs/wprof/wineprof.h	2003-05-30 17:19:45.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;
+	GList        *callers;
+} symbol_t;
+
+typedef struct profile_s {
+	int             fd;
+	wprof_header_t *header;
+	GHashTable     *symtab;
+	GList          *symlist;
+	counter64_t     total_ticks;
+} profile_t;
+
+
+#endif /*__WINEPROF_H__*/
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ libs/wprof/wrappers.c	2003-08-15 11:27:31.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 "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*/


More information about the wine-patches mailing list