[fnrec] Add function recorder for debugging
[people/pcmattman/gpxe.git] / src / core / fnrec.c
1 /*
2  * Copyright (C) 2010 Stefan Hajnoczi <stefanha@gmail.com>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 FILE_LICENCE ( GPL2_OR_LATER );
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <gpxe/init.h>
25 #include <gpxe/uaccess.h>
26
27 /** @file
28  *
29  * Function trace recorder for crash and hang debugging
30  *
31  */
32
33 enum {
34         /** Constant for identifying valid trace buffers */
35         fnrec_magic = 'f' << 24 | 'n' << 16 | 'r' << 8 | 'e',
36
37         /** Trace buffer length */
38         fnrec_buffer_length = 4096 / sizeof ( unsigned long ),
39 };
40
41 /** A trace buffer */
42 struct fnrec_buffer {
43         /** Constant for identifying valid trace buffers */
44         uint32_t magic;
45
46         /** Next trace buffer entry to fill */
47         uint32_t idx;
48
49         /** Function address trace buffer */
50         unsigned long data[fnrec_buffer_length];
51 };
52
53 /** The trace buffer */
54 static struct fnrec_buffer *fnrec_buffer;
55
56 /**
57  * Test whether the trace buffer is valid
58  *
59  * @ret is_valid        Buffer is valid
60  */
61 static int fnrec_is_valid ( void ) {
62         return fnrec_buffer && fnrec_buffer->magic == fnrec_magic;
63 }
64
65 /**
66  * Reset the trace buffer and clear entries
67  */
68 static void fnrec_reset ( void ) {
69         memset ( fnrec_buffer, 0, sizeof ( *fnrec_buffer ) );
70         fnrec_buffer->magic = fnrec_magic;
71 }
72
73 /**
74  * Write a value to the end of the buffer if it is not a repetition
75  *
76  * @v l                 Value to append
77  */
78 static void fnrec_append_unique ( unsigned long l ) {
79         static unsigned long lastval;
80         uint32_t idx = fnrec_buffer->idx;
81
82         /* Avoid recording the same value repeatedly */
83         if ( l == lastval )
84                 return;
85
86         fnrec_buffer->data[idx] = l;
87         fnrec_buffer->idx = ( idx + 1 ) % fnrec_buffer_length;
88         lastval = l;
89 }
90
91 /**
92  * Print the contents of the trace buffer in chronological order
93  */
94 static void fnrec_dump ( void ) {
95         size_t i;
96
97         if ( !fnrec_is_valid() ) {
98                 printf ( "fnrec buffer not found\n" );
99                 return;
100         }
101
102         printf ( "fnrec buffer dump:\n" );
103         for ( i = 0; i < fnrec_buffer_length; i++ ) {
104                 unsigned long l = fnrec_buffer->data[
105                         ( fnrec_buffer->idx + i ) % fnrec_buffer_length];
106                 printf ( "%08lx%c", l, i % 8 == 7 ? '\n' : ' ' );
107         }
108 }
109
110 /**
111  * Function tracer initialisation function
112  */
113 static void fnrec_init ( void ) {
114         /* Hardcoded to 17 MB */
115         fnrec_buffer = phys_to_virt ( 17 * 1024 * 1024 );
116         fnrec_dump();
117         fnrec_reset();
118 }
119
120 struct init_fn fnrec_init_fn __init_fn ( INIT_NORMAL ) = {
121         .initialise = fnrec_init,
122 };
123
124 /*
125  * These functions are called from every C function.  The compiler inserts
126  * these calls when -finstrument-functions is used.
127  */
128 void __cyg_profile_func_enter ( void *called_fn, void *call_site __unused ) {
129         if ( fnrec_is_valid() )
130                 fnrec_append_unique ( ( unsigned long ) called_fn );
131 }
132
133 void __cyg_profile_func_exit ( void *called_fn __unused, void *call_site __unused ) {
134 }