This is the mail archive of the ecos-patches@sources.redhat.com mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

gprof support for x86 PC


This adds gprof support (both mcount()/callgraph and a profiling
timer) to x86 PCs. The profiling timer is somewhat nasty. There is
only a single h/w timer which gets used for the system clock, so I
have to intercept its interrupts and then chain. This means profiling
is only possible on configurations containing the kernel.

The mcount() support in SMP configurations is untested - I don't have
suitable h/w readily available. However gprof profiling does not make
very much sense on SMP systems anyway, the technology was not designed
for such environments.

Bart

Index: hal/i386/arch/current/ChangeLog
===================================================================
RCS file: /cvs/ecos/ecos/packages/hal/i386/arch/current/ChangeLog,v
retrieving revision 1.41
diff -u -r1.41 ChangeLog
--- hal/i386/arch/current/ChangeLog	22 Apr 2004 15:26:37 -0000	1.41
+++ hal/i386/arch/current/ChangeLog	21 Mar 2005 14:54:39 -0000
@@ -1,3 +1,8 @@
+2005-03-21  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/vectors.S, src/hal_misc.c, cdl/hal_i386.cdl: add profiling
+	support.
+
 2004-04-22  Jani Monoses <jani@iv.ro>
 
 	 * cdl/hal_i386.cdl :
Index: hal/i386/arch/current/cdl/hal_i386.cdl
===================================================================
RCS file: /cvs/ecos/ecos/packages/hal/i386/arch/current/cdl/hal_i386.cdl,v
retrieving revision 1.11
diff -u -r1.11 hal_i386.cdl
--- hal/i386/arch/current/cdl/hal_i386.cdl	22 Apr 2004 15:26:37 -0000	1.11
+++ hal/i386/arch/current/cdl/hal_i386.cdl	21 Mar 2005 14:54:39 -0000
@@ -61,6 +61,8 @@
         necessary to select a specific target platform HAL
         package."
 
+    implements	  CYGINT_PROFILE_HAL_MCOUNT
+    
     compile       hal_misc.c context.S i386_stub.c hal_syscall.c
 
     make {
Index: hal/i386/arch/current/src/hal_misc.c
===================================================================
RCS file: /cvs/ecos/ecos/packages/hal/i386/arch/current/src/hal_misc.c,v
retrieving revision 1.12
diff -u -r1.12 hal_misc.c
--- hal/i386/arch/current/src/hal_misc.c	8 Dec 2003 15:34:56 -0000	1.12
+++ hal/i386/arch/current/src/hal_misc.c	21 Mar 2005 14:54:39 -0000
@@ -51,6 +51,7 @@
 //
 //===========================================================================
 
+#include <pkgconf/system.h>
 #include <pkgconf/hal.h>
 
 #include <cyg/infra/cyg_type.h>
@@ -58,6 +59,7 @@
 #include <cyg/infra/diag.h>      // diag_printf
 
 #include <cyg/hal/hal_arch.h>
+#include <cyg/hal/hal_intr.h>
 
 #include <cyg/hal/hal_if.h>
 
@@ -156,4 +158,83 @@
 }
 
 //---------------------------------------------------------------------------
