This document describes how to draw function call graphs for Minix operating system running on Bochs emulator.
Japanese translation is available.
Sample call graph
fs:do_read fs:do_read+0x5 fs:read_wr fs:read_wr+0xa0 fs:get_fil fs:get_fil+0x2d ret fs:read_wr+0xa5 fs:read_wr+0x119 fs:sys_uma fs:sys_uma+0x2a fs:_taskca fs:_taskca+0x12 fs:_sendre
Runtime function call graph is helpful for studying source code.
Bochs, if configured with debugger enabled, can write each instruction executed to a log file. You can map each instruction address to the symbol using map files generated by nm. No modifications to the target operating system, Minix, are required.
Environment
Minix 3.1.0
Bochs-2.3.6 built with Microsoft VC++ 2005 and Platform SDK for Windows Server 2003 SP1
Step 1 Compile Minix. # cd /usr/src/tools # make hdboot Step 2 Generate symbol map files on Minix. # cd /usr/src/ # nm -n kernel/kernel > kernel.map # nm -n servers/fs/fs > fs.map etc. # more kernel.map kernel/kernel: 00000000 d ... 00002fe8 T _do_fork Step 3 Copy map files to the host computer. # mtools copy *.map a: and C:\>copy a:*.map your-bochs-dir Step 4 Get load addresses of kernel modules. On bootup, Minix boot loader prints following message. Use "snapshot" button of Bochs gui and save this to a file. d0p0s0>boot Loading Boot image 3.1.0 revision 0. cs ds text data bss stack 0000800 0005800 19568 3144 29808 0 kernel 0100000 0105000 19504 2360 48616 1024 pm 0111c00 011c400 42624 5556 5316124 2048 fs 0630000 0631400 4352 616 4696 131072 rs 0652c00 0659400 26144 4996 44192 1024 tty 0665c00 0667000 4784 764 3012 4096 memory 0669000 066a800 5904 504 63276 4096 log 067b400 0681400 23680 10776 10932 8192 AT:at_wini 0688c00 068a800 7088 2284 1356 768 init Step 5 On the host computer, rebuild Bochs with internal debugger support. Add --enable-debugger to configure arg, or modify .conf.win32-vcpp. See Bochs User Manual, "Compiling on Win32 with Microsoft VC++", and "Using Bochs internal debugger, Instruction tracing" for detail. Step 6 Enable debugger log file in .bochsrc. debugger_log: debugger.out Step 7 Hand-modify the script minixtrace.pl, which I wrote, and adjust kernel module load addresses according to the file in step 5. Ugly... Step 8 Boot new Minix with new Bochs. Step 9 Find the physical address of the function you want to trace and set a breakpoint there. This time, it is do_read in servers/fs/read.c. C:\>findstr do_read fs.map 000013d8 T _do_read On the other hand, Minix boot loader said, 0111c00 011c400 42624 5556 5316124 2048 fs So, 0x0111c00 + 0x000013d8 == 0x000112FD8 In Bochs, see the address. <bochs:2> disassemble 0x000112FD8 0x000112FF8 00112fd8: ( ): push ebp ; 55 00112fd9: ( ): mov ebp, esp ; 89e5 00112fdb: ( ): push 0x00000000 ; 6a00 00112fdd: ( ): call .+0x00000003 ; e803000000 00112fe2: ( ): pop ecx ; 59 00112fe3: ( ): leave ; c9 00112fe4: ( ): ret ; c3 Looks good. Now set breakpoints at the entry and exit of do_read. <bochs:2> break 0x000112FD8 <bochs:3> break 0x000112fe2 <bochs:4> info break Num Type Disp Enb Address 1 pbreakpoint keep y 0x00112fd8 2 pbreakpoint keep y 0x00112fe2 Step 10 Do something. Try read a file on Minix. # cat /home/kanda/hello > /dev/null Step 11 Breakpoint will hit. Enable the trace. (0) Breakpoint 1, 0x0000000000112fd8 in ?? () Next at t=564623263 (0) [0x00112fd8] 0007:00000000000013d8 (unk. ctxt): push ebp ; 55 <bochs:6> trace on Tracing enabled for CPU 0 Step 12 Continue and when the exit breakpoint hits, disable the trace. <bochs:7>c ... Lots of trace records... May take several minutes... (0) Breakpoint 2, 0x0000000000112fe2 in ?? () Next at t=564626075 (0) [0x00112fe2] 0007:00000000000013e2 (unk. ctxt): pop ecx ; 59 <bochs:8> trace off Tracing disabled for CPU 0 Step 13 Almost done. Disable break points and let Minix go. <bochs:9> bpd 1 <bochs:10> bpd 2 <bochs:11> c Step 14 On the host computer, run the script minixtrace.pl against the log file debugger.out. Be sure to have kernel.map etc. in the current directory. C:\>perl minixtrace.pl debugger.out | more (0) Breakpoint 1, 0x0000000000112fd8 in ?? () fs:do_read fs:do_read+0x5 fs:read_wr ... Enjoy.
Complete call graph of do_read
Script minixtrace.pl
In step 9, set breakpoints at the first instruction of the kernel, and at restart, which ends main in kernel/main.c.
00000431 T _restart <bochs:1> b 0x0800 <bochs:4> b 0x0c31
Set breakpoints at entry and exit of do_open in servers/fs/open.c. And open an existing regular file at the root directory.
# cat /hello.txt
00000a76 T _do_open 00000ae8 t _common_ <bochs:23> b 0x112676 <bochs:24> b 0x1126e6
Call graph of open.
Same breakpoints on do_open. Create a new file.
# date > /world.txt
Call graph of create.
Set breakpoints at do_exec in servers/pm/exec.c and at pm_exit.
00000e34 T _do_exec 00000913 T _pm_exit <bochs:1> b 0x100E34 <bochs:2> b 0x100913
Call graph of exec /usr/bin/date.Too large and I have not figured out what has happened.
Amsterdam Compiler Kit (ACK) cc truncates function names longer than 8 bytes.
I have built Minix 3.1.2a with gcc and generated a call graph with no name truncations, like this.
pm:do_fork pm:do_fork+0x7c pm:alloc_mem pm:alloc_mem+0xba ret pm:do_fork+0x81 pm:do_fork+0xce pm:sys_physcopy pm:sys_physcopy+0x49 pm:_taskcall
Here are tips.
1) Gcc requires 80 MB of memory.
# PATH=$PATH:/usr/gnu/bin # gcc helloworld.c gcc: installation problem, cannot exec `/usr/gnu/libexec/gcc/i386-pc-minix/3.4.3/cc1': Not enough core # size /usr/gnu/libexec/gcc/i386-pc-minix/3.4.3/cc1 text data bss stack memory 2535864 606696 542792 73400320 77085672 cc1
2) Gcc cannot parse Minix assembler .s files.
# gcc -c kernel/mpx386.s mpx386.s:2: Error: junk at end of line, first unrecognized character is `!' mpx386.s:46: Error: unknown pseudo-op: `.sect'
3) You cannot mix ack cc, anm, aal tool chain and gcc, gnm.
4) I failed to build Minix 3.1.0 book version on Minix 3.1.2a.
nb_send was missing. Userland library should be sync with the kernel. Not a gcc issue.
I used ack cc for /usr/src/kernel and built several servers with gcc.
# cd /usr/src/servers/fs # make CC=gcc ... Same for pm and other servers. # cd /usr/src/tools # make hdboot
installboot and boot loader worked fine.
Read through the Minix 3 book.
Study the do_read trace. Actually, I do not know what will happen after the do_read, so, I cannot tell even if the call graph generated is bogus:-)
Trim, remove, trivial functions such as strncmp.
Make clock tick less often and remove context switch noise.
Run and upload more call graphs.
Try Bochs "load-symbols" and "show call" debugger commands.
Q. Kernel panics.
Debug exception k_reenter = 1 process -4 (-IDLE), pc = 5:0xE74 Kernel panic: exception in a kernel task sys_call: trap 1 not allowed, caller -4, sr
A. I do not know which of Minix/Bochs is wrong, but commenting out the panic can be a workaround for now.
kernel/exception.c 98 /* XXX panic("exception in a kernel task", NO_NUM); */
And you may want to comment out fsck in /etc/rc, because fsck on an emulator is so slow.
if shutdown -C # fflag=t XXX
Q. Cannot type colon : or pipe | if keymap is set to japanese. I use Fedora Core 2 X Window environment.
A. jp106 keymap on Minix works fine on bare PC, on Bochs on Windows, and on Bochs on Fedora Core 2 CLI console. Try "display_library: term" in .bochsrc.
See Also
Minix on Bochs by Prof. Woodhull.
Some Linux kernel function trace results
Linux kernel function trace browser on Google App Engine (NEW)
Written by
Kanda.Motohiro@gmail.com
Last update July 4 2008