diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index 8ba7e441..7e7ad762 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -375,6 +375,35 @@ CFLAGS += -Werror ASFLAGS += --fatal-warnings endif +# Function trace recorder state in the last build. This is needed +# in order to correctly rebuild whenever the function recorder is +# enabled/disabled. +# +FNREC_STATE := $(BIN)/.fnrec.state +ifeq ($(wildcard $(FNREC_STATE)),) +FNREC_STATE_OLD := +else +FNREC_STATE_OLD := $(shell cat $(FNREC_STATE)) +endif +ifeq ($(FNREC_STATE_OLD),$(FNREC)) +$(FNREC_STATE) : +else +$(FNREC_STATE) : clean +$(shell $(ECHO) "$(FNREC)" > $(FNREC_STATE)) +endif + +VERYCLEANUP += $(FNREC_STATE) +MAKEDEPS += $(FNREC_STATE) + +ifeq ($(FNREC),1) +# Enabling -finstrument-functions affects gcc's analysis and leads to spurious +# warnings about use of uninitialised variables. +# +CFLAGS += -Wno-uninitialized +CFLAGS += -finstrument-functions +CFLAGS += -finstrument-functions-exclude-file-list=core/fnrec.c +endif + # compiler.h is needed for our linking and debugging system # CFLAGS += -include compiler.h diff --git a/src/core/fnrec.c b/src/core/fnrec.c new file mode 100644 index 00000000..c768c91d --- /dev/null +++ b/src/core/fnrec.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2010 Stefan Hajnoczi . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include + +/** @file + * + * Function trace recorder for crash and hang debugging + * + */ + +enum { + /** Constant for identifying valid trace buffers */ + fnrec_magic = 'f' << 24 | 'n' << 16 | 'r' << 8 | 'e', + + /** Trace buffer length */ + fnrec_buffer_length = 4096 / sizeof ( unsigned long ), +}; + +/** A trace buffer */ +struct fnrec_buffer { + /** Constant for identifying valid trace buffers */ + uint32_t magic; + + /** Next trace buffer entry to fill */ + uint32_t idx; + + /** Function address trace buffer */ + unsigned long data[fnrec_buffer_length]; +}; + +/** The trace buffer */ +static struct fnrec_buffer *fnrec_buffer; + +/** + * Test whether the trace buffer is valid + * + * @ret is_valid Buffer is valid + */ +static int fnrec_is_valid ( void ) { + return fnrec_buffer && fnrec_buffer->magic == fnrec_magic; +} + +/** + * Reset the trace buffer and clear entries + */ +static void fnrec_reset ( void ) { + memset ( fnrec_buffer, 0, sizeof ( *fnrec_buffer ) ); + fnrec_buffer->magic = fnrec_magic; +} + +/** + * Write a value to the end of the buffer if it is not a repetition + * + * @v l Value to append + */ +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; + + fnrec_buffer->data[idx] = l; + fnrec_buffer->idx = ( idx + 1 ) % fnrec_buffer_length; + lastval = l; +} + +/** + * 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; + } + + 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' : ' ' ); + } +} + +/** + * Function tracer initialisation function + */ +static void fnrec_init ( void ) { + /* Hardcoded to 17 MB */ + fnrec_buffer = phys_to_virt ( 17 * 1024 * 1024 ); + fnrec_dump(); + fnrec_reset(); +} + +struct init_fn fnrec_init_fn __init_fn ( INIT_NORMAL ) = { + .initialise = fnrec_init, +}; + +/* + * 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_exit ( void *called_fn __unused, void *call_site __unused ) { +} diff --git a/src/util/fnrec.sh b/src/util/fnrec.sh new file mode 100755 index 00000000..00784ac5 --- /dev/null +++ b/src/util/fnrec.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# +# Copyright (C) 2010 Stefan Hajnoczi . +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or any later version. +# +# This program 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 +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +if [ $# != 2 ] +then + cat >&2 < +Look up symbol names in for function addresses from +. + +Example: +$0 bin/gpxe.hd.tmp fnrec.dat +EOF + exit 1 +fi + +tr ' ' '\n' <"$2" | addr2line -fe "$1" | awk '(NR % 2) { print }'