《操作系统》的实验代码。
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.

326 lines
12 KiB

  1. #include <defs.h>
  2. #include <x86.h>
  3. #include <stab.h>
  4. #include <stdio.h>
  5. #include <string.h>
  6. #include <sync.h>
  7. #include <kdebug.h>
  8. #include <kmonitor.h>
  9. #include <assert.h>
  10. #define STACKFRAME_DEPTH 20
  11. extern const struct stab __STAB_BEGIN__[]; // beginning of stabs table
  12. extern const struct stab __STAB_END__[]; // end of stabs table
  13. extern const char __STABSTR_BEGIN__[]; // beginning of string table
  14. extern const char __STABSTR_END__[]; // end of string table
  15. /* debug information about a particular instruction pointer */
  16. struct eipdebuginfo {
  17. const char *eip_file; // source code filename for eip
  18. int eip_line; // source code line number for eip
  19. const char *eip_fn_name; // name of function containing eip
  20. int eip_fn_namelen; // length of function's name
  21. uintptr_t eip_fn_addr; // start address of function
  22. int eip_fn_narg; // number of function arguments
  23. };
  24. /* *
  25. * stab_binsearch - according to the input, the initial value of
  26. * range [*@region_left, *@region_right], find a single stab entry
  27. * that includes the address @addr and matches the type @type,
  28. * and then save its boundary to the locations that pointed
  29. * by @region_left and @region_right.
  30. *
  31. * Some stab types are arranged in increasing order by instruction address.
  32. * For example, N_FUN stabs (stab entries with n_type == N_FUN), which
  33. * mark functions, and N_SO stabs, which mark source files.
  34. *
  35. * Given an instruction address, this function finds the single stab entry
  36. * of type @type that contains that address.
  37. *
  38. * The search takes place within the range [*@region_left, *@region_right].
  39. * Thus, to search an entire set of N stabs, you might do:
  40. *
  41. * left = 0;
  42. * right = N - 1; (rightmost stab)
  43. * stab_binsearch(stabs, &left, &right, type, addr);
  44. *
  45. * The search modifies *region_left and *region_right to bracket the @addr.
  46. * *@region_left points to the matching stab that contains @addr,
  47. * and *@region_right points just before the next stab.
  48. * If *@region_left > *region_right, then @addr is not contained in any
  49. * matching stab.
  50. *
  51. * For example, given these N_SO stabs:
  52. * Index Type Address
  53. * 0 SO f0100000
  54. * 13 SO f0100040
  55. * 117 SO f0100176
  56. * 118 SO f0100178
  57. * 555 SO f0100652
  58. * 556 SO f0100654
  59. * 657 SO f0100849
  60. * this code:
  61. * left = 0, right = 657;
  62. * stab_binsearch(stabs, &left, &right, N_SO, 0xf0100184);
  63. * will exit setting left = 118, right = 554.
  64. * */
  65. static void
  66. stab_binsearch(const struct stab *stabs, int *region_left, int *region_right,
  67. int type, uintptr_t addr) {
  68. int l = *region_left, r = *region_right, any_matches = 0;
  69. while (l <= r) {
  70. int true_m = (l + r) / 2, m = true_m;
  71. // search for earliest stab with right type
  72. while (m >= l && stabs[m].n_type != type) {
  73. m --;
  74. }
  75. if (m < l) { // no match in [l, m]
  76. l = true_m + 1;
  77. continue;
  78. }
  79. // actual binary search
  80. any_matches = 1;
  81. if (stabs[m].n_value < addr) {
  82. *region_left = m;
  83. l = true_m + 1;
  84. } else if (stabs[m].n_value > addr) {
  85. *region_right = m - 1;
  86. r = m - 1;
  87. } else {
  88. // exact match for 'addr', but continue loop to find
  89. // *region_right
  90. *region_left = m;
  91. l = m;
  92. addr ++;
  93. }
  94. }
  95. if (!any_matches) {
  96. *region_right = *region_left - 1;
  97. }
  98. else {
  99. // find rightmost region containing 'addr'
  100. l = *region_right;
  101. for (; l > *region_left && stabs[l].n_type != type; l --)
  102. /* do nothing */;
  103. *region_left = l;
  104. }
  105. }
  106. /* *
  107. * debuginfo_eip - Fill in the @info structure with information about
  108. * the specified instruction address, @addr. Returns 0 if information
  109. * was found, and negative if not. But even if it returns negative it
  110. * has stored some information into '*info'.
  111. * */
  112. int
  113. debuginfo_eip(uintptr_t addr, struct eipdebuginfo *info) {
  114. const struct stab *stabs, *stab_end;
  115. const char *stabstr, *stabstr_end;
  116. info->eip_file = "<unknown>";
  117. info->eip_line = 0;
  118. info->eip_fn_name = "<unknown>";
  119. info->eip_fn_namelen = 9;
  120. info->eip_fn_addr = addr;
  121. info->eip_fn_narg = 0;
  122. stabs = __STAB_BEGIN__;
  123. stab_end = __STAB_END__;
  124. stabstr = __STABSTR_BEGIN__;
  125. stabstr_end = __STABSTR_END__;
  126. // String table validity checks
  127. if (stabstr_end <= stabstr || stabstr_end[-1] != 0) {
  128. return -1;
  129. }
  130. // Now we find the right stabs that define the function containing
  131. // 'eip'. First, we find the basic source file containing 'eip'.
  132. // Then, we look in that source file for the function. Then we look
  133. // for the line number.
  134. // Search the entire set of stabs for the source file (type N_SO).
  135. int lfile = 0, rfile = (stab_end - stabs) - 1;
  136. stab_binsearch(stabs, &lfile, &rfile, N_SO, addr);
  137. if (lfile == 0)
  138. return -1;
  139. // Search within that file's stabs for the function definition
  140. // (N_FUN).
  141. int lfun = lfile, rfun = rfile;
  142. int lline, rline;
  143. stab_binsearch(stabs, &lfun, &rfun, N_FUN, addr);
  144. if (lfun <= rfun) {
  145. // stabs[lfun] points to the function name
  146. // in the string table, but check bounds just in case.
  147. if (stabs[lfun].n_strx < stabstr_end - stabstr) {
  148. info->eip_fn_name = stabstr + stabs[lfun].n_strx;
  149. }
  150. info->eip_fn_addr = stabs[lfun].n_value;
  151. addr -= info->eip_fn_addr;
  152. // Search within the function definition for the line number.
  153. lline = lfun;
  154. rline = rfun;
  155. } else {
  156. // Couldn't find function stab! Maybe we're in an assembly
  157. // file. Search the whole file for the line number.
  158. info->eip_fn_addr = addr;
  159. lline = lfile;
  160. rline = rfile;
  161. }
  162. info->eip_fn_namelen = strfind(info->eip_fn_name, ':') - info->eip_fn_name;
  163. // Search within [lline, rline] for the line number stab.
  164. // If found, set info->eip_line to the right line number.
  165. // If not found, return -1.
  166. stab_binsearch(stabs, &lline, &rline, N_SLINE, addr);
  167. if (lline <= rline) {
  168. info->eip_line = stabs[rline].n_desc;
  169. } else {
  170. return -1;
  171. }
  172. // Search backwards from the line number for the relevant filename stab.
  173. // We can't just use the "lfile" stab because inlined functions
  174. // can interpolate code from a different file!
  175. // Such included source files use the N_SOL stab type.
  176. while (lline >= lfile
  177. && stabs[lline].n_type != N_SOL
  178. && (stabs[lline].n_type != N_SO || !stabs[lline].n_value)) {
  179. lline --;
  180. }
  181. if (lline >= lfile && stabs[lline].n_strx < stabstr_end - stabstr) {
  182. info->eip_file = stabstr + stabs[lline].n_strx;
  183. }
  184. // Set eip_fn_narg to the number of arguments taken by the function,
  185. // or 0 if there was no containing function.
  186. if (lfun < rfun) {
  187. for (lline = lfun + 1;
  188. lline < rfun && stabs[lline].n_type == N_PSYM;
  189. lline ++) {
  190. info->eip_fn_narg ++;
  191. }
  192. }
  193. return 0;
  194. }
  195. /* *
  196. * print_kerninfo - print the information about kernel, including the location
  197. * of kernel entry, the start addresses of data and text segements, the start
  198. * address of free memory and how many memory that kernel has used.
  199. * */
  200. void
  201. print_kerninfo(void) {
  202. extern char etext[], edata[], end[], kern_init[];
  203. cprintf("Special kernel symbols:\n");
  204. cprintf(" entry 0x%08x (phys)\n", kern_init);
  205. cprintf(" etext 0x%08x (phys)\n", etext);
  206. cprintf(" edata 0x%08x (phys)\n", edata);
  207. cprintf(" end 0x%08x (phys)\n", end);
  208. cprintf("Kernel executable memory footprint: %dKB\n", (end - kern_init + 1023)/1024);
  209. }
  210. /* *
  211. * print_debuginfo - read and print the stat information for the address @eip,
  212. * and info.eip_fn_addr should be the first address of the related function.
  213. * */
  214. void
  215. print_debuginfo(uintptr_t eip) {
  216. struct eipdebuginfo info;
  217. if (debuginfo_eip(eip, &info) != 0) {
  218. cprintf(" <unknow>: -- 0x%08x --\n", eip);
  219. }
  220. else {
  221. char fnname[256];
  222. int j;
  223. for (j = 0; j < info.eip_fn_namelen; j ++) {
  224. fnname[j] = info.eip_fn_name[j];
  225. }
  226. fnname[j] = '\0';
  227. cprintf(" %s:%d: %s+%d\n", info.eip_file, info.eip_line,
  228. fnname, eip - info.eip_fn_addr);
  229. }
  230. }
  231. static __noinline uint32_t
  232. read_eip(void) {
  233. uint32_t eip;
  234. asm volatile("movl 4(%%ebp), %0" : "=r" (eip));
  235. return eip;
  236. }
  237. /* *
  238. * print_stackframe - print a list of the saved eip values from the nested 'call'
  239. * instructions that led to the current point of execution
  240. *
  241. * The x86 stack pointer, namely esp, points to the lowest location on the stack
  242. * that is currently in use. Everything below that location in stack is free. Pushing
  243. * a value onto the stack will invole decreasing the stack pointer and then writing
  244. * the value to the place that stack pointer pointes to. And popping a value do the
  245. * opposite.
  246. *
  247. * The ebp (base pointer) register, in contrast, is associated with the stack
  248. * primarily by software convention. On entry to a C function, the function's
  249. * prologue code normally saves the previous function's base pointer by pushing
  250. * it onto the stack, and then copies the current esp value into ebp for the duration
  251. * of the function. If all the functions in a program obey this convention,
  252. * then at any given point during the program's execution, it is possible to trace
  253. * back through the stack by following the chain of saved ebp pointers and determining
  254. * exactly what nested sequence of function calls caused this particular point in the
  255. * program to be reached. This capability can be particularly useful, for example,
  256. * when a particular function causes an assert failure or panic because bad arguments
  257. * were passed to it, but you aren't sure who passed the bad arguments. A stack
  258. * backtrace lets you find the offending function.
  259. *
  260. * The inline function read_ebp() can tell us the value of current ebp. And the
  261. * non-inline function read_eip() is useful, it can read the value of current eip,
  262. * since while calling this function, read_eip() can read the caller's eip from
  263. * stack easily.
  264. *
  265. * In print_debuginfo(), the function debuginfo_eip() can get enough information about
  266. * calling-chain. Finally print_stackframe() will trace and print them for debugging.
  267. *
  268. * Note that, the length of ebp-chain is limited. In boot/bootasm.S, before jumping
  269. * to the kernel entry, the value of ebp has been set to zero, that's the boundary.
  270. * */
  271. void
  272. print_stackframe(void) {
  273. /* LAB1 2012011346 : STEP 1 */
  274. /* (1) call read_ebp() to get the value of ebp. the type is (uint32_t);
  275. * (2) call read_eip() to get the value of eip. the type is (uint32_t);
  276. * (3) from 0 .. STACKFRAME_DEPTH
  277. * (3.1) printf value of ebp, eip
  278. * (3.2) (uint32_t)calling arguments [0..4] = the contents in address (unit32_t)ebp +2 [0..4]
  279. * (3.3) cprintf("\n");
  280. * (3.4) call print_debuginfo(eip-1) to print the C calling function name and line number, etc.
  281. * (3.5) popup a calling stackframe
  282. * NOTICE: the calling funciton's return addr eip = ss:[ebp+4]
  283. * the calling funciton's ebp = ss:[ebp]
  284. */
  285. uint32_t ebp, eip;
  286. int i, j;
  287. ebp = read_ebp();
  288. eip = read_eip();
  289. for (i = 0; i < STACKFRAME_DEPTH; i++) {
  290. uint32_t *start = (uint32_t *)ebp + 2;
  291. cprintf("ebp:0x%08x eip:0x%08x args:", ebp, eip);
  292. for (j = 0; j < 4; j++) {
  293. cprintf("0x%08x", *(start + j));
  294. if (j != 3)
  295. cprintf(" ");
  296. }
  297. cprintf("\n");
  298. print_debuginfo(eip - 1);
  299. ebp = *((uint32_t *)ebp);
  300. eip = *((uint32_t *)ebp + 1);
  301. }
  302. }