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.0KB

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