This is the mail archive of the
ecos-patches@sources.redhat.com
mailing list for the eCos project.
gprof support for x86 PC
- From: Bart Veer <bartv at ecoscentric dot com>
- To: ecos-patches at ecos dot sourceware dot org
- Date: Mon, 21 Mar 2005 15:04:02 +0000 (GMT)
- Subject: 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 */