dpcc(1)

NAME

dpcc - DProbes C compiler

SYNOPSIS

dpcc  [-achp?]  [-Iinclude_dir]  [-Drpn_def]  [-o outfile]
filename

DESCRIPTION

The DProbes C compiler, dpcc , provides a high-level language interface to the IBM Dynamic Probes debugging facility, dprobes. The dprobes facility itself provides an assembly-like language based on Reverse Polish Notation
(RPN) for writing user-defined probe-handlers, and allows
a certain limited set of objects in the probed program to
be specified symbolically, namely global variables and
functions. dpcc allows probe-handlers to be written using
a language comprising a substantial subset of ANSI C, and
allows most probed program structures, including stackbased objects such as function parameters and locals, to
be used symbolically in arbitrary 'probe expressions'.
dpcc supports all probe and module types provided by
dprobes i.e. user, kernel, and kernel module 'breakpoint' and 'watchpoint' probes.

The C language implemented by dpcc supports a large number
of ANSI C language features, and adds several others, most
notably exceptions and try/catch exception handling. See
the LANGUAGE REFERENCE section for an exhaustive description of the supported language, as well as a summary of
how the DProbes C language differs from ANSI C.

OPTIONS

The dpcc command-line has the following options:

-a print abstract syntax tree for the program (used
for debugging).
-c generate comments in the generated RPN code.
-D pass the give define to the preprocessor.
-h print help.
-I include the given directory in the search path for
files included in the program.
-o generate the RPN output to the given output file.
The default output file is the input filename with
the extension (e.g .dpc) replaced by .rpn.
-p ignore preprocessor errors. This is useful for
ignoring preprocessor errors.
-? print help.

OVERVIEW

This man page serves as a reference manual and also contains a 'quick start' and tutorial. Here's an outline of
its organization:
Quick Start
Compiling Probes
Inserting and Testing Probes
Language Reference
Language Summary
Semantic Differences From ANSI C
Compilation Phases
Language Details
Pragma List
Supported Types
Variable Scope and Storage
Global Variable Syntax
Operators
Functions
Conditional Statements and Expressions
Looping Statements
Exceptions and Exception Handling
Stack Traces
Library Functions
Built-in Functions
Adding Your Own Built-in Functions
Tutorial
Preliminaries
Examples
Diagnostics
Known Problems and Bugs
See Also

QUICK START

Compiling Probes

ArrayIf you get a 'probe not allowed' error message from the
dprobes command e.g. 'probe not allowed on opcode 0xcc',
first remove the current probes:
dprobes -r -a
then recompile the probe and insert again.
The triggerprobe program include with the compiler allows
the user-mode example probes to be triggered:

./triggerprobe
Executing triggerprobe will run test code that triggers
the example probes. See the probe source and triggerprobe.c for specifics.
In all cases, the output of the probe can be found in the
system log, /var/log/messages , for example. However, many of the examples make use of the utility functions
definde in "string.dph", which are designed to display
output to a 'debugging console'. This output can be
viewed in real-time by running the 'tailprint' Perl script
included with the compiler:

./tailprint
When the probe fires and executes one of the
print/printd/printu/printx utility functions defined in "string.dph", the output will be immediately printed to
the console window running tailprint. In practice, it's
most useful to have one console window running tailprint,
and the other actually tailing the system log (tail -f
/var/log/messages), in order to see output from probes
that don't use the tailprint as well as output from those
that do.
NOTE: If the system log on your system is not
/var/log/messages, the tailprint script must be edited to reflect the actual location of the system log i.e. replace
all occurrences of /var/log/messages with the correct location.

LANGUAGE REFERENCE

Language Summary

The C HLL language is basically ANSI C, with a few additions and several omissions, and some minor semantic differences. The grammar in c_hll_lang.y is derived from the grammar maintained by Jutta Degener, which can be viewed
at http://www.lysator.liu.se/c/ANSI-C-grammar-y.html.

Additions to ANSI C:

- exception handling (try/catch)

- ROL/ROR (<<</>>>)

Omissions:

- typedefs

- function pointers

- function prototypes

- register keyword

- volatile keyword

- auto keyword

- float, double, long long

- variable arg functions (ellipsis)

- array/struct initializers

- static blocks

- bitfields

- "old" style function definitions

- labeled statements and goto

Note that dprobes C doesn't provide an equivalent to malloc() ; there is no provision to dynamically allocate
objects at run-time, so struct/union objects can only be
created at compile-time either as global or static variables, or on the stack as local variables.

Probe program expressions use the gdb C parser and thus
may accept a slightly different grammar in some cases, and
they also don't support the following:

- floats, doubles

- function calls

- bitfields

Probe expressions do however support the use of typedefs,
so most probe expressions can be expressed using the same
type definitions found in the source code being probed.
Basically, anything that can be expressed using a gdb
expression can be expressed in the same way in a probe
expression.

Semantic Differences From ANSI C

ArrayFor watchpoints, these are:

#pragma MODNAME(modname)
#pragma PROBEPOINT_HANDLER(entry point function)
#pragma MODTYPE(user | kernel | kmod)
#pragma WATCHPOINT_LOCATION(location)
#pragma WATCHPOINT_TYPE(X | RW | W | IO)

In particular, the MODNAME should name the image containing debug symbols for the module being probed. Modules
being probed should be compiled using the -g option, and
for this version of the DProbes compiler, must NOT be compiled with the -fomit-frame-pointers flag. To compile a kernel image with debugging symbols, add the -g compile
flag and remove the -fomit-frame-pointers flag from the CFLAGS* variables in the top-level kernel Makefile and
recompile. For kernel probes, the file you'll name in the
MODNAME pragma is the vmlinux file in the top-level kernel source directory (the bootable image is stripped of all
debug info in a final phase of the make, so you won't
actually be running with a 10 M kernel!).

Pragma List

The DProbes C compiler supports the following pragmas,
most of which translate fairly directly to the corresponding dprobes.lang RPN header statements:

File pragmas (can only appear once per program file):

#pragma MODNAME(modname)
#pragma SYMBOLS(symbol_file)
#pragma MODTYPE(user | kernel | kmod)
#pragma MAJOR(major code)
#pragma JMPMAX(number)
#pragma LOGMAX(number)
#pragma EX_LOGMAX(number)
#pragma PRINTSTACKTRACE(yes | no)
#pragma PROBE_GROUPDEF(groupdef)
#pragma PROBE_TYPEDEF(typedef)

Probe pragmas (can appear once per probe):

Array#pragma PROBEPOINT_OPCODE(opcode)
#pragma WATCHPOINT_LOCATION(location)
#pragma WATCHPOINT_TYPE(X | RW | W | IO)
#pragma MINOR(minor code)
#pragma PASSCOUNT(number)
#pragma MAXHITS(number)
#pragma EX_MASK(number)
#pragma PROBE_GROUP(group)
#pragma PROBE_TYPE(type)
#pragma LOGONFAULT(yes | no)

The PROBEPOINT_HANDLER pragma names the 'entry-point' function for a probe. This function must be a parameterless, void-returning function defined following this
pragma. A given probe must name one and only one handler
function.

The PROBEPOINT_LOCATION can be either a function name or a "filename:line number" indicating the line number in the
source file corresponding to where the probe should be
applied (typically the filename:line number version is
what you want to use to log the value of a local variable).

In either case, the PROBEPOINT_LOCATION will be used to calculate an offset and opcode for probepoints, thus there
is no need for an explicit offet pragma, although the
opcode can be explicitly specified using the PROBEPOINT_OPCODE pragma if necessary.

The SYMBOLS pragma can be used if the symbols should be found somewhere other than in the module itself.

The PRINTSTACKTRACE pragma is used mainly to turn off stack tracing. By default, if a program contains
try/catch blocks, stack tracing will be enabled, unless
otherwise directed via this pragma.

Note that there are no pragmas defined to set the number
of local or global variables (i.e. dprobes lvar/gvar variables) - these values will be calculated and set by
the compiler, and are inaccessible to user programs.

Supported Types

The dprobes C language supports a limited set of variable types: integral ( char, short, int, long, as well as signed and unsigned variants), enums, structs, and unions. Pointers to the built-in integral types (including void )
and derived types are supported. Arrays of built-in and
derived types are also supported. Note that the granularity of 'memory' locations in the dprobes C language is the same as the machine word size, (e.g. 32 bits for Intel
x86), so all variables, including chars, occupy 32 bits
regardless of their size in the language.

Typedefs, function pointers, variable argument functions,
floating point types, long long types, and bitfields are
not yet supported.

Pointers are defined in the dprobes C grammar and are also used to store addresses of objects in the probed program,
so in addition to keeping track of their source, the compiler makes sure they're wide enough to hold addresses in
the target architecture.

Variable Scope and Storage

Within a probe program, variables declared outside of any
function default to the 'static' storage class, and are
visible only to functions within the given probe program
file. Variables outside of any function and qualified
with the extern(n) keyword are visible to all probe programs i.e. they're global. Function parameters and variables declared within functions default to the 'auto'
storage class, which means they're visible only within the
containing function, and don't retain their values between
invocations. Variables local to a function may be made to
retain their values between function invocations by qualifying them with the static keyword. extern and static variables are automatically initialized to 0. Uninitialized automatic variables have undefined contents.

Any variable declared within a block is local to that
block. Blocks are delimited by curly braces - {}.

Local variables and parameters are stored on the RPN
stack. All other variables are stored either in the
dprobes local or global variable arrays (lvar/gvar storage
areas). All variables have a size that is a multiple of
the machine word size (RPN stack width). From here on,
'word' refers to this size, not the 16-bit size as used in
the dprobes RPN language, unless otherwise noted.

Global Variable Syntax

Array}
Accessing function params in a function:
When the function is entered, the SBP register points to
the stack position when the function was entered, which is
the position following the last parameter that was pushed.
Since parameters are pushed in reverse order, the last
parameter pushed is the first function parameter. This
allows parameters to be accessed using their natural index
relative to SBP e.g. the first param index is 0 relative
to SBP, the second is 1 relative to SBP, etc.
Returning a value from a function:
If the function doesn't return a value, there's nothing to
do. Otherwise, there's a stack word reserved for the
return value and it must be filled in with the actual
return value. The reserved stack word is the stack word
just before the first param that was pushed onto the stack
before the call.
For clarity, here's a crude diagram of what the stack
looks like just after a function call (stack grows downward):

3 space for return value
2 third_param
1 second_param
0 first_param
SBP->
-1 local1
-2 local2
TOS->
See builtins.c for actual examples.

TUTORIAL

Preliminaries

This section presents dpcc concepts using a hands-on
approach, starting with the simplest probes and progressing through more advanced and useful features, explaining
things along the way. If you're more interested in 'just
the facts, ma'am', see the LANGUAGE REFERENCE section.

The probes presented here are complete and tested probes.
To try them out yourself, you can either cut and paste
from this document, or see the tut*.dpc files in the examples directory of the dpcc distribution.

Arraytree is a pointer to the root of a linked data structure
passed as a parameter to the probed function, find(). The
expression goes through a couple levels of structure
access and uses a complex expression as an array subscript.

Notice that the object being logged is an instance of a
struct point , which contains two integers. The output shows that both integers are automatically logged (the 3
initial bytes are a sentinel code and a count of how many
of the bytes following it correspond to this log statement), illustrating that the expression analyzer will figure out how many bytes to log by examining the type of
object being logged, where possible.

ArrayIn the first line of the probe-handler, the result of
evaluating list via the probe_expr() builtin function is assigned to the probe's test_node static variable, which
is a void *. The list variable within the context of the function being probed refers to a struct list_elt * parameter passed to the probed function, which is the head of a
linked list. The probe_expr() function evaluates its argument and returns a single value from the program being
probed, which can subsequently be stored in a probe variable. The value returned from probe_expr() or probe_expr_rel() can represent any value in the probed program and can be assigned to any probe program variable.
In this case, the value returned refers to a pointer in
the probed program, which is assigned to a void * probe
variable because there's no represention of a list_elt in
the probe itself, and there generally won't be unless it's
a pointer to a primitive type. Variables assigned the
value of probe expression results are just like any other
probe variable, with the exception of pointers, which have
to keep track of the fact that they refer not to a location in the probe program but rather to a location in the
probed program. Arithmetic operations applied to probe
pointers take this into account, i.e. pointer arithmetic
will work correctly according to what the pointer actually
points to.

Now that we have a pointer variable that contains a
pointer to the linked list in the probed program, we can
use the log_probe_expr_rel() and probe_expr_rel() builtin functions to traverse it and log each element's variables
along the way, until we reach the end of the list. The
controlling while loop checks at each iteration whether or
not the list_elt * pointer, test_node is NULL. If not, it uses that value to have log_probe_expr_rel() evaluate its probe expression relative to the passed-in test_node pointer and log the result. In this case, three such
calls are made, one to log a primitive list_elt member, an embedded array member and an embedded struct member. The
final line in the loop updates the test_node pointer variable to point to the next element in the linked list, via
the probe_expr_rel() function, which is similar to the probe_expr() function except that the probe expression next is evaluated relative to the current value of
test_node, which is subsequently updated with the new value.

Array0 11 0 0 0 0 4 0 2 0 0 0 0 4 0 8 0 0 0 0 4 0 12 0 0 0 0 4 0 3 0 0
0 0 4 0 7 0 0 0 0 4 0 13 0 0 0 0 4 0 4 0 0 0 0 4 0 6 0 0 0 0 4 0
14 0 0 0 0 4 0 5 0 0 0 0 4 0 5 0 0 0 0 4 0 15 0 0 0 0 4 0 6 0 0 0
0 4 0 4 0 0 0 0 4 0 16 0 0 0 0 4 0 7 0 0 0 0 4 0 3 0 0 0 0 4 0 17
0 0 0 0 4 0 8 0 0 0 0 4 0 2 0 0 0 0 4 0 18 0 0 0 0 4 0 9 0 0 0 0
4 0 1 0 0 0 0 4 0 19 0 0 0
Note that in this case as well, each logged item is preceded with a sentinel value and the number of bytes per
call i.e. each log call is preceded with 0 4 0.

ArrayIf you can read raw ASCII, you'll see that this says
"hello world!" (following the 3 sentinel and length
bytes).

Arrayint array[13];

Array#pragma PROBEPOINT_LOCATION("update_stats")
#pragma MODTYPE(kmod)
#pragma PROBEPOINT_HANDLER("test")

char * fn;

Array#pragma WATCHPOINT_TYPE(RW)
#pragma MODTYPE(kernel)
#pragma PROBEPOINT_HANDLER("starthere")

int val;

ArrayFinally, dprobes provides the ability to define multiple 'probepoints' per probe program file, and likewise, so
does dpcc:
#pragma MODNAME("/home/trz/dpcc-1.0.0/triggerprobe")
#pragma MODTYPE(user)
#pragma JMPMAX(65535)
#pragma PROBEPOINT_LOCATION("print_list")
#pragma PROBEPOINT_HANDLER("test")
/* View print output using tailprint. */
#include "string.dph"
int j;
void test()
{
j = probe_expr("list->next->val"); /* 1 */
printd("j: %d", j);
}
#pragma PROBEPOINT_LOCATION("test_fn")
#pragma PROBEPOINT_HANDLER("starthere")
int i;
unsigned int u=7;
unsigned int xval=0x0caaffee;
int test_return(char * str, int a, int b, int c)
{
int d;
int e;
int f;
d = a;
e = b;
f = c;
printd(str, e);
return 9;
}
void starthere()
{
i = test_return("hola %d", 6, 7, 8);
printd("i: %d", i);
printu("u: %u", u);
printx("x: %x", xval);
print("adios");
}
#pragma PROBEPOINT_LOCATION("find")
#pragma PROBEPOINT_HANDLER("test_find")
int test_key;
void test_find()
{
test_key = 13;
test_key = probe_expr("key"); /* 3 */
printd("key: %d", test_key);
}
This probe program contains 3 probepoints, each preceded
by pragmas that apply only to that probepoint. What we
really mean, then, when we talk about a probepoint, is
actually (at minimum) just a unique entry-point function
as defined by unique PROBEPOINT_HANDLER and PROBEPOINT_LOCATION pragma values. Non-handler functions are available to any of the probe-point handlers, as are
the global/static variables (these are only visible to
code following their declarations however).

DIAGNOSTICS

- If you get a 'probe not allowed' error message from
the dprobes command e.g. 'probe not allowed on
opcode 0xcc', first remove the current probes:

dprobes -r -a
then recompile the probe and insert again.
- If the Dprobes interpreter gets into a sort of
state where output isn't being printed, or only
exceptions are being logged, try removing and reinserting the dprobes module (assuming you compiled
it as a module):

rmmod dp
insmod dp

KNOWN PROBLEMS AND BUGS

- Executables (including the kernel and kernel mod
ules) must be compiled without the -fomit-framepointers flag in order for parameters/local variables to be accessible within probe expressions.
- Probes on inline functions not supported.
- Referencing global or static data in modules not
supported.
- Floating point not supported. This is really a
language non-feature rather than a bug, but as it
will in the future be supported, is listed here.
- long long not supported. This is really a language
non-feature rather than a bug, but as it will in
the future be supported, is listed here.
- struct and union definitions containing embedded
struct or union definitions aren't supported.
- The tailprint script can at times be fooled into
producing rubbish as output. It and the print() utility functions should be considered unsupported;
only the builtin logging functions are guaranteed
to work as advertised.
- Some of the the logging functions use instructions
that prefix logged data with sentinel values, while
others don't, so it may be possible to generate
ambiguous log data. This is a potential problem
mainly when multiple, interleaved logging calls are
made within the same probe handler.
- When logging local arrays three extra words are
prepended to the logged array. This is a result of
having no good way to log the middle of the stack.
- There's currently no way to specify param1/param2
for user exceptions.
- Don't pass a pointer containing a probe expression
result to a user-defined function (as opposed to
builtin functions, which work fine).

SEE ALSO

dprobes(8)

dprobes.lang(8)

The Heisenberg Debugging Technology, whitepaper by James Blandy and Michael Snyder (Cygnus/Redhat)

AUTHOR

IBM Corporation

The DProbes C Compiler is based partly on the agent
expressions code from gdb, the GNU Project Debugger.

The DProbes C grammar is based on the ANSI C grammar maintained by Jutta Degener.

VERSION

Version 1.0.0 Last Modified February 2002

LICENSE

dpcc is licensed under GNU General Public License version
2 or later.

Copyright (c) International Business Machines Corp., 2002
Copyright © 2010-2025 Platon Technologies, s.r.o.           Index | Man stránky | tLDP | Dokumenty | Utilitky | O projekte
Design by styleshout