You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

fnrec.c 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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., 51 Franklin Street, Fifth Floor, Boston, MA
  17. * 02110-1301, USA.
  18. *
  19. * You can also choose to distribute this program under the terms of
  20. * the Unmodified Binary Distribution Licence (as given in the file
  21. * COPYING.UBDL), provided that you have satisfied its requirements.
  22. */
  23. FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  24. #include <stdlib.h>
  25. #include <stdio.h>
  26. #include <string.h>
  27. #include <ipxe/init.h>
  28. #include <ipxe/uaccess.h>
  29. #include <ipxe/io.h>
  30. /** @file
  31. *
  32. * Function trace recorder for crash and hang debugging
  33. *
  34. */
  35. /** Constant for identifying valid trace buffers */
  36. #define FNREC_MAGIC ( 'f' << 24 | 'n' << 16 | 'r' << 8 | 'e' )
  37. /** Number of trace buffer entries */
  38. #define FNREC_NUM_ENTRIES 4096
  39. /** Trace buffer physical address
  40. *
  41. * Fixed at 17MB
  42. */
  43. #define FNREC_PHYS_ADDRESS ( 17 * 1024 * 1024 )
  44. /** A trace buffer entry */
  45. struct fnrec_entry {
  46. /** Called function address */
  47. void *called_fn;
  48. /** Call site */
  49. void *call_site;
  50. /** Entry count */
  51. uint16_t entry_count;
  52. /** Exit count */
  53. uint16_t exit_count;
  54. /** Checksum */
  55. unsigned long checksum;
  56. };
  57. /** A trace buffer */
  58. struct fnrec_buffer {
  59. /** Constant for identifying valid trace buffers */
  60. uint32_t magic;
  61. /** Next trace buffer entry to fill */
  62. unsigned int idx;
  63. /** Trace buffer */
  64. struct fnrec_entry data[FNREC_NUM_ENTRIES]
  65. __attribute__ (( aligned ( 64 ) ));
  66. };
  67. /** The trace buffer */
  68. static struct fnrec_buffer *fnrec_buffer;
  69. /**
  70. * Test whether the trace buffer is valid
  71. *
  72. * @ret is_valid Buffer is valid
  73. */
  74. static int fnrec_is_valid ( void ) {
  75. return ( fnrec_buffer && ( fnrec_buffer->magic == FNREC_MAGIC ) );
  76. }
  77. /**
  78. * Invalidate the trace buffer
  79. *
  80. */
  81. static void fnrec_invalidate ( void ) {
  82. fnrec_buffer->magic = 0;
  83. }
  84. /**
  85. * Reset the trace buffer and clear entries
  86. */
  87. static void fnrec_reset ( void ) {
  88. memset ( fnrec_buffer, 0, sizeof ( *fnrec_buffer ) );
  89. fnrec_buffer->magic = FNREC_MAGIC;
  90. }
  91. /**
  92. * Append an entry to the trace buffer
  93. *
  94. * @v called_fn Called function
  95. * @v call_site Call site
  96. * @ret entry Trace buffer entry
  97. */
  98. static struct fnrec_entry * fnrec_append ( void *called_fn, void *call_site ) {
  99. struct fnrec_entry *entry;
  100. /* Re-use existing entry, if possible */
  101. entry = &fnrec_buffer->data[ fnrec_buffer->idx ];
  102. if ( ( entry->called_fn == called_fn ) &&
  103. ( entry->call_site == call_site ) &&
  104. ( entry->entry_count >= entry->exit_count ) ) {
  105. return entry;
  106. }
  107. /* Otherwise, create a new entry */
  108. fnrec_buffer->idx = ( ( fnrec_buffer->idx + 1 ) % FNREC_NUM_ENTRIES );
  109. entry = &fnrec_buffer->data[ fnrec_buffer->idx ];
  110. entry->called_fn = called_fn;
  111. entry->call_site = call_site;
  112. entry->entry_count = 0;
  113. entry->exit_count = 0;
  114. entry->checksum = ( ( ( unsigned long ) called_fn ) ^
  115. ( ( unsigned long ) call_site ) );
  116. return entry;
  117. }
  118. /**
  119. * Print the contents of the trace buffer in chronological order
  120. */
  121. static void fnrec_dump ( void ) {
  122. struct fnrec_entry *entry;
  123. unsigned int i;
  124. unsigned int idx;
  125. unsigned long checksum;
  126. printf ( "fnrec buffer dump:\n" );
  127. for ( i = 1 ; i <= FNREC_NUM_ENTRIES ; i++ ) {
  128. idx = ( ( fnrec_buffer->idx + i ) % FNREC_NUM_ENTRIES );
  129. entry = &fnrec_buffer->data[idx];
  130. if ( ( entry->entry_count == 0 ) && ( entry->exit_count == 0 ) )
  131. continue;
  132. checksum = ( ( ( ( unsigned long ) entry->called_fn ) ^
  133. ( ( unsigned long ) entry->call_site ) ) +
  134. entry->entry_count + entry->exit_count );
  135. printf ( "%p %p %d %d", entry->called_fn, entry->call_site,
  136. entry->entry_count, entry->exit_count );
  137. if ( entry->checksum != checksum ) {
  138. printf ( " (checksum wrong at phys %08lx)",
  139. virt_to_phys ( entry ) );
  140. }
  141. printf ( "\n");
  142. }
  143. }
  144. /**
  145. * Function tracer initialisation function
  146. */
  147. static void fnrec_init ( void ) {
  148. fnrec_buffer = phys_to_virt ( FNREC_PHYS_ADDRESS );
  149. if ( fnrec_is_valid() ) {
  150. fnrec_invalidate();
  151. fnrec_dump();
  152. } else {
  153. printf ( "fnrec buffer not found\n" );
  154. }
  155. fnrec_reset();
  156. }
  157. struct init_fn fnrec_init_fn __init_fn ( INIT_NORMAL ) = {
  158. .initialise = fnrec_init,
  159. };
  160. /*
  161. * These functions are called from every C function. The compiler inserts
  162. * these calls when -finstrument-functions is used.
  163. */
  164. void __cyg_profile_func_enter ( void *called_fn, void *call_site ) {
  165. struct fnrec_entry *entry;
  166. if ( fnrec_is_valid() ) {
  167. entry = fnrec_append ( called_fn, call_site );
  168. entry->entry_count++;
  169. entry->checksum++;
  170. mb();
  171. }
  172. }
  173. void __cyg_profile_func_exit ( void *called_fn, void *call_site ) {
  174. struct fnrec_entry *entry;
  175. if ( fnrec_is_valid() ) {
  176. entry = fnrec_append ( called_fn, call_site );
  177. entry->exit_count++;
  178. entry->checksum++;
  179. mb();
  180. }
  181. }