+// Profiling support. The timer has to be implemented by the platform
+// HAL, but mcount() is generic for all x86 processors. There are three
+// complications: the generic profile code assumes that __profile_mcount()
+// is called with interrupts disabled; on an SMP system there may be
+// concurrent calls to mcount(); and if eCos itself is built with
+// -pg then there may be recursive calls to mcount() which we have
+// to guard against.
+//
+// With i386-elf-gcc the call is to _mcount(), not mcount(), and there
+// is already a return address in %edx. For now the latter is ignored.
+// It would be useful in assembler code, but because we have to worry
+// about interrupts and spinlocks a implementation is rather easier.
+
+#ifdef CYGPKG_PROFILE_GPROF
+#include <cyg/profile/profile.h>
+
+# ifdef CYGPKG_HAL_SMP_SUPPORT
+static HAL_SMP_CPU_TYPE     mcount_cpu  = HAL_SMP_CPU_NONE;
+static HAL_SPINLOCK_TYPE    mcount_lock = HAL_SPINLOCK_INIT_CLEAR;
+
+void
+_mcount(void)
+{
+    int                 ints_enabled;
+    HAL_SMP_CPU_TYPE    this_cpu;
+    
+    HAL_DISABLE_INTERRUPTS(ints_enabled);
+
+    // This cpu is now not going to run any other code. So, did it
+    // already own the spinlock?
+    this_cpu = HAL_SMP_CPU_THIS();
+    if (mcount_cpu != this_cpu) {
+        // Nope, so this cannot be a nested call to mcount()
+        HAL_SPINLOCK_SPIN(mcount_lock);
+        // And no other cpu is executing inside mcount() either
+        mcount_cpu  = this_cpu;
+        // A possibly-recursive call is now safe.
+        __profile_mcount((CYG_ADDRWORD)__builtin_return_address(1),
+                         (CYG_ADDRWORD)__builtin_return_address(0));
+        // All done.
+        mcount_cpu = HAL_SMP_CPU_NONE;
+        HAL_SPINLOCK_CLEAR(mcount_lock);
+    }
+    
+    HAL_RESTORE_INTERRUPTS(ints_enabled);
+}
+
+# else   // ! SMP
+
+static int  mcount_nested;
+
+void
+_mcount(void)
+{
+    int ints_enabled;
+    HAL_DISABLE_INTERRUPTS(ints_enabled);
+    if (! mcount_nested) {
+        mcount_nested   = 1;
+        __profile_mcount((CYG_ADDRWORD)__builtin_return_address(1),
+                         (CYG_ADDRWORD)__builtin_return_address(0));
+        mcount_nested   = 0;
+    }
+    HAL_RESTORE_INTERRUPTS(ints_enabled);
+}
+
+# endif // SMP
+
+// The main VSR will update hal_saved_interrupt_state if profiling is
+// enabled, on the assumption that the platform-specific
+// hal_enable_profile_timer() will somehow make use of timer
+// interrupts. The generic HAL will only provide that if GDB break
+// support is enabled.
+# if !( defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT) || defined(CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT))
+struct HAL_SavedRegisters *hal_saved_interrupt_state;
+# endif
+
+#endif  // CYGPKG_PROFILE_GPROF
+
+//---------------------------------------------------------------------------
 // End of hal_misc.c
Index: hal/i386/arch/current/src/vectors.S
===================================================================
RCS file: /cvs/ecos/ecos/packages/hal/i386/arch/current/src/vectors.S,v
retrieving revision 1.16
diff -u -r1.16 vectors.S
--- hal/i386/arch/current/src/vectors.S	1 Dec 2002 15:50:14 -0000	1.16
+++ hal/i386/arch/current/src/vectors.S	21 Mar 2005 14:54:43 -0000
@@ -494,7 +494,8 @@
 	hal_fpu_push_int	# save FPU state
 
 #if defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT) || \
-    defined(CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT)
+    defined(CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT) || \
+    defined(CYGPKG_PROFILE_GPROF)
 	# Save the context just to be able to set a breakpoint
 	# when we have a CTRL-C
 	.extern hal_saved_interrupt_state
Index: hal/i386/pcmb/current/ChangeLog
===================================================================
RCS file: /cvs/ecos/ecos/packages/hal/i386/pcmb/current/ChangeLog,v
retrieving revision 1.20
diff -u -r1.20 ChangeLog
--- hal/i386/pcmb/current/ChangeLog	22 Jan 2005 15:11:31 -0000	1.20
+++ hal/i386/pcmb/current/ChangeLog	21 Mar 2005 14:54:43 -0000
@@ -1,3 +1,7 @@
+2005-03-21  Bart Veer  <bartv@ecoscentric.com>
+
+	* src/pcmb_misc.c, cdl/hal_i386_pcmb.cdl: add profiling support
+
 2005-01-22  Ian Campbell  <icampbell@arcom.com>
 
 	* include/pcmb_serial.h, 
Index: hal/i386/pcmb/current/cdl/hal_i386_pcmb.cdl
===================================================================
RCS file: /cvs/ecos/ecos/packages/hal/i386/pcmb/current/cdl/hal_i386_pcmb.cdl,v
retrieving revision 1.8
diff -u -r1.8 hal_i386_pcmb.cdl
--- hal/i386/pcmb/current/cdl/hal_i386_pcmb.cdl	24 Jul 2003 20:24:01 -0000	1.8
+++ hal/i386/pcmb/current/cdl/hal_i386_pcmb.cdl	21 Mar 2005 14:54:43 -0000
@@ -173,6 +173,27 @@
 	    puts $::cdl_header ""
 	}
     }
