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

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