Acid Manual

Phil Winterbottom

philw@plan9.bell-labs.com

Introduction

Acid is a general purpose, source level symbolic debugger. The debugger is built around a simple command language. The command language, distinct from the language of the program being debugged, provides a flexible user interface that allows the debugger interface to be customized for a specific application or architecture. Moreover, it provides an opportunity to write test and verification code independently of a program’s source code. Acid is able to debug multiple processes provided they share a common set of symbols, such as the processes in a threaded program.

Like other language-based solutions, Acid presents a poor user interface but provides a powerful debugging tool. Application of Acid to hard problems is best approached by writing functions off-line (perhaps loading them with the include function or using the support provided by acme(1)), rather than by trying to type intricate Acid operations at the interactive prompt.

Acid allows the execution of a program to be controlled by operating on its state while it is stopped and by monitoring and controlling its execution when it is running. Each program action that causes a change of execution state is reflected by the execution of an Acid function, which may be user defined. A library of default functions provides the functionality of a normal debugger.

A Plan 9 process is controlled by writing messages to a control file in the proc(3) file system. Each control message has a corresponding Acid function, which sends the message to the process. These functions take a process id (pid) as an argument. The memory and text file of the program may be manipulated using the indirection operators. The symbol table, including source cross reference, is available to an Acid program. The combination allows complex operations to be performed both in terms of control flow and data manipulation.

Input format and whatis

Comments start with // and continue to the end of the line. Input is a series of statements and expressions separated by semicolons. At the top level of the interpreter, the builtin function print is called automatically to display the result of all expressions except function calls. A unary + may be used as a shorthand to force the result of a function call to be printed.

Also at the top level, newlines are treated as semicolons by the parser, so semicolons are unnecessary when evaluating expressions.

When Acid starts, it loads the default program modules, enters interactive mode, and prints a prompt. In this state Acid accepts either function definitions or statements to be evaluated. In this interactive mode statements are evaluated immediately, while function definitions are stored for later invocation.

The whatis operator can be used to report the state of identifiers known to the interpreter. With no argument, whatis reports the name of all defined Acid functions; when supplied with an identifier as an argument it reports any variable, function, or type definition associated with the identifier. Because of the way the interpreter handles semicolons, the result of a whatis statement can be returned directly to Acid without adding semicolons. A syntax error or interrupt returns Acid to the normal evaluation mode; any partially evaluated definitions are lost.

Using the Library Functions

After loading the program binary, Acid loads the portable and architecture-specific library functions that form the standard debugging environment. These files are Acid source code and are human-readable. The following example uses the standard debugging library to show how language and program interact:

% acid /bin/ls

/bin/ls:mips plan 9 executable

/sys/lib/acid/port

/sys/lib/acid/mips

acid: new()

75721: system call  _main ADD  $-0x14,R29

75721: breakpoint   main+0x4   MOVW  R31,0x0(R29)

acid: bpset(ls)

acid: cont()

75721: breakpoint   ls    ADD  $-0x16c8,R29

acid: stk()

At pc:0x0000141c:ls /sys/src/cmd/ls.c:87

ls(s=0x0000004d,multi=0x00000000) /sys/src/cmd/ls.c:87

    called from main+0xf4 /sys/src/cmd/ls.c:79

main(argc=0x00000000,argv=0x7ffffff0) /sys/src/cmd/ls.c:48

    called from _main+0x20 /sys/src/libc/mips/main9.s:10

acid: PC

0xc0000f60

acid: *PC

0x0000141c

acid: ls

0x0000141c

The function new() creates a new process and stops it at the first instruction. This change in state is reported by a call to the Acid function stopped, which is called by the interpreter whenever the debugged program stops. Stopped prints the status line giving the pid, the reason the program stopped and the address and instruction at the current PC. The function bpset makes an entry in the breakpoint table and plants a breakpoint in memory. The cont function continues the process, allowing it to run until some condition causes it to stop. In this case the program hits the breakpoint placed on the function ls in the C program. Once again the stopped routine is called to print the status of the program. The function stk prints a C stack trace of the current process. It is implemented using a builtin Acid function that returns the stack trace as a list; the code that formats the information is all written in Acid. The Acid variable PC holds the address of the cell where the current value of the processor register PC is stored. By indirecting through the value of PC the address where the program is stopped can be found. All of the processor registers are available by the same mechanism.

Types

An Acid variable has one of four types: integer, float, list, or string. The type of a variable is inferred from the type of the right-hand side of the assignment expression which last set its value. Referencing a variable that has not yet been assigned draws a "used but not set" error. Many of the operators may be applied to more than one type; for these operators the action of the operator is determined by the types of its operands. The action of each operator is defined in the Expressions section of this manual.

Variables

Acid has three kinds of variables: variables defined by the symbol table of the debugged program, variables that are defined and maintained by the interpreter as the debugged program changes state, and variables defined and used by Acid programs.

Some examples of variables maintained by the interpreter are the register pointers listed by name in the Acid list variable registers, and the symbol table listed by name and contents in the Acid variable symbols.

The variable pid is updated by the interpreter to select the most recently created process or the process selected by the setproc builtin function.

Formats

In addition to a type, variables have formats. The format is a code letter that determines the printing style and the effect of some of the operators on that variable. The format codes are derived from the format letters used by db(1). By default, symbol table variables and numeric constants are assigned the format code X, which specifies 32-bit hexadecimal. Printing a variable with this code yields the output 0x00123456. The format code of a variable may be changed from the default by using the builtin function fmt. This function takes two arguments, an expression and a format code. After the expression is evaluated the new format code is attached to the result and forms the return value from fmt. The backslash operator is a short form of fmt. The format supplied by the backslash operator must be the format character rather than an expression. If the result is assigned to a variable the new format code is maintained in the variable. For example:

acid: x=10

acid: print(x)

0x0000000a 

acid: x = fmt(x, ’D’)

acid: print(x, fmt(x, ’X’))

10 0x0000000a

acid: x

10

acid: x\o

12

The supported format characters are:

The function new() creates a new process and stops it at the first instruction. This change in state is reported by a call to the Acid function stopped, which is called by the interpreter whenever the debugged program stops. Stopped prints the status line giving the pid, the reason the program stopped and the address and instruction at the current PC. The function bpset makes an entry in the breakpoint table and plants a breakpoint in memory. The cont function continues the process, allowing it to run until some condition causes it to stop. In this case the program hits the breakpoint placed on the function ls in the C program. Once again the stopped routine is called to print the status of the program. The function stk prints a C stack trace of the current process. It is implemented using a builtin Acid function that returns the stack trace as a list; the code that formats the information is all written in Acid. The Acid variable PC holds the address of the cell where the current value of the processor register PC is stored. By indirecting through the value of PC the address where the program is stopped can be found. All of the processor registers are available by the same mechanism.

Types

An Acid variable has one of four types: integer, float, list, or string. The type of a variable is inferred from the type of the right-hand side of the assignment expression which last set its value. Referencing a variable that has not yet been assigned draws a "used but not set" error. Many of the operators may be applied to more than one type; for these operators the action of the operator is determined by the types of its operands. The action of each operator is defined in the Expressions section of this manual.

Variables

Acid has three kinds of variables: variables defined by the symbol table of the debugged program, variables that are defined and maintained by the interpreter as the debugged program changes state, and variables defined and used by Acid programs.

Some examples of variables maintained by the interpreter are the register pointers listed by name in the Acid list variable registers, and the symbol table listed by name and contents in the Acid variable symbols.

The variable pid is updated by the interpreter to select the most recently created process or the process selected by the setproc builtin function.