+
+    # Profiling support. This is not really a user-settable option,
+    # just a way of associating a constraint with a description. The
+    # profiling timer involves munging the system timer so profiling
+    # is only possible in suitable kernel configurations.
+    cdl_option CYGFUN_HAL_I386_PCMB_GPROF_SUPPORT {
+	display		"Support for gprof profiling"
+	active_if	CYGPKG_PROFILE_GPROF
+	calculated	1
+	implements	CYGINT_PROFILE_HAL_TIMER
+	requires	CYGVAR_KERNEL_COUNTERS_CLOCK
+	description "
+            The PC HAL support can provide a profiling timer for use
+          by the gprof package. However the hardware only provides a
+          single source of timer interrupts so this must be shared
+          between the system clock and the profiling code. The current
+          implementation requires that the system clock be initialized
+          first (which happens in a static constructor with priority
+          CYG_INIT_CLOCK), then profiling can be enabled via a call
+          to profile_on()."
+    }
     
     cdl_interface CYGINT_HAL_I386_PCMB_SCREEN_SUPPORT {
 	display       "Enable PC screen support"
Index: hal/i386/pcmb/current/src/pcmb_misc.c
===================================================================
RCS file: /cvs/ecos/ecos/packages/hal/i386/pcmb/current/src/pcmb_misc.c,v
retrieving revision 1.7
diff -u -r1.7 pcmb_misc.c
--- hal/i386/pcmb/current/src/pcmb_misc.c	23 May 2002 23:03:16 -0000	1.7
+++ hal/i386/pcmb/current/src/pcmb_misc.c	21 Mar 2005 14:54:44 -0000
@@ -8,6 +8,7 @@
 //####ECOSGPLCOPYRIGHTBEGIN####
 // -------------------------------------------
 // This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2005 eCosCentric Ltd
 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
 //
 // eCos is free software; you can redistribute it and/or modify it under
@@ -51,6 +52,7 @@
 //
 //========================================================================*/
 
+#include <pkgconf/system.h>
 #include <pkgconf/hal.h>
 #include <pkgconf/hal_i386.h>
 #include <pkgconf/hal_i386_pcmb.h>
@@ -59,6 +61,7 @@
 #include <cyg/infra/cyg_trac.h>         // tracing macros
 #include <cyg/infra/cyg_ass.h>          // assertion macros
 
+#include <cyg/hal/hal_arch.h>
 #include <cyg/hal/hal_intr.h>
 #include <cyg/hal/hal_io.h>
 #include <cyg/hal/hal_smp.h>
@@ -228,5 +231,46 @@
 #endif
 }
 
+// ----------------------------------------------------------------------------
+// Profiling support. mcount() is provided by the architectural HAL
+// but the timer has to be provided by the platform. This is tricky on
+// a PC because there is only a single clock which can generate
+// interrupts, and that gets used for the system clock. To get around
+// this we assume that the system clock has already been enabled,
+// and we sneak in a new ISR routine which will chain to the default
+// one. This is possible because of the way the interrupt subsystem
+// is implemented in the x86 architectural HAL.
+
+#ifdef CYGFUN_HAL_I386_PCMB_GPROF_SUPPORT
+# include <cyg/profile/profile.h>
+
+extern HAL_SavedRegisters *hal_saved_interrupt_state;
+static int  (*hal_pcmb_profile_saved_isr)(CYG_ADDRWORD, CYG_ADDRWORD);
+
+static int
+hal_pcmb_profile_isr(CYG_ADDRWORD vector, CYG_ADDRWORD data)
+{
+    // Now chain to the saved ISR.
+    __profile_hit(hal_saved_interrupt_state->pc);
+    return (*hal_pcmb_profile_saved_isr)(vector, data);
+}
+
+int
+hal_enable_profile_timer(int resolution)
+{
+    cyg_uint64  actual_resolution;
+
+    hal_pcmb_profile_saved_isr  = (int (*)(CYG_ADDRWORD, CYG_ADDRWORD)) hal_interrupt_handlers[CYGNUM_HAL_INTERRUPT_TIMER - CYGNUM_HAL_ISR_MIN];
+    hal_interrupt_handlers[CYGNUM_HAL_INTERRUPT_TIMER - CYGNUM_HAL_ISR_MIN]  = (CYG_ADDRESS) &hal_pcmb_profile_isr;
+    
+    // The only resolution that can be supported is that of the system
+    // clock. RTC_PERIOD works in terms of a 1193180Hz clock, and we
+    // need to return microseconds.
+    actual_resolution = (1000000ll * CYGNUM_HAL_RTC_PERIOD) / 1193180;
+    return actual_resolution;
+}
+
+#endif  // CYGFUN_HAL_I386_PCMB_GPROF_SUPPORT
+
 /*------------------------------------------------------------------------*/
 /* End of pcmb_misc.c                                                      */


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]