1.de d0 2.nr dP +1 3.nr dV +1p 4.. 5.de d1 6.nr dP -1 7.nr dV -1p 8.. 9.nr dT 4 10.de Af \" acid function 11.CW "\\$1(" "\fI\\$2\fP\f(CW)\fP" 12.. 13.TL 14Native Kernel Debugging with Acid 15.AU 16Tad Hunt 17tad@plan9.bell-labs.com 18.br 19Lucent Technologies Inc 20.br 21(Revised 22 May 2000 by Vita Nuova) 22.SH 23Introduction 24.PP 25This tutorial provides an introduction to the Acid debugger. It assumes that you are familiar with the features of a typical source-level debugger. The Acid debugger is built round a command language with a syntax similar to C. 26This tutorial is not an 27introduction to Acid as a whole, but 28offers a brief tour 29of the basic built in and standard library functions, 30especially those needed for debugging native Inferno kernels on a target board. 31.PP 32Acid was originally developed by Phil Winterbottom 33to help debug multi-threaded programs in 34the concurrent language Alef, and provide more sophisticated 35debugging for C programs. 36In the paper 37.I "Acid: A Debugger Built From a Language" , 38Winterbottom 39discusses Acid's design, including some worked examples of unusual 40applications of Acid to find memory leaks and assist code coverage analysis. 41Following that is the 42.I "Acid Reference Manual" , 43also by Phil Winterbottom, 44which gives a more precise specification of the Acid debugging language and its libraries. 45.SH 46Preliminaries -- the environment 47.PP 48Acid runs under the host operating system used for cross-development, 49in the same way as the Inferno compilers. 50Before running either compilers or Acid, the following 51environment variables must be set appropriately: 52.TS 53center; 54lf(CW) lf(R)w(4i) . 55ROOT T{ 56the directory in which Inferno lives (eg, 57.CW /usr/inferno ). 58T} 59SYSHOST T{ 60.I host 61operating system type: 62.CW Nt , 63.CW Solaris , 64.CW Plan9 , 65.CW Linux 66or 67.CW FreeBSD 68T} 69OBJTYPE T{ 70.I host 71machine's architecture type: 72.CW 386 , 73.CW sparc , 74.CW mips , 75or 76.CW powerpc 77T} 78.TE 79They might be set by a login shell profile 80(eg, 81Unix 82.CW ".profile" , 83or 84Plan 9 85.CW lib/profile ). 86Also ensure that the directory 87.P1 88$ROOT/$SYSHOST/$OBJTYPE/bin 89.P2 90is on your search path. 91For example, on a Solaris sparc, one might use: 92.P1 93ROOT=\fIinferno_root\fP 94SYSHOST=Solaris 95OBJTYPE=sparc 96ACIDLIB=$ROOT/lib/acid 97PATH=$ROOT/$SYSHOST/$OBJTYPE/bin:$PATH 98export ROOT ACIDLIB PATH OBJTYPE SYSHOST 99.P2 100where 101.I "inferno_root" 102is the directory in which Inferno lives (eg, 103.CW "/usr/inferno" ). 104.SH 105An Example Program 106.PP 107The first example is not kernel code, but a small program that 108will be compiled but not run, to demonstrate basic Acid commands for 109source and object file inspection. 110The code is shown below: 111.P1 112int 113factorial(int n) 114{ 115 if (n == 1) 116 return 1; 117 return n * factorial(n-1); 118} 119 120int f; 121void 122main(void) 123{ 124 f = factorial(5); 125} 126 127void 128_main(void) 129{ 130 main(); 131} 132.P2 133.SH 134Compiling and Linking 135.PP 136The first step is to create an executable. The example shows the process for creating ARM executables. Substitute the appropriate compiler and linker for other cpu types. 137.P1 138% 5c factorial.c 139% 5l -o factorial factorial.5 140% ls 141factorial 142factorial.5 143factorial.c 144.P2 145.SH 146Starting Acid 147.PP 148Even without the target machine on which 149to run the program, many Acid features are available. 150The following command starts debugging the 151.CW "factorial" 152executable. Note that, upon startup, Acid will attempt to load some libaries from the directory specified in the 153.CW "ACIDLIB" 154environment variable (defaults to 155.CW "/usr/inferno/lib/acid" ). 156It will also attempt to load the file 157.CW "$HOME/lib/acid" , 158in which you can place commands to be executed during startup. 159.P1 160% acid factorial 161factorial:Arm plan 9 executable 162 163$ROOT/lib/acid/port 164$ROOT/lib/acid/arm 165acid: 166.P2 167.SH 168Exploring the Executable 169.PP 170To find out what symbols are in the program: 171.P1 172acid: symbols("") 173etext T 0x00001068 174f D 0x00002000 175setR12 D 0x00002ffc 176end B 0x00002008 177bdata D 0x00002000 178edata D 0x00002008 179factorial T 0x00001020 180main T 0x00001048 181_main T 0x0000105c 182acid: 183.P2 184The output from the 185.CW symbols() 186function is similar to the output from the 187.I nm (10.1) 188command. The first column is the symbol name, the second column gives the section the symbol is in, and the third column is the address of the symbol. 189.PP 190There is also a 191.CW "symbols" 192global variable. Variables and functions can have the same names. It holds the list of symbol information that the 193.CW symbols 194function uses to generate the table: 195.d0 196.P1 197acid: symbols 198{{"etext", T, 0x00001068}, {"f", D, 0x00002000}, {"setR12", D, 0x00002ffc}, 199 {"end", B, 0x00002008}, {"bdata", D, 0x00002000}, {"edata", D, 0x00002008}, 200 {"factorial", T, 0x00001020}, {"main", T, 0x00001048}, {"_main", T, 0x00001 20105c}} 202acid: 203.P2 204.d1 205In large programs, finding the symbol you are interested in from a list that may be thousands of lines long would be difficult. The string argument of 206.CW symbols() 207is a regular expression against which to match symbols. 208All symbols that contain the pattern will be displayed. For example: 209.P1 210acid: symbols("main") 211main T 0x00001048 212_main T 0x0000105c 213acid: symbols("^main") 214main T 0x00001048 215acid: 216.P2 217The 218.CW symbols 219function is written in the 220.I acid 221command language and lives in the 222.CW "port" 223library 224.CW $ACIDLIB/port ). ( 225.P1 226defn symbols(pattern) 227{ 228 local l, s; 229 230 l = symbols; 231 while l do { 232 s = head l; 233 if regexp(pattern, s[0]) then 234 print(s[0], "\t", s[1], "\t", s[2], "\n"); 235 l = tail l; 236 } 237} 238.P2 239Acid retrieves the list of symbols from the executable and turns each one into a global variable whose value is the address of the symbol. If the symbol clashes with a builtin name or keyword or a previously defined function, enough 240.CW "$" 241characters are prepended to the name to make it unique. The list of such renamings is printed at startup. 242.PP 243Most acid functions operate on addresses. For example, to view the source code for a given address, use the 244.CW src 245function: 246.P1 247acid: src(main) 248/usr/jrf/factorial.c:10 249 5 return n * factorial(n-1); 250 6 } 251 7 252 8 int f; 253 9 void 254>10 main(void) 255 11 { 256 12 f = factorial(5); 257 13 } 258 14 259 15 void 260.P2 261The 262.Af "src" addr 263function displays a section of source code, with the line containing the address passed as an argument in the middle of the display. To print the assembly code beginning at a given address, use the 264.CW asm() 265function. 266.P1 267acid: asm(factorial) 268factorial 0x00001020 MOVW.W R14,#-0x8(R13) 269factorial+0x4 0x00001024 CMP.S $#0x1,R0 270factorial+0x8 0x00001028 MOVW.EQ $#0x1,R0 271factorial+0xc 0x0000102c RET.EQ.P #0x8(R13) 272factorial+0x10 0x00001030 MOVW R0,n+0(FP) 273factorial+0x14 0x00001034 SUB $#0x1,R0,R0 274factorial+0x18 0x00001038 BL factorial 275factorial+0x1c 0x0000103c MOVW n+0(FP),R2 276factorial+0x20 0x00001040 MUL R2,R0,R0 277factorial+0x24 0x00001044 RET.P #0x8(R13) 278main 0x00001048 MOVW.W R14,#-0x8(R13) 279acid: 280.P2 281The output contains the symbolic address (symbol name+offset, where symbol name is the name of the enclosing function) followed by the absolute address, followed by the disassembled code. 282The 283.Af "asm" addr 284function prints the assembly beginning at 285.I "addr" 286and ending after either 30 lines have been printed, or the end of the function has been reached. The 287.CW "casm()" 288function continues the assembly listing from where it left off, even past the end of the function and into the next one. 289.P1 290acid: casm() 291main+0x4 0x0000104c MOVW $#0x5,R0 292main+0x8 0x00001050 BL factorial 293main+0xc 0x00001054 MOVW R0,$f-SB(SB) 294main+0x10 0x00001058 RET.P #0x8(R13) 295_main 0x0000105c MOVW.W R14,#-0x4(R13) 296acid: 297.P2 298All the functions presented so far are written in the acid command language. To see the source of a comand written in the acid command language, use the builtin command 299.CW "whatis [" "\fIname\fP\f(CW ]\fP." 300It prints the definition of the optional argument 301.I "name" . 302If 303.I "name" 304is an Acid builtin, 305.CW whatis 306prints 307.CW "builtin function" . 308.P1 309acid: whatis casm 310defn casm() { 311 asm(lasmaddr); 312} 313acid: 314acid: whatis atof 315builtin function 316acid: 317.P2 318If 319.I name 320is a variable, it prints the type of variable, and for the integer type, gives the format code used to print the value: 321.P1 322acid: whatis pid 323integer variable format D 324acid: 325.P2 326With no arguments, 327.CW whatis 328lists all available functions: 329.P1 330acid: whatis 331Bsrc bpmask follow new sh 332_bpconddel bpneq func newproc source 333_bpcondset bpor gpr next spr 334_stk bpprint include notestk spsrch 335access bppush interpret params src 336acidinit bpset itoa pcfile start 337addsrcdir bptab kill pcline startstop 338asm casm kstk pfl status 339atof cont labstk print stk 340atoi debug line printto stmnt 341bpaddr dump linkreg procs stop 342bpand error lkstk rc stopped 343bpconddel file locals readfile strace 344bpcondset filepc lstk reason symbols 345bpdel findsrc map regexp waitstop 346bpderef fmt match regs 347bpeq fnbound mem setproc 348acid: 349.P2 350The 351.Af "Bsrc" addr 352function brings up an editor on the line containing 353.I "addr" . 354It simply invokes a shell script named 355.CW "B" 356that takes two arguments, 357.I "-line" 358and 359.I "file" 360The shell script invokes 361.CW "$EDITOR +" 362.I "line file" . 363If unset, 364.CW "EDITOR" 365defaults to 366.I vi . 367The shell script, or the 368.CW Bsrc 369function can be easily rewritten to work with your favorite editor. 370.PP 371Entering a symbol name by itself will print the address of the symbol. Prefixing the symbol name with a 372.CW "*" 373will print the value at the address in the variable. Continuing to use our 374.CW "factorial" 375example: 376.P1 377acid: f 3780x00002000 379acid: *f 3800x00000000 381acid: 382.P2 383.SH 384Remote Debugging 385.PP 386Now that you have a basic understanding of how to explore the executable, it is time to examine a real remote debugging session. 387.PP 388We'll use the SA1100 keyboard driver as an example. Examining the kernel configuration file, you'll see the following: 389.P1 390dev 391 keyboard 392link driver/keyboard port 393 scanfujn860 kbd.h keycodes.h 394link ./../driver plat 395 kbdfujitsu ./../common/ssp.h \e 396 /driver/keyboard/kbd.h \e 397 /driver/keyboard/keycodes.h 398port 399 const char *defaultkeyboard = "fujitsu"; 400 const char *defaultkeytable = "scanfujn860"; 401 int debugkeys = 1; /* 1 = enabled, 0 = disabled */ 402.P2 403This describes the pieces of the keyboard driver which are linked into the kernel. The source code lives in two places, 404.CW "$ROOT/os/driver/keyboard" , 405and 406.CW "$ROOT/os/plat/sa1100/driver" . 407.PP 408The next step is to build a kernel. Use the 409.I mk 410target 411.CW acid 412to ensure that the Acid symbolic debugging data is 413produced. 414For example: 415.P1 416% mk 'CONF=sword' acid isword.p9.gz 417.P2 418This creates the Acid file 419.CW isword.acid , 420containing Acid declarations describing kernel structures, 421the kernel executable 422.CW isword.p9 ; 423and finally 424.I gzip s 425a copy of the kernel in 426.CW isword.p9.gz 427to load onto the device. Next, copy the gzipped image onto the device and then boot it. Follow the directions found elsewhere for details of this process. 428.PP 429From a shell prompt on the target device, start the remote debugger by writing the letter 430.CW r 431(for run) to 432.CW "#b/dbgctl" . 433Next, start Acid in remote debug mode, specifying the serial port it is connected to with the 434.CW "-R" 435option. 436.CW "$CONF" 437is the name of the configuration file used, for example 438.CW "sword" . 439.P1 440% acid -R /dev/cua/b -l i$CONF.acid i$CONF 441isword:Arm plan 9 executable 442$ROOT/lib/acid/port 443i$CONF.acid 444$ROOT/lib/acid/arm 445/usr/jrf/lib/acid 446acid: 447.P2 448You are now debugging the kernel that is running on the target device. All of the previously listed commands will work as described before, in addition, there are many more commands available. 449.SH 450Kernel Process Listing 451.PP 452To get a list of kernel processes, use the 453.CW "ps()" 454function: 455.P1 456acid: ps() 457PID PC PRI STATE NAME 4581 0x00054684 5 Queueing interp 4592 0x00000000 1 Wakeme consdbg 4603 0x00000000 5 Wakeme tcpack 4614 0x00000000 5 Wakeme Fs.sync 4625 0x00000000 4 Wakeme touchscreen 4636 0x00054684 5 Queueing dis 4647 0x00059788 5 Wakeme dis 4658 0x00054684 5 Queueing dis 4669 0x00054684 5 Queueing dis 46710 0x00054684 5 Wakeme dis 46811 0x0004c26c 1 Running dbg 469acid: 470.P2 471The 472.CW "PC" 473column shows the address the process was executing at when the 474.CW ps 475command retrieved statistics on it. The 476.CW "PRI" 477column lists process priorities. The smaller the number the higher the process priority. Notice that the kernel process (kproc) running the debugger is the highest priority process in the system. The only process you will ever see in the 478.CW "Running" 479state while executing the 480.CW ps 481command will be the debugger, since it is gathering information about the other processes. 482.SH 483Breakpoints 484.PP 485Breakpoints in Inferno, unlike most traditional kernel debuggers, are conditional breakpoints. There are minimally two conditions which must be met. These conditions are address and process id. A breakpoint will only be taken when execution for a specific kernel process reaches the specified address. The user can create additional conditions that are evaluated if the address and process id match. If evaluation of these conditions result in a nonzero value, the breakpoint is taken, otherwise it is ignored, and execution continues. 486.PP 487Again, the best way to proceed is with an example: 488.P1 489acid: setproc(7) 490.P2 491The 492.Af setproc pid 493function selects a kproc to which later commands will be applied; 494the one with process ID (\fIpid\fP) 495in this case. 496.P1 497acid: bpset(keyboardread) 498Waiting... 4997: stopped flush8to4+0x18c MOVW (R3<<#4),R3 500.P2 501After selecting a kproc, we set a breakpoint at the address referred to by the 502.CW "keyboardread" 503symbol. As described before, the value of a global variable created from a symbol in the executable is the address of the symbol. In this case the address is the first instruction in the 504.CW "keyboardread()" 505function. Notice that setting a breakpoint stops the kproc from executing. A bit later, we'll see how to get it to continue execution. 506.PP 507Next, display the list of breakpoints using 508.CW "bptab()" : 509.P1 510acid: bptab() 511ID PID ADDR CONDITIONS 5120 7 keyboardread 0x0003c804 { } 513.P2 514The first column is a unique number that identifies the breakpoint. The second column is the process ID in which the breakpoint will be taken. The third and fourth columns are the address of the breakpoint, first in symbolic form, then in numeric form. Finally, the last column is a list of conditions to evaluate whenever the kproc specified in the 515.CW "PID" 516column hits the the address specified in the 517.CW "ADDR" 518column. When they match, the list of conditions is evaluated. If the result is nonzero, the breakpoint is taken. Since we used the simplified breakpoint creation function, 519.CW "bpset()" 520, there are no additional conditions. Later on, we'll see how to set conditional breakpoints. 521.PP 522Start the selected kproc executing again, and wait for it to hit the breakpoint. 523.P1 524acid: cont() 525.P2 526The 527.CW "cont()" 528function will not return until a breakpoint has been hit, and there is no way to interrupt it. This means you should only set breakpoints that will be hit, otherwise you'll have to reboot the target device and restart your debugging session. 529.PP 530To continue our example, repeatedly hit new line (return, enter) 531on the keyboard on the target device, until the breakpoint occurs: 532.P1 533break 0: pid 7: stopped keyboardread SUB $#0xa4,R13,R13 534acid: 535.P2 536This message, followed by the interactive prompt returning tells you that a breakpoint was hit. It gives the breakpoint id, the kernel process id, then the symbolic address at which execution halted, followed by the disassembly of the instruction at that address. 537.PP 538The 539.CW "kstk()" 540function prints a kernel stack trace, beginning with the current frame, all the way back to the call that started the kproc. For each function, it gives the name name, arguments, source file, and line number, followed by the symbolic address, source file, and line number of the caller. 541.d0 542.P1 543acid: kstk() 544At pc:247812:keyboardread /usr/inferno/os/driver/keyboard/devkey 545board.c:350 546keyboardread(offset=0x0000009d,buf=0x001267f8,n=0x00000001) /usr 547/inferno/os/driver/keyboard/devkeyboard.c:350 548 called from kchanio+0x9c /usr/inferno/os/port/sysfile.c: 54975 550kchanio(buf=0x001267f8,n=0x00000001,mode=0x00000000) /usr/infern 551o/os/port/sysfile.c:64 552 called from consread+0x144 /usr/inferno/os/driver/port/d 553evcons 554consread(offset=0x0000009d,buf=0x0043d4fc,n=0x00000400,c=0x0044e 555c38) / 556usr/inferno/os/driver/port/devcons.c:357 557 called from kread+0x164 /usr/inferno/os/port/sysfile.c:2 55897 559kread(fd=0x00000006,n=0x00000400,va=0x0043d4fc) /usr/inferno/os/ 560port/sysfile.c:272 561 called from Sys_read+0x84 /usr/inferno/os/port/inferno.c 562:244 563Sys_read() /usr/inferno/os/port/inferno.c:229 564 called from mcall+0x98 /usr/inferno/interp/xec.c:590 565mcall() /usr/inferno/interp/xec.c:569 566 called from xec+0x128 /usr/inferno/interp/xec.c:1098 567xec(p=0x0044edd8) /usr/inferno/interp/xec.c:1077 568 called from vmachine+0xbc /usr/inferno/os/port/dis.c:706 569vmachine() /usr/inferno/os/port/dis.c:677 570 called from _main+0x50 /usr/inferno/os/plat/sa1100/infern 571o/main.c:237 572acid: 573.P2 574.d1 575There is another kernel stack dump function, 576.CW "lkstk()" 577which shows the same information as 578.CW "kstk()" 579plus the names and values of local variables. Notice that in addition to the 580`called from' 581information, each local variable and its value is listed on a line by itself. 582.d0 583.P1 584acid: lkstk() 585At pc:247812:keyboardread /usr/inferno/os/driver/keyboard/devkeyboard. 586c:350 587keyboardread(offset=0x00000018,buf=0x001267f9,n=0x00000001) /usr/inferno 588/os/driver/keyboard/devkeyboard.c:350 589 called from kchanio+0x9c /usr/inferno/os/port/sysfile.c:75 590 tmp=0x00000000 591kchanio(buf=0x001267f9,n=0x00000001,mode=0x00000000) /usr/inferno/os/por 592t/sysfile.c:64 593 called from consread+0x144 /usr/inferno/os/driver/port/devcons 594 c=0x0045a858 595 r=0x00000001 596consread(offset=0x00000015,buf=0x0043d4fc,n=0x00000400,c=0x0044ec38) /us 597r/inferno/os/driver/port/devcons.c:357 598 called from kread+0x164 /usr/inferno/os/port/sysfile.c:297 599 r=0x00000001 600 ch=0x0000006c 601 eol=0x00000000 602 i=0x00000000 603 mt=0x60000053 604 tmp=0x0007317c 605 l=0x0044ec38 606 p=0x00049754 607kread(fd=0x00000006,n=0x00000400,va=0x0043d4fc) /usr/inferno/os/port/sys 608file.c:272 609 called from Sys_read+0x84 /usr/inferno/os/port/inferno.c:244 610 c=0x0044ec38 611 dir=0x00000000 612Sys_read() /usr/inferno/os/port/inferno.c:229 613 called from mcall+0x98 /usr/inferno/interp/xec.c:590 614 f=0x0044eff0 615 n=0x00000400 616mcall() /usr/inferno/interp/xec.c:569 617 called from xec+0x128 /usr/inferno/interp/xec.c:1098 618 ml=0x0043d92c 619 f=0x0044eff0 620xec(p=0x0044edd8) /usr/inferno/interp/xec.c:1077 621 called from vmachine+0xbc /usr/inferno/os/port/dis.c:706 622vmachine() /usr/inferno/os/port/dis.c:677 623 called from _main+0x50 /usr/inferno/os/plat/sa1100/inferno/main. 624c:237 625 r=0x0044edd8 626 o=0x0044ee50 627.P2 628.d1 629The 630.CW "step()" 631function allows the currently selected process to execute a single instruction, and then stop. 632.P1 633acid: step() 634break 1: pid 7: stopped keyboardread+0x4 MOVW R14,#0x0(R13) 635acid: 636.P2 637The 638.CW "bpdel" ( 639.I id ) 640command deletes the breakpoint identified by 641.I id : 642.P1 643acid: bpdel(0) 644.P2 645The 646.CW "start()" 647command places the kproc back into the state it was in when it was stopped. 648.P1 649acid: start(7) 650acid: 651.P2 652Now lets look at how to set conditional breakpoints. 653.d0 654.P1 655acid: bpcondset(7, keyboardread, {bppush(_startup), bpderef()}) 656Waiting... 6577: stopped sched+0x20 MOVW #0xffffff70(R12),R6 658acid: bptab() 659ID PID ADDR CONDITIONS 6600 7 keyboardread 0x0003c804 { 661 {"p", 0x00008020} 662 {"*", 0x00000000} } 663acid: *_startup = 0 664acid: cont() 665.P2 666.d1 667Conditional breakpoints are set with 668.CW "bpcondset()" 669. It takes three arguments, the kernel process id, the address, and a list of stack based operations which are executed if the pid and addr match. The operations push values onto the stack, and if at the end of execution, a nonzero value is on the top of the stack, the breakpoint is taken. Examining the list of breakpoints with the 670.CW "bptab()" 671function shows the list of conditions to apply. The list is a bit confusing to read, but the 672.CW ""p"" 673means push and the 674.CW ""*"" 675means 676.I dereference . 677.PP 678No matter how much you type on the keyboard, this particular breakpoint will never be taken. That's because before continuing, we set the value at the address 679.CW "_startup" 680to zero, so whenever execution reaches 681.CW "keyboardread" 682in kproc number 7, it pushes the address 683.CW "_startup" , 684then pops it and pushes the word at that address. Since the top of the stack is zero, the breakpoint is ignored. 685.PP 686This contrived example may not be all that useful, but you can use a similar method in your driver to examine some state before making the decision to take the breakpoint. 687.SH 688Examining Registers 689.PP 690There are three commands to dump registers: 691.CW gpr() , 692.CW spr() 693and 694.CW "regs()" . 695The 696.CW "gpr()" 697function dumps the general purpose registers, 698.CW "spr()" 699dumps special purpose registers (such as the 700.CW "PC" 701and 702.CW "LINK " 703registers), and 704.CW "regs()" 705dumps both: 706.d0 707.P1 708acid: regs() 709PC 0x0004a3b0 sched+0x20 /home/tad/inf2.1/os/port/proc.c:82 710LINK 0x0004b8e8 kchanio+0xa4 /home/tad/inf2.1/os/port/sysfile.c:75 711SP 0x00453c4c 712R0 0x00458798 R1 0x000fdf9c R2 0x0003c804 R3 0x00000000 713R4 0xffffffff R5 0x00000001 R6 0x00458798 R7 0x00000001 714R8 0x001267f8 R9 0x00000000 R10 0x0044ee50 R11 0x00029f9c 715R12 0x000fc854 716acid: 717.P2 718.d1 719.SH 720Complex Types 721.PP 722When reading in the symbol table, Acid treats all of the symbols in the executable as pointers to integers. This is fine for global integer variables, but it makes examining more complex types difficult. Luckily there is a solution. Acid allows you to create a description for more complex types, and a function which will automatically be called for these complex types. In fact, the compiler can automatically generate the acid code to describe these complex types. For example, if we wanted to print out the devtab structure for the keyboard driver, we can just give its name: 723.P1 724acid: whatis keyboarddevtab 725integer variable format a complex Dev 726acid: keyboarddevtab 727 dc 107 728 name 0x0010e0ea 729 reset 0x0003c3fc 730 init 0x0003c438 731 attach 0x0003c5dc 732 clone 0x000480d0 733 walk 0x0003c600 734 stat 0x0003c640 735 open 0x0003c680 736 create 0x0004881c 737 close 0x0003c768 738 read 0x0003c804 739 bread 0x0004883c 740 write 0x0003c968 741 bwrite 0x00048900 742 remove 0x00048978 743 wstat 0x00048998 744acid: 745.P2 746Acid knows the keyboarddevtab variable is of type Dev, and it prints it by invoking the function Dev(keyboarddevtab). 747.P1 748acid: whatis Dev 749complex Dev { 750 'D' 0 dc; 751 'X' 4 name; 752 'X' 8 reset; 753 'X' 12 init; 754 'X' 16 attach; 755 'X' 20 clone; 756 'X' 24 walk; 757 'X' 28 stat; 758 'X' 32 open; 759 'X' 36 create; 760 'X' 40 close; 761 'X' 44 read; 762 'X' 48 bread; 763 'X' 52 write; 764 'X' 56 bwrite; 765 'X' 60 remove; 766 'X' 64 wstat; 767}; 768.P3 769defn Dev(addr) { 770 complex Dev addr; 771 print("\etdct",addr.dc,"\en"); 772 print("\etnamet",addr.nameX,"\en"); 773 print("\etresett",addr.resetX,"\en"); 774 print("\etinitt",addr.initX,"\en"); 775 print("\etattacht",addr.attachX,"\en"); 776 print("\etclonet",addr.cloneX,"\en"); 777 print("\etwalkt",addr.walkX,"\en"); 778 print("\etstatt",addr.statX,"\en"); 779 print("\etopent",addr.openX,"\en"); 780 print("\etcreatet",addr.createX,"\en"); 781 print("\etcloset",addr.closeX,"\en"); 782 print("\etreadt",addr.readX,"\en"); 783 print("\etbreadt",addr.breadX,"\en"); 784 print("\etwritet",addr.writeX,"\en"); 785 print("\etbwritet",addr.bwriteX,"\en"); 786 print("\etremovet",addr.removeX,"\en"); 787 print("\etwstatt",addr.wstatX,"\en"); 788} 789.P2 790Notice the complex type definition and the function to print the type both have the same name. If we know that an address is the address of a complex type, even though acid may not 791(say we're storing multiple types of data in a void pointer), 792we can print the complex type by calling the type printing function ourselves. 793.P1 794acid: print(fmt(keyboarddevtab, 'X')) 7950x00106d50 796acid: Dev(0x00106d50) 797 dc 107 798 name 0x0010e0ea 799 reset 0x0003c3fc 800 init 0x0003c438 801 attach 0x0003c5dc 802 clone 0x000480d0 803 walk 0x0003c600 804 stat 0x0003c640 805 open 0x0003c680 806 create 0x0004881c 807 close 0x0003c768 808 read 0x0003c804 809 bread 0x0004883c 810 write 0x0003c968 811 bwrite 0x00048900 812 remove 0x00048978 813 wstat 0x00048998 814acid: 815.P2 816.SH 817Conclusion 818.PP 819This introduction to using Acid for remote debugging Inferno kernels should be enough to get you started. As a tutorial, it only describes how to use some of the features of the debugger, and does not attempt to describe how to do advanced debugging such as writing your own functions, or modifying existing ones. Exploring the source, setting breakpoints, single stepping through code, and examining the contents of variables are the usual uses of a debugger. This tutorial gives examples of all of these. 820.PP 821For a more in depth discussion of the acid command language, and how to write your own acid functions, see the manual page 822.I acid (10.1) 823and Phil Winterbottom's papers on the Acid Debugger, 824reprinted in this volume. 825.TL 826Appendix 827.LP 828There are two important differences between Acid described in the 829accompanying paper, and Acid as distributed with Inferno for use in 830kernel debugging. 831.SH 832Connecting Acid to the remote Inferno kernel 833.PP 834A remote Plan 9 kernel can be debugged in the same 835way as a Plan 9 user process, using the 836file server 837.I rdbfs (4). 838It is a user-level file server on Plan 9 that 839uses a special debugging protocol on a serial connection to 840the remote kernel, but on the Plan 9 side serves a file system interface 841like that of 842.I proc (3), 843for use by Acid. 844Acid therefore does not need any special code to access the remote kernel's memory, 845or exert control over it. 846.PP 847Inferno's version of Acid currently runs under the host operating systems, 848which do not support such a mechanism (except for Plan 9). 849Instead, Acid itself provides a special debugging protocol, 850with (host) platform-specific interface code to access a serial port. 851This might well be addressed in future by implementing the native kernel debugger 852in Limbo. 853.SH 854Handling of breakpoints 855.PP 856.de Ip 857.KS 858.LP 859.tl '\f2\\$1\fP\ \ \f(CW\\$2(\f2\\$3\f(CW)\f1''\\$4' 860.IP 861.. 862.de Ex 863.KE 864.KS 865.IP 866.ft CW 867.ta 4n +4n +4n +4n +4n +4n +4n +4n +4n +4n +4n +4n +4n +4n +4n +4n 868.nf 869.in +4n 870.br 871.. 872.de Ee 873.fi 874.ft 1 875.br 876.in -4n 877.KE 878.. 879The following functions are provided by the Acid library 880.CW $ROOT/lib/acid/$OBJTYPE 881for use in native kernel debugging. 882In several cases they change the behavior described in the Acid manual. 883The functions are: 884.P1 885 id = bpset(addr) 886 id = bpcondset(pid, addr, list) 887 bppush(val) 888 bpderef() 889 bpmask() 890 bpeq() 891 bpneq() 892 bpand() 893 bpor() 894 bptab() 895 addr = bpaddr(id) 896 bpdel(id) 897 bpconddel(id) 898.P2 899.PP 900With traditional breakpoints, when a program reaches an address at which a breakpoint is set, execution is halted, and the debugger is notified. In applications programming, this type of breakpoint is sufficient because communicating the break in execution to the debugger is handled by the operating system. The traditional method of handling breakpoints breaks down when program being debugged is the kernel. A breakpoint cannot entirely suspend the execution of the kernel because there is no other program that can handle the communication to the debugger. 901.PP 902Some operating systems solve this problem by including a 903`mini' operating system, 904a self-contained program within the kernel that has its own code to handle the hardware used to communicate with the remote debugger or user. There are many problems with this mechanism. First, the debugger code that lives inside the kernel must duplicate a lot of code contained elsewhere in the kernel. This makes the kernel much bigger, and can increase maintenance costs. Typically this type of debug support treats the kernel as having a single thread of control, so a breakpoint stops everything while the user decides what to do about it. The only places in the kernel breakpoints cannot be set are in the debugger itself, and in the code that handles notifying the debugger of the breakpoint. 905.PP 906The Inferno kernel takes a different approach. The remote debug support is provided by a device driver that makes use of kernel services. Communication with the remote debugger is handled by a kernel process dedicated entirely to that task. All breakpoints can be considered to be minimally conditional on two values. First, the address to take the break at, and second, the kernel process to take the break in. This method allows the kernel debugger to be implemented as a regular Inferno device driver. The device driver can make use of all the APIs available to device drivers, it does not need to be self contained. Additionally, conditional breakpoints can be set anywhere in the kernel, with two exceptions. As with traditional debugger implementations, breakpoints can not be set in the code that handles notifying the debugger of the breakpoint. Unlike traditional implementations, the code that handles the execution and evaluation of the conditions applied to the breakpoint is the only other place breakpoint 907cannot be set. Since both of these parts of the kernel code are self contained, the user can set breakpoints in any other kernel routines. For example, the user could set a breakpoint in 908.CW kread() , 909for a given kernel process, but the debugger can still call 910.CW kread() 911itself. 912.PP 913Use of conditional breakpoints can help make the debugging process more efficient. If there is a bug that occurs in the Nth iteration of a loop, with unconditional breakpoints, user intervention is required N-1 times before reaching the state the bug occurs in. Conditional breakpoints give the user the ability to automatically check the value of N, and only take the breakpoint when it reaches the critical value. 914.PP 915The following changed 916and additional functions in the Acid library provide access 917to this extended breakpoint support: 918.SH 919Setting Breakpoints 920.LP 921.\" 922.\" 923.\" 924.Ip integer bpset integer "Set a breakpoint 925.CW bpset 926places an unconditional breakpoint for the currently 927selected kernel process at the address specified 928by its 929.I integer 930argument. 931It returns the ID of the newly created breakpoint, or the nil list on error. 932It is simply shorthand for a call 933.Ex 934bpcondset(pid, addr, {}) 935.Ee 936where 937.I pid 938is the global variable identifying the currently selected process, 939.I addr 940is the user-supplied address for the breakpoint, 941and 942.CW {} 943is the empty list, signifying no conditions. 944.Ip integer bpcondset "pid,addr,list" "Set conditional breakpoint 945Sets a conditional breakpoint at addr for the kernel process identified by 946.I pid . 947The 948.I list 949argument is a list of operations that are executed when execution reaches 950.I addr . 951If execution results in a a non-zero value on the top of the stack, the breakpoint is taken, otherwise it is skipped. 952The 953.I list 954is in reverse polish notation format, and has these operations: 955.Ex 956PUSH 957DEREF (pop val, push *(ulong*)val) 958MASK (pop mask, pop value, push value & mask) 959EQ (pop v1, pop v2, push v1 == v2) 960NEQ (pop v1, pop v2, push v1 != v2) 961AND (pop v1, pop v2, push v1 && v1) 962OR (pop v1, pop v2, push v1 || v2) 963.Ee 964Condition lists are executed in a single pass, starting with the first command in the list, ending with the last. If a nonzero value is on the top of the stack at the end of execution, the breakpoint is taken, otherwise it is skipped. 965.RS 966.LP 967In effect, there are two mandatory conditions, the address of the breakpoint, and the kernel process id. These two conditions must be met for the condition list to be processed. If these conditions are met, the entire condition list is processed, there is no short circuit evaluation path. 968.LP 969For example, given the following code fragment: 970.P1 +.4i 971int i; 972 973for(i=0; i<1000; i++) { 974 ... 975} 976.P2 977the following call to 978.CW bpcondset() 979sets a conditional breakpoint to be taken when execution reaches 980.I addr 981in kernel process 982.I pid 983on the 500th iteration of the loop: 984.P1 +.4i 985bpcondset(pid, addr, {bppush(i), 986 bpderef(), bppush(500), bpeq()}); 987.P2 988.RE 989.SH 990Condition List Construction 991.LP 992.Ip list bppush val "Construct breakpoint stack 993Push val onto the stack. 994.KE 995.Ip list bpderef "" "Construct breakpoint stack 996Replace the value at the top of the stack with the value found at the address obtained by treating value at the top of the stack as an address. Pop the value on the top of the stack, treat it as a ulong*, and push the value at the address. 997.Ex 998addr = pop(); 999push(*(ulong*)addr); 1000.Ee 1001.Ip list bpmask "" "Construct breakpoint stack 1002Replace the top two values on the stack with the value obtained by masking the second value on the stack with the top of the stack. 1003.Ex 1004mask = pop(); 1005value = pop(); 1006push(value & mask); 1007.Ee 1008.Ip list bpeq "" "Construct breakpoint stack 1009Comparison of the top two values on the stack. Replace the top two values on the stack with a 1 if the values are equal, or a zero if they are not. 1010.Ex 1011v1 = pop(); 1012v2 = pop(); 1013push(v1 == v2); 1014.Ee 1015.Ip list bpneq "" "Construct breakpoint stack 1016Negative comparison of the top two values on the stack. Replace the top two values on the stack with a 0 if the values are equal, or 1 if they are not. 1017.Ex 1018v1 = pop(); 1019v2 = pop(); 1020push(v1 != v2); 1021.Ee 1022.Ip list bpand "" "Construct breakpoint stack 1023Logical and of the top two values on the stack. Replace the top two values on the stack with a 0 if both are zero, or 1 if both are nonzero. 1024.Ex 1025v1 = pop(); 1026v2 = pop(); 1027push(v1 && v2); 1028.Ee 1029.Ip list bpor "" "Construct breakpoint stack 1030Logical or of the top two values on the stack. Replace the top two values on the stack with a 1 if either is nonzero, 0 otherwise. 1031.Ex 1032v1 = pop(); 1033v2 = pop(); 1034push(v1 || v2); 1035.Ee 1036.SH 1037Breakpoint Status 1038.LP 1039.Ip {} bptab "" "List active breakpoints 1040Prints the list of breakpoints containing the following information in order: breakpoint number, kernel process id, breakpoint address, and the list of conditions to execute to determine if the breakpoint will be taken. 1041.Ex 1042acid: bptab() 1043ID PID ADDR CONDITIONS 10440 1 consread+0x20 0x216cc {} 1045acid: 1046.Ee 1047.Ip integer bpaddr id "Address of breakpoint 1048Returns the address the breakpoint identified by 1049.I id 1050is set to trigger on. 1051.KE 1052.SH 1053Deleting breakpoints 1054.Ip {} bpdel id "Delete breakpoint 1055Delete the breakpoint identified by 1056.I id . 1057Shorthand for bpconddel(). 1058.KE 1059.Ip {} bpconddel id "Delete conditional breakpoint 1060Delete the conditional breakpoint identified by the integer 1061.I id . 1062.KE 1063