diff options
author | Michael Brown <mcb30@ipxe.org> | 2010-12-04 02:55:11 +0000 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2010-12-09 13:11:27 +0000 |
commit | ea0fcb9460403a16d4e2a5e1d48ee6279794450c (patch) | |
tree | ea6bb728ad797317ad93551eb1678c08cf874e81 /src/core/fnrec.c | |
parent | f8a82c7d2300df620ef917d5b7c855d91aa0f31c (diff) | |
download | ipxe-ea0fcb9460403a16d4e2a5e1d48ee6279794450c.zip ipxe-ea0fcb9460403a16d4e2a5e1d48ee6279794450c.tar.gz ipxe-ea0fcb9460403a16d4e2a5e1d48ee6279794450c.tar.bz2 |
[fnrec] Enhance function recording
Enhance the information collected by the function recorder to include
the call site and entry/exit counts. This allows fnrec.pl to produce
a call tree such as:
step (from core/getkey.c:46 = 0x17e90) {
ref_increment (from core/process.c:93 = 0x73ec) { }
net_step (from core/process.c:96 = 0x73f1) {
net_poll (from net/netdevice.c:741 = 0xbce6) {
netdev_poll (from net/netdevice.c:700 = 0xbc58) { }
netdev_rx_dequeue (from net/netdevice.c:709 = 0xbc65) { }
}
}
ref_decrement (from core/process.c:96 = 0x73f9) { }
}
Note that inlined functions are reported, confusingly, as extra calls
to the *containing* function. Minimise this confusion by adding the
attribute "no_instrument_function" to all functions declared as
inline. (Static functions that have been inlined autonomously by gcc
will still be problematic, but these are far fewer in number.)
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/core/fnrec.c')
-rw-r--r-- | src/core/fnrec.c | 146 |
1 files changed, 107 insertions, 39 deletions
diff --git a/src/core/fnrec.c b/src/core/fnrec.c index 888a4a2..05d63aa 100644 --- a/src/core/fnrec.c +++ b/src/core/fnrec.c @@ -23,6 +23,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <string.h> #include <ipxe/init.h> #include <ipxe/uaccess.h> +#include <ipxe/io.h> /** @file * @@ -30,12 +31,30 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -enum { - /** Constant for identifying valid trace buffers */ - fnrec_magic = 'f' << 24 | 'n' << 16 | 'r' << 8 | 'e', +/** Constant for identifying valid trace buffers */ +#define FNREC_MAGIC ( 'f' << 24 | 'n' << 16 | 'r' << 8 | 'e' ) + +/** Number of trace buffer entries */ +#define FNREC_NUM_ENTRIES 4096 - /** Trace buffer length */ - fnrec_buffer_length = 4096 / sizeof ( unsigned long ), +/** Trace buffer physical address + * + * Fixed at 17MB + */ +#define FNREC_PHYS_ADDRESS ( 17 * 1024 * 1024 ) + +/** A trace buffer entry */ +struct fnrec_entry { + /** Called function address */ + void *called_fn; + /** Call site */ + void *call_site; + /** Entry count */ + uint16_t entry_count; + /** Exit count */ + uint16_t exit_count; + /** Checksum */ + unsigned long checksum; }; /** A trace buffer */ @@ -44,10 +63,11 @@ struct fnrec_buffer { uint32_t magic; /** Next trace buffer entry to fill */ - uint32_t idx; + unsigned int idx; - /** Function address trace buffer */ - unsigned long data[fnrec_buffer_length]; + /** Trace buffer */ + struct fnrec_entry data[FNREC_NUM_ENTRIES] + __attribute__ (( aligned ( 64 ) )); }; /** The trace buffer */ @@ -59,7 +79,15 @@ static struct fnrec_buffer *fnrec_buffer; * @ret is_valid Buffer is valid */ static int fnrec_is_valid ( void ) { - return fnrec_buffer && fnrec_buffer->magic == fnrec_magic; + return ( fnrec_buffer && ( fnrec_buffer->magic == FNREC_MAGIC ) ); +} + +/** + * Invalidate the trace buffer + * + */ +static void fnrec_invalidate ( void ) { + fnrec_buffer->magic = 0; } /** @@ -67,43 +95,64 @@ static int fnrec_is_valid ( void ) { */ static void fnrec_reset ( void ) { memset ( fnrec_buffer, 0, sizeof ( *fnrec_buffer ) ); - fnrec_buffer->magic = fnrec_magic; + fnrec_buffer->magic = FNREC_MAGIC; } /** - * Write a value to the end of the buffer if it is not a repetition + * Append an entry to the trace buffer * - * @v l Value to append + * @v called_fn Called function + * @v call_site Call site + * @ret entry Trace buffer entry */ -static void fnrec_append_unique ( unsigned long l ) { - static unsigned long lastval; - uint32_t idx = fnrec_buffer->idx; - - /* Avoid recording the same value repeatedly */ - if ( l == lastval ) - return; +static struct fnrec_entry * fnrec_append ( void *called_fn, void *call_site ) { + struct fnrec_entry *entry; + + /* Re-use existing entry, if possible */ + entry = &fnrec_buffer->data[ fnrec_buffer->idx ]; + if ( ( entry->called_fn == called_fn ) && + ( entry->call_site == call_site ) && + ( entry->entry_count >= entry->exit_count ) ) { + return entry; + } - fnrec_buffer->data[idx] = l; - fnrec_buffer->idx = ( idx + 1 ) % fnrec_buffer_length; - lastval = l; + /* Otherwise, create a new entry */ + fnrec_buffer->idx = ( ( fnrec_buffer->idx + 1 ) % FNREC_NUM_ENTRIES ); + entry = &fnrec_buffer->data[ fnrec_buffer->idx ]; + entry->called_fn = called_fn; + entry->call_site = call_site; + entry->entry_count = 0; + entry->exit_count = 0; + entry->checksum = ( ( ( unsigned long ) called_fn ) ^ + ( ( unsigned long ) call_site ) ); + return entry; } /** * Print the contents of the trace buffer in chronological order */ static void fnrec_dump ( void ) { - size_t i; - - if ( !fnrec_is_valid() ) { - printf ( "fnrec buffer not found\n" ); - return; - } + struct fnrec_entry *entry; + unsigned int i; + unsigned int idx; + unsigned long checksum; printf ( "fnrec buffer dump:\n" ); - for ( i = 0; i < fnrec_buffer_length; i++ ) { - unsigned long l = fnrec_buffer->data[ - ( fnrec_buffer->idx + i ) % fnrec_buffer_length]; - printf ( "%08lx%c", l, i % 8 == 7 ? '\n' : ' ' ); + for ( i = 1 ; i <= FNREC_NUM_ENTRIES ; i++ ) { + idx = ( ( fnrec_buffer->idx + i ) % FNREC_NUM_ENTRIES ); + entry = &fnrec_buffer->data[idx]; + if ( ( entry->entry_count == 0 ) && ( entry->exit_count == 0 ) ) + continue; + checksum = ( ( ( ( unsigned long ) entry->called_fn ) ^ + ( ( unsigned long ) entry->call_site ) ) + + entry->entry_count + entry->exit_count ); + printf ( "%p %p %d %d", entry->called_fn, entry->call_site, + entry->entry_count, entry->exit_count ); + if ( entry->checksum != checksum ) { + printf ( " (checksum wrong at phys %08lx)", + virt_to_phys ( entry ) ); + } + printf ( "\n"); } } @@ -111,9 +160,14 @@ static void fnrec_dump ( void ) { * Function tracer initialisation function */ static void fnrec_init ( void ) { - /* Hardcoded to 17 MB */ - fnrec_buffer = phys_to_virt ( 17 * 1024 * 1024 ); - fnrec_dump(); + + fnrec_buffer = phys_to_virt ( FNREC_PHYS_ADDRESS ); + if ( fnrec_is_valid() ) { + fnrec_invalidate(); + fnrec_dump(); + } else { + printf ( "fnrec buffer not found\n" ); + } fnrec_reset(); } @@ -125,10 +179,24 @@ struct init_fn fnrec_init_fn __init_fn ( INIT_NORMAL ) = { * These functions are called from every C function. The compiler inserts * these calls when -finstrument-functions is used. */ -void __cyg_profile_func_enter ( void *called_fn, void *call_site __unused ) { - if ( fnrec_is_valid() ) - fnrec_append_unique ( ( unsigned long ) called_fn ); +void __cyg_profile_func_enter ( void *called_fn, void *call_site ) { + struct fnrec_entry *entry; + + if ( fnrec_is_valid() ) { + entry = fnrec_append ( called_fn, call_site ); + entry->entry_count++; + entry->checksum++; + mb(); + } } -void __cyg_profile_func_exit ( void *called_fn __unused, void *call_site __unused ) { +void __cyg_profile_func_exit ( void *called_fn, void *call_site ) { + struct fnrec_entry *entry; + + if ( fnrec_is_valid() ) { + entry = fnrec_append ( called_fn, call_site ); + entry->exit_count++; + entry->checksum++; + mb(); + } } |