Lackwit 1.0 Instructions

To use Lackwit to analyze a program, you first use the front-end tool (called "Lackwit") to construct a database containing information about the program. Then you use another tool ("BackEnd") to analyze the contents of the database, answer queries and display the results.

In the instructions below, we assume that $LACKWIT_HOME has been set to the Lackwit installation directory. The examples show how to analyze the code in $LACKWIT_HOME/demo/lislook.

Constructing the database

  1. Set the LACKWITDB environment variable to point to a work area where the database will be constructed.
    setenv LACKWITDB $LACKWIT_HOME/demo/lislook
    
  2. Put the Lackwit tools at the front of your path. This is particularly important because it puts the wrapper script for your compiler (cc or gcc) at the front of the path.
    setenv PATH $LACKWIT_HOME/bin:$PATH
    
  3. Initialize the database with information about built-in library functions, by running the dummy library code through the Lackwit front-end.
    EmitHeaders $LACKWIT_HOME/lib/Default.sigs 
    cc -c $LACKWIT_HOME/lib/libc.c -o /dev/null
    
    You can use any C compiler here. You should ignore the warnings and errors generated by the compiler.
  4. Rebuild your project from scratch using your normal build process. When 'make' (or whatever) tries to run your compiler ('gcc' or 'cc'), it will instead run the 'wrap_compiler' script in the Lackwit 'bin' directory. This script runs the compiler to preprocess the code, pipes the result into the Lackwit front-end, and then runs the compiler again to actually perform the build step. Thus the project will build as normal, and create the Lackwit database as a side-effect.
    rm -f *.o
    gmake
    
    This will work no matter what build process you use, as long as it searches your path to find which compiler to run. If your compiler isn't called 'gcc' or 'cc', just make a link in the Lackwit 'bin' directory from the compiler name to the 'wrap_compiler' script. One other thing: the build process must use the '-c' option to compile files; one-step compilation-and-link such as "gcc test.c -o a.out" will not work.
  5. Subsequently, whenever the code changes, you can rerun your build process and the database will be updated incrementally as your changes are recompiled.

Querying the database

  1. Run the BackEnd to analyze the database:
    BackEnd
    
    BackEnd will chug along; eventually, it will prompt you for a query.
  2. Run a query and return grep-like output:
    searchlocal lis_loadAdb:dbname
    
    This will output a list of the other pointer variables that may contain the pointer values that occur in dbname. Only the variables that actually have some operation performed on them are shown. (For example, if the pointer is stored in a structure which is simply passed on through a function without being actually accessed, that function will not show up in the output.)
  3. Run a query and invoke DaVinci to display the output:
    davinci lis_loadAdb:dbname
    
    This will launch DaVinci to display a graph of the function call connections that pass around the pointer values, and where the operations actually occur. This requires that you have DaVinci installed on the machine hosting BackEnd. DaVinci must be installed separately from Lackwit; see the DaVinci Web page for details.
  4. Run a query to try to find malloc/free memory leaks:
    searchglobal "dealloc ! alloc &"
    
    This will find all the variables that are NOT marked with the "dealloc" tag (that says that the value may eventually be passed to "free"), but that ARE marked with the "alloc" tag (that says that the value may eventually be passed to "malloc"). The argument is an expression in Reverse Polish Notation that is equivalent to "(NOT dealloc) AND alloc". Other literals that can be used in these queries are "ref" (to indicate that this value may occupy memory), "assign" (to indicate that the memory for this value may be overwritten with an assignment), and "deref" (to indicate that the memory for this value may be read). E.g. "assign ! ref &" will find constants and "deref ! ref &" will find unused data.

For more information about queries, see the reference documentation.

Troubleshooting

Warnings and errors during compilation

Some C compilers accept extensions to C that Lackwit doesn't understand. Even if you don't use these extensions in your programs, the vendor's .h files often use them and this can cause problems for Lackwit. Sometimes errors occur when you accidentally process C++ code with Lackwit, or process code that contains compilation errors. Warnings are produced when you use unknown identifiers in programs, and for certain non-fatal compiler errors. If only warnings are generated, then they can usually be safely ignored.

The problems caused by compiler extensions can often be fixed by passing certain options into the Lackwit front-end to tell it how to treat the unknown constructs. Run 'Lackwit -help' for a list of the available options, and look at the 'wrap_compiler' script for a list of options that cover most of the extensions provied by 'gcc'. Many other extensions can be handled by adapting that list. If all else fails, send me some preprocessed example code that triggers the problem.

Warnings and errors during analysis

'Duplicate definition' warnings

These messages are generated when there are non-static symbols with the same name defined in different files. The system chooses one definition (the one from the module that was most recently added to the database) and uses that, which is often the right choice. However it may indicate that there are actually multiple different programs being built, in which case you should use a different database for each program.

'Type mismatch' errors

These are internal errors in the Lackwit system. Please send preprocessed code that reproduces the errors to me.

'Undefined external' warnings

These errors occur when some referenced symbols were not defined. Usually these are library functions that I haven't provided dummy code for. The best approach for these is to provide the code, if you can. The system assumes that the undefined functions simply don't do anything, and references to undefined variables are simply ignored.

Other potential problems

Unfortunately, Lackwit is not fully "conservative"; some programs use (or more often abuse) language features in ways that cause the tool to miss some variables that it should include in response to a query. These features are generally non-portable and usually occur in programs that are very low-level systems code, very poorly designed, or buggy (or some combination of these :-) ).

Given these restrictions, Lackwit's results cannot be fully trusted for certain programs. If your knowledge of the code base or of the coding standard in use lets you rule out these kinds of behaviours, then there's nothing to worry about. (But be aware that most programs have bugs and bugs may cause these behaviours.) Otherwise, you can use Lackwit as a tool to aid your understanding of programs, keeping in mind that the only sure way to know what a C program does is to test it on the target machine.

Other information

Lackwit can be customized in many ways. The type system can be customized to give different interpretations of the basic operators, which can be helpful when using Lackwit for specialized applications. Also, user-defined functions can be given the same "tagging" support that "malloc" and "free" have, so that queries can be written to, for example, find windows that are never closed. For more information about how to do this kind of thing, you'll have to write to me personally.


Robert O'Callahan