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

351 lines
13 KiB

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