xref: /inferno-os/doc/acidtut.ms (revision 46439007cf417cbd9ac8049bb4122c890097a0fa)
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