[Previous] [Contents] [Index] [Next]

Caution: This version of this document is no longer maintained. For the latest documentation, see http://www.qnx.com/developers/docs.

Debugging a Graphics Driver

In this chapter...

A simple setup

Let's assume you have two machines: a remote debug terminal (Machine A) and your QNX Neutrino target (Machine B).

The remote debug terminal, Machine A, can be running QNX Neutrino or any system that supports a telnet connection into Machine B. (Some prefer to run QNX Neutrino on Machine A as well.)


Note: In order to support a telnet session on Machine B, be sure to run the inetd services (e.g. as root, type: inetd &).

Once you have a telnet session, you should be able to login to Machine B from Machine A and get a shell prompt.

As an alternative to telnet, you could use qtalk to communicate to both machines over a serial link.


Note: Unless noted otherwise, you should run all the commands described in the following sections on Machine B, your QNX Neutrino target system.

Location of the Graphics DDK

The Graphics Driver Development Kit as downloaded (version 1.00) is in the form of a package, which should install to ddk_install_dirddk-graphics.

Compiling shared objects

After installation, you'll first need to compile the shared objects needed by the graphics driver.

  1. cd to the ddk_install_dir/ddk-graphics directory.
  2. Type:
    make

You can individually go in to each subdirectory (ffb, disputil, etc.) and make from the lowest level if you choose.

Building for a particular OS or architecture

You can easily specify the hardware platform (e.g. x86):

To build for: Use this command:
x86 only CPULIST=x86 make
PPC only CPULIST=ppc make

Note: The libdisputil.so and libffb.so objects that we created have slightly different symbol information than the original ones that exist in /usr/lib.

Note also that you need to add the extension ".2" to the files libdisputil.so and libffb.so in order for the loader to locate them.


In order to run our debug driver, we'll need these newer libraries that we just compiled. We'll need to compile against them as well as have them available in our library path so that they're loaded in when we start our driver.

We recommend that you make a backup of the original libdisputil.so and libffb.so objects and then copy the newly compiled ones on top of the existing versions. For example:

cd /usr/lib
cp libdisputil.so.1 libdisputil_orig.so.1
cp libffb.so.1 libffb_orig.so.1

cp ddk_working_dir/prebuilt/cpu variant/libdisputil.so  /usr/lib/libdisputil.so.1
cp ddk_working_dir/prebuilt/cpu variant/lib/ffb/nto/x86/so/libffb.so  /usr/lib/libffb.so.1

Note: Note that the make environment used for the Graphics DDK is the same as the recursive make as described in the Conventions for Makefiles and Directories in appendix of the QNX Neutrino Programmer's Guide.

Shipping modified libs with your product

If you wish to ship modified versions of the ffb or disputil libraries with your product, you should rename them so that they're not confused with the QNX-supplied libraries.

You'll also need to modify your driver's common.mk, so that it's linked against the renamed library. Generally, however, you link and test your driver against the QNX-supplied library binaries.

Setting the LD_LIBRARY_PATH environment variable

To make sure the loader can find all the binaries when Photon and the driver are started, you should set the LD_LIBRARY_PATH variable to point at their location. (Or, you could simply install them in /lib/dll, but using the environment variable is probably better for testing purposes).

For example, before starting Photon, type:

export LD_LIBRARY_PATH=/home/barney/test_drivers

Now, running crttrap trap should create a trap file with the entries required to have Photon use the sample drivers.


Note: The command at the top line of the trap file is the command that's run to start the graphics driver. In order to specify which mode is used when Photon starts, you may select a line for a different mode and place it at the top of the trap file.

The graphics file

Another important file is /etc/system/enum/devices/graphics. The system determines which particular driver to use with a particular chip, using the mappings in this graphics file.

If you want to add a new driver to the system, you need to add an entry to this file.

Making a debug version of a driver

In this example, we'll make a debug version of the devg-banshee driver.

  1. cd to the devg/banshee/nto/x86 directory. You'll see a dll directory.
  2. Make a dll.g directory at the same level as the dll directory.
  3. Copy the Makefile from inside the dll directory to the dll.g directory.
    Note: Both dll and dll.g use the very same Makefile -- the directory-naming convention is what tells make to compile with the -g debugging option.

  4. cd to dll.g and type:
    make                                          

    This should create a devg-banshee_g.so shared object.


    Note: We recommend that you copy the debug object to the same spot on disk as the non-debug object. This is okay because they have different names, and you'll invoke them differently.

  5. Copy the debug object:
    cp devg-banshee_g.so /lib/dll   

    The gdb debug example assumes that the debug object resides under the /lib/dll directory.

Running the debug driver and setting a breakpoint

Now let's try to run your debug driver (devg-banshee_g.so) and set a breakpoint on banshee_init(), which is a function within the devg-banshee_g.so shared object.

There's a slight complication here: we can't set the breakpoint under gdb until after the banshee DLL is loaded. What we want to do is freeze io-graphics after it has loaded devg-banshee_g.so, but before banshee_init() has been called.

Note how io-graphics gets the address of the banshee_init() function before it can call it: it obtains it via the devg_get_modefuncs() entry point. This means that devg_get_modefuncs() has to get called before banshee_init() (or any other modeswitcher entry point for that matter).

So in order to gain control under gdb before any of the modeswitcher entry points get called, we can add the following line of code to the devg_get_modefuncs() function:

kill(0, SIGSTOP);

What we're doing here is dropping a "stop" signal on our own process, io-graphics. Once io-graphics is in a stopped state, we can attach to it with gdb.

Now we're ready to start Photon.

If you're starting Photon manually, or via a custom script, you could launch io-graphics with the following command:

io-graphics -g1024x768x32 -dldevg-banshee_g.so -I0 -d0x121a,0x5 -R75

If you're using the ph script (which launches io-graphics via crttrap) you could place the above line at the top of the file /etc/system/config/graphics-modes.

When io-graphics runs, it loads your debug driver, but when it hits the kill() function in devg_get_modefuncs(), it goes into a stopped state.

From a telnet or serial session, type:

pidin | grep io-graphics

to find the process ID of io-graphics, so that we can attach to it with gdb.


Note: You need to ensure that gdb looks in the same location for your driver shared object as the run timer loader (otherwise the symbol table in gdb won't match the binary you're trying to debug!)

You can do this by setting the solib-search-path variable inside gdb. Here's an example for an x86 target:

(gdb) set solib-search-path /x86/lib:/x86/usr/lib:/x86/usr/dll:/home/barney/test_drivers

You could place the above command line in your .gdbinit file (note the leading period) so that it's automatically executed when gdb starts. This file is located in your home directory.


Now we can start gdb, attach to io-graphics using the process ID that we got from pidin, load the symbol table from the debug driver, and set a breakpoint:

(gdb) attach 123456
(gdb) shared
(gdb) break banshee_init
(gdb) cont

The gdb debugger should regain control when io-graphics hits the breakpoint in banshee_init().


[Previous] [Contents] [Index] [Next]