# ylang - Universal debugging/tracing language targeting gdb/systemtap/bcc/lldb/etc.

# Table of Contents

# Language Reference

The Y language (or ylang) closely follows the C language syntax. It tries hard to be (quite) compatible with the C11 standard.

Still, a few standard C language features are not supported yet:

  • C struct definitions with fields, for example,

    struct foo {
        int a;
        char *p;
    }
    

    Right now we must declare opaque C struct types only, as in

    struct foo;
    

    or

    typedef struct foo  foo;
    
  • C union definitions with member fields, for instance,

    union foo {
        int a;
        char *p;
    }
    

    Right now we must declare opaque C union types only, as in

    union foo;
    

    or

    typedef union foo  foo;
    
  • Standard C header files like stdint.h and stddef.h are not supported and should never be included in a ylang program via the #include directive. You are free, however, to include other ylang files via #include.

It is worth noting that the full gcc macro directives (including variadic macros!) are supported since we run gcc's preprocessor directly on the user ylang source.

Ylang also has varous of its own language extensions:

Back to TOC

# Macros

All the standard C preprocessor directives are supported, including gcc specific features (like variadic macros). Under the hood, the ylang compiler actually invokes gcc's preprocessor.

Addtionally, ylang supports the following special macro directives. They usually run before the standard C macro directives unless specificially specified.

Back to TOC

# ##ifdefvar

syntax: #ifdefvar VAR

This macro is a condition similar to #if, which evalutes to true when the top-level (static or global) variable VAR is defined in the debuginfo of the target process (including dynamically linked libraries).

This branch should be concluded with ##endif (or also with ##elifxxx).

Back to TOC

# ##ifndefvar

syntax: #ifndefvar VAR

This macro condition is similar to ##ifdefvar, but only evaluates to true when the top-level variable VAR is NOT defined in the target process.

# ##elifdefvar

syntax: #elifdefvar VAR

This macro is similar to ##ifdefvar but should only be used after those ##ifxxx or ##elifxxx directives.

# ##elifndefvar

syntax: #elifndefvar VAR

Similar to ##elifdefvar, but the condition is negated.

# ##else

syntax: #else

This macro directive is usually used after ##ifxxx or ##elifxxx directives.

# ##endif

syntax: #endif

This macro directive is used to conclude the branch created by ##ifxxx or ##elifxxx.

# ##error

syntax: ##error "MSG"

This macro is similar to the standard #error directive but runs at the same phase of the other ##xxx directives.

# ##ifdeffield

syntax: ##ifdeffield TYPE FIELD

This directive is similar to the standard macro directive #if, but evaluates to true when the user-specified FIELD exists in the type TYPE in the target process.

The TYPE can be a type name defined by typedef, a struct type name like struct foo, a union type name like union foo, or a enum type name like enum foo.

Consider there is a type defined in the target like this:

typedef struct {
    int       bar;
    long    **baz;
} foo_t;

Then both the following directives evalute to true:

##ifdeffield foo_t bar
##ifdeffield foo_t baz

The branch created by this directive must be concluded by ##endif (or also with ##elifxxx.

Back to TOC

# ##ifndeffield

syntax: ##ifdneffield TYPE FIELD

Similar to ##ifdeffield, but the condition is negated.

# ##elifdeffield

syntax: ##elifdefield TYPE FIELD

Similar to ##ifdeffield, but should only be used after other ##ifxxx or ##elifxxx directives.

# ##elifndeffield

syntax: ##elifndefield TYPE FIELD

Similar to ##elifdeffield, but with the condition negated.

# ##ifdeftype

syntax: ##ifdeftype TYPE

A conditonal which evaluates to true when the user-specified TYPE exists in the target process.

The TYPE can be a type name defined by typedef, a struct type name like struct foo, a union type name like union foo, or a enum type name like enum foo.

Back to TOC

# ##ifndeftype

syntax: ##ifndeftype TYPE

Similar to ##ifdeftype, but with the condition negated.

# ##elifdeftype

Similar to ##ifdeftype, but used as an "else if" variant.

# ##elifndeftype

Similar to ##ifndeftype, but used as an "else if" variant.

# ##yexe

syntax: ##yexe PATTERN

This macro directive chooses a different executable component (an executable program file or a dynamically linked library) by a string pattern. For example,

##yexe luajit

will choose the libluajit-5.1.so.2.1.0 library as the top-priority target component if this library exists when searching debuginfo. When a symbol appears in multiple components, then this directive can be very useful in disambiguating.

The same .y file can have multiple ##yexe directives. The scope of the directive extends to the next ##yexe directive (if any).

The pattern matching is more intelligent than a naive sub-string matching. Basically target component paths having the pattern at word boundaries will take precedence. For example, given the pattern libc, the libc-2.7.so file will be matched while libcat.so will not. On the other hand, if there are only libcat.so andglibc.so`, then the latter will win.

Back to TOC

# Backends-specific macros

The ylang backends define their own sets of built-in macros:

Back to TOC

####### Y_GDB_PY

This macro is defined as value 1 when the GDB Python backend is in use.

####### Y_STAP

This macro is defined as value 1 when the SystemTap backend is in use.

# Probes

Ylang supports specifying probes in the target processes, just like systemtap or dtrace.

Back to TOC

# Running probe-driven ylang programs with gdb

For the gdb python backend, a ylang program with user-defined probes would result in a special gdb command of the same name of the basename of the ylang input file which runs an event loop which keeps firing probes until the target process terminates or the _exit() built-in function is called in one of the probe handler fired. For example, if you have a foo.y input file which defines some probe handlers, then the ylang --gdb-py foo.y command would generates a python code file foo.py, which when loaded, we can run the probe event loop by issuing the gdb command foo after the (gdb) prompt.

Consider the following C target program

/* target.c */

int foo(int i) {
    return i + 1;
}

int main(void) {
    int sum = 0;
    for (int i = 0; i < 3; i++) {
        sum += foo(i);
    }
    return sum;
}

and then the ylang source file

/* tracer.y */

_probe foo() {
    printf("foo() fired!\n");
}

We first compile the target C program like this:

# here we have to use -O0 to compile otherwise gcc may optimize away the
# foo() function calls.
gcc -O0 -g target.c  # generates ./a.out

And then, we generate a symbol table file from the debuginfo in the executable file ./a.out:

dw2c.pl --exe ./a.out --gen-symtab -o a.jl

Next, compile the tracer.y into a python source file, using the symbol table file ./a.jl:

resty ylang.ljbc -I ./lua --symtab a.jl --gdb-py-out tracer.py tracer.y

Finally, we run everything like this:

gdb --nx --batch -ex 'set python print-stack full' \
    -ex 'file ./a.out' -ex 'source tracer.py' -ex 'tracer'

The last command should generate output like below:

Breakpoint 1 at 0x40048e: file target.c, line 4.
foo() fired!
foo() fired!
foo() fired!
[Inferior 1 (process 75273) exited normally]

The first line and last line of the output were produced by gdb automatically while the middle 3 lines were generated by our own ylang probe handlers firing upon the entry point of the C function foo call.

The automatically generated gdb command can also accepts extra arguments which will simply be forwarded to the target program's command line if the target is not running yet.

For an already running target program, we can use the following gdb command to run our tracer tool:

gdb --nx --batch -ex 'attach 75317' -ex 'source tracer.py' -ex 'tracer'

where the magical number 75317 is the pid of the running program. Once we're done, we can simply use Ctrl-C to end this gdb command session and simply leave the target process to continue running without any tracers attached.

Back to TOC

# The gdb-tracer tool

We also provide the ./bin/gdb-tracer tool to wrap around the gdb command and make gdb-based dynamic tracing even easier. For example, the gdb command line above can be replaced by the following much simpler command:

gdb-tracer tracer.py -p 75317

assuming the directory of ./bin/gdb-tracer has already been added to the PATH system environment.

The gdb-tracer tool does not support tracing multiple processes at the same time due to a bug in gdb, tracing multiple processes at the same time may leave some of the processes in a bad state and would crash due to segmentation faults after getting detached from the gdb session.

It is also possible to ask gdb-tracer to run a new command and trace it at the same time. It is achieved by the -c CMD option like this:

# start the perl process and trace it right away:
gdb-tracer tracer.py -c "perl a.pl"

Back to TOC

# Timer probes

We support the following systemtap-style timer probes:

Back to TOC

####### _timer.profile

Probing on system profiling timers which provide probes that execute on all CPUs at the rate of the system tick (CONFIG_HZ).

For the systemtap backend, this probe maps to probe timer.profile. It is not (yet) supported in the GDB Python backend.

For example,

_probe _timer.profile {
    ...
}

Back to TOC

####### _timer.s(N)

The probe handler is run every N second. The actual resolution of the timers depends on the target kernel and the target architecture.

For the systemtap backend, this probe maps to probe timer.s(N).

For instance,

/* triggered every 3 seconds. */
_probe _timer.s(3) {
    ...
}

Back to TOC

####### _timer.ms(N)

The probe handler is run every N millisecond. The actual resolution of the timers depends on the target kernel and the target architecture.

For the systemtap backend, this probe maps to probe timer.ms(N).

For instance,

/* triggered every 200 milliseconds. */
_probe _timer.ms(200) {
    ...
}

Back to TOC

####### _scheduler.cpu_on

This probe handler runs when the OS kernel scheduler switches a process onto a CPU for execution.

Back to TOC

####### _scheduler.cpu_off

This probe handler runs when the OS kernel scheduler switches a process off a CPU for sleeping (or waiting for IO events and etc).

Back to TOC

####### _begin

This probe runs at the very beginning of the tracing tool. It does not have any target process contexts yet.

Back to TOC

####### _end

This probe runs at the very end of the tracing tool (even when the tool is quitting via _exit()). It does not have any target process contexts associated.

Back to TOC

####### _syscall.NAME

This probes runs at the entry point of the system call named NAME.

Currently this probes only works in the systemtap backend.

Back to TOC

####### _syscall.NAME.return

This probes runs at the return point of the system call named NAME.

Currently this probes only works in the systemtap backend.

This probe point syntax is subject to change in the near future without notice.

Back to TOC

# C code label probes

We can define dynamic probes on code labels (which are usually the targets for goto statements in the C/C++ languages), like this:

_probe foo() :my_label_name {
    ...
}

Here my_label_name is assumed to be a code label name in the target program.

Back to TOC

# C function entry probes

We can define dynamic entry probes for a C function in the target process like this:

_probe foo() {
    // ...
}

Here we ignores the parameter signature of the target C function foo. We can also specify the parameter list explicitly and then reference the parameter variables inside the probe handler body, as in

_probe foo(int a, char *p) {
    printf("a = %d, p = %p\n", a, p);
}

Back to TOC

# C function return probes

We can also define dynamic return probes for C functions in the target process, using the following syntax:

_probe foo() -> int {
    // ...
}

Here we must specify the return value type after the special arrow notation (->).

We can also give the return variable a name so that we can reference its value inside the probe handler body, as in

_probe foo() -> int bar {
    printf("returning value %d\n", bar);
}

Back to TOC

# User-defined tracer functions
# Helper functions

We can define helper functions in the tracer space just like defining normal C functions, as in

int foo(int a) {
    return a + 1;
}

And then we can call it in other user functions or user probe handlers, using the same syntax of a C function call:

int b = foo(3);

Both the return type and the paramter types can use ylang's built-in data types like _str and built-in array/hash types.

User-defined functions must not specify the _target specifier. Otherwise it would become declarations of the C functions in the target process space.

Back to TOC

# Command functions

Functions declared with the _cmd specifier are special "command functions". For backends that support it, like the GDB python backend, these "command functions" would generate new GDB commands of the same names. For example,

_cmd void foo(int a) {
    printf("value is %d.\n", a);
}

would yield a new GDB command named "foo" which can be used after the gdb prompt directly, as in

(gdb) foo 3
value is 3.

The last line above is the output of the command foo 3.

Command functions must take the return value type void. It could also take no parameters by specifying the parameter list as (void).

Command functions can also be called just like other user-defined functions.

In non-GDB backends like systemtap, command functions are identical to other user-defined functions.

Back to TOC

# Built-in types
# _str

This type is semantically similar to C++'s standard string type, but with different API functions used to manipulate its objects.

For the GDB python backend, this is directly mapped to the Python string type.

To convert a C-land string data into a _str value, you can write

const char *buf = "hello, world!";
_str a = (_str) buf;

or equivalently,

_str a = _tostr(buf);

You can also specify a length as the 2nd argument to the _tostr() built-in function in case the C buffer does not contain a null-terminated C string. For instance,

_str a = _tostr(buf, 32);  // 32 is the string length

The _str type value supports concatenation through the += operator, as in

_str s = "hello";
s += ", world";

Similarly, + can be used to concatenate 2 built-in string values, as in

_str res = "hello" + "world";

Built-in string values can also be compared alphabetically via the binary relational operators >, >=, \\\<, \\\<=, ==, and !=.

Back to TOC

# _agg

The _agg data type provides easy way to do data statistics similar to systemtap's "aggregate" variables. One can use the \\\<\\\<\\\< operator to add new (numerical) value record to the aggregate. For example,

_agg stats;

_probe foo(int a) {
    stats <<< a;
}

_probe main() -> int {
    printf("count = %d, max = %d, min = %d, avg = %.2f\n", _count(stats),
           _max(stats), _min(stats), _avg(stats));
    printf("%s", _hist_log(stats));
}

Back to TOC

# arrays

Built-in array variables take the sigil @ like in Perl 6. Below is an example:

void foo(void *p) {
    void *@arr = [NULL];
    _push(@arr, p);
    printf("value: %p\n", @arr[0]);

    void *q = _pop(@arr);
    printf("array len: %d\n", _elems(@arr));  // output 1
}

As this examples demonstrates, we can

  1. use [a, b, c, ...] to construct a literal array value,
  2. use _push() to append an element to the end of the array,
  3. use _pop() to remove an element from the end of the array and return that value,
  4. use _elems() to fetch the number of elements currently in the array.

The element type of an array can be any C data types or ylang's own built-in types like _str.

Built-in arrays can also be global variables and function parameters and arguments.

One common use of the array type is to emulate C language's output parameter, as in

void foo(int a, void *@out) {
    void *p = (void *) 0xdeadbeef;
    @out[0] = p + a;
}

void bar(void) {
    void *@res = [NULL];
    foo(3, @res);
    void *q = @res[0];
    printf("got the output pointer vaue: %p\n", q);
}

This technique is especially useful when a tracer function needs to return multiple values. Unlike C, ylang does not allow taking the address of a tracer-space variable, so this is the only way to do this (actually the other way is to use a similar container built-in type value like a hash value, though more cumbursome and more expensive).

Built-in arrays can only be in the tracer space.

Back to TOC

# hash tables

Built-in hash variables take the sigil % like in Perl 6. For example,

void foo(void *p) {
    void *%hash{_str};

    %hash<foo> = p;  // literal key 'foo'
    %hash{'foo'} = p;  // equivalent to the line above
    if (_exists %hash<bar>) {
        _error("hash key bar not exists!");
    }
    printf("foo: %p\n", %hash<foo>);
}

The key data type of a built-in hash value in this examle is a _str while its value type can be any C data types or ylang's own built-in types like _str.

When the key is a literal indenfifier string, we can use the %hash\\\<key> shortcut to avoid writing %hash{'key'}. For example,

int %foo{_str};
foo<age> = 3;
foo<height> = 186;

Key types can also be integer types (like int and long) or pointer types (like char * or void *). In the case of pointer typed keys, the pointer's integral value will be used.

Multiple keys are also allowed, and their order is important. For example,

_str %bar{int, _str};
%bar{32, "hello"} = "world";
%bar{17, "hi"} = "bird";

The ylang parser requires that no whitespace characters are allowed between %hash and the subscript part ({...} or \\\<...>).

Back to TOC

# New statements
# _foreach

syntax: _foreach %hash -> type1 key, type2 value { ... }

syntax: _foreach %hash -> type1 key1, type2 key2, ..., type value { ... }

syntax: _foreach %hash -> type1 key, type2 value _asc _limit N { ... }

Iterate through the specified built-in hash table variable using custom iterator variales for both the keys and the value in the hash table.

If you do not care about any particular iterator variable, then you could just omit its variable name but a type is still needed to serve as a placeholder, as in

/* we do not care about the keys but still need a placeholder for them */
_foreach %foo -> _str, int val {
    printf("value: %d\n", val);
}

and

/* we do not care about the values but still need a placeholder for them */
_foreach %foo -> _str k, int {
    printf("key: %d\n", k);
}

It is supported to iterate through the hash table by a custom order. For example, to sort the hash table values in a descent order, just append the keyword _desc to the value iterator variable declaration, as in

_foreach %foo -> _str k, int v _desc {
    ...
}

If the values are of the _agg type, then the values will be sorted by the order of _count(v) by the _foreach loop.

Or sort by a key in an ascendent order:

_foreach %foo -> _str k, int v _desc {
    ...
}

For multi-key hash tables, you can also sort by any one of those sub-keys, as in

_foreach %foo -> int k1, _str k2 _asc, _agg v {
    ...
}

Also, an optional _limit N clause is supported to limit the number of loop iterations, as in

_foreach %foo -> _str k, _agg v _desc _limit 10 {
    ...
}

This will iterate through only the top 10 aggregate values with their keys in the hash table %foo according to the number of entries in those aggregates.

Any integer-typed expressions could be used in the _limit clause.

Back to TOC

# try/catch

syntax: try { ... } catch (_str) {...}

syntax: try { ... } catch (_str e) {...}

Uses try/catch to handle most kinds of run-time errors in the surrounded code inside the try block instead of aborting the current probe handler immediately. The semantics is similar to C++ and the try/catch statements can be nested. The error string may be captured by optionally declaring a variable in the catch clause.

To capture the error thrown inside the try clause, you can write

void foo(void) {
    _error("bad things happened!");
}

_cmd void test(void) {
    try {
        foo();

    } catch (_str e) {   /* omit the variable name to discard the error message */
        _warn("caught error '%s'\n", e);
    }

    printf("done\n");
}

Back to TOC

# New operators
# _exists

syntax: _exists %hash{key}

syntax: _exists %hash{key1, key2, ...}

Returns a boolean value indicating whether a specified key (or a combination of multiple keys) exists in a built-in hash table. For example,

if (_exists %foo<my_key>) {
    // ... do something
}

Back to TOC

# _del

syntax: _del %hash{key}

syntax: _del %hash{key1, key2, ...}

syntax: _del %hash

syntax: _del @array

syntax: _del agg

When the operand is a hash subscript expression, this operator deletes the key (or a combination of multiple keys for multi-key hash tables) in a built-in hash table.

When the operand is a built-in hash table variable, then it clears all the keys in the hash table.

When the operand is a built-in array, then it clears all the elements in the array. Any other kinds of operands yield an error.

When the operand is a variable of the _agg type, then it clears the aggregate.

Back to TOC

# GCC builtins

The following GCC builtins are accepted (though they are currently equivalent to no-op in the current ylang implementation).

Back to TOC

# __builtin_expect

syntax: long __builtin_expect(long exp, long c)

Currently this function is simply compiled down to (long) exp.

Back to TOC

# __builtin_unreachable

syntax: void __builtin_unreachable(void)

Currently this function is simply compiled down to _error("unreachable").

Back to TOC

# Built-in functions

The ylang compiler does support the following built-in functions (or standard functions). Some of them are compatible with the same-name standard C functions like assert(), printf(), and sprintf().

Back to TOC

# _reg

syntax: long _reg(_str name)

Returns the value of the specified CPU register. For example, _reg("rax") returns the value of the CPU register rax on x86_64.

Back to TOC

# _tostr

syntax: _str _tostr(const char *s)

syntax: _str _tostr(const char *s, size_t len)

Converts a C string value into a string of the type _str. When only one argument is given, that argument is treated as a const char * pointer and a NULL-terminated C string.

When an extra length argument is given, that length is used for the resulting _str value.

Back to TOC

# _contains

syntax: void _contains(_str a, _str b)

It accepts two _str values, a, and b. Returns true (1) when a contains b.

Back to TOC

# _warn

syntax: void _warn(_str fmt, ...)

Prints out a custom text message (as a warning) to the stderr stream.

Unlike _error(), this function does not abort he current execution flow.

Back to TOC

# _error

syntax: void _error(_str fmt, ...)

Throws out an error with the error string formatted by the user.

Accepts a formatter string and several more parameters just like sprintf() and printf.

For example,

_error("an error happened!");

_error("this value is bad: %d (%s)", foo, bar);

This function never returns.

Back to TOC

# _exit

syntax: void _exit(void)

Exits the current tracer. It does not take any arguments right now. For example:

exit();

This function never returns.

Back to TOC

# _push

syntax: void _push(@array, any elem)

Appends (or pushes) a new element to a built-in array. For example,

_push(@a, 3);

Back to TOC

# _pop

syntax: any _pop(@array)

Removes and returns the last element of a built-in array. For instance,

int a = _pop(@a);

Back to TOC

# _shift

syntax: any _shift(@array)

Removes and returns the first element of a built-in array. For instance,

int a = _shift(@a);

Back to TOC

# _elems

syntax: int _elems(@array)

Returns the number of elements currently in a built-in array. For example,

int n = _elems(@a);

Back to TOC

# printf

syntax: void printf(_str fmt, ...)

Similar to the standard C function printf().

Back to TOC

# sprintf

syntax: _str sprintf(_str fmt, ...)

Similar to the standard C function sprintf(), but returns a built-in _str type value.

Back to TOC

# assert

syntax: void assert(scalar expr)

Similar to the standard C function (or macro) assert(), when the argument expression is a false value, then abort the current trace program's execution and prints out an error message to the stderr stream, as in

Assertion `a - 32 != 0' failed.

Back to TOC

# _gdb

syntax: void _gdb(_str fmt, ...)

Executes an aributrary gdb command. The command can be formatted like sprintf(). For example,

void foo(GCobj *p) {
    _gdb("p *(GCobj*)%p", p);
}

This function does not capture the output of the gdb command and thus does not return any values.

Obviously this function is only available when the gdb python backend is used.

Back to TOC

# _stap_str

syntax: _str _stap_str(_str stap_expr)

Emits the stap language expression which returns a string value to the generated stap script for the systemtap backend.

It generates a compile-time error for other backends like gdb python.

It is the user's responsibility to make sure the stap_expr string value is indeed a well-formed systemtap expression and that expression indeed is evaluated to a string value.

Back to TOC

# _stap_long

syntax: long _stap_long(_str stap_expr)

Emits the stap language expression which returns a long value to the generated stap script for the systemtap backend.

It generates a compile-time error for other backends like gdb python.

It is the user's responsibility to make sure the stap_expr string value is indeed a well-formed systemtap expression and that expression indeed is evaluated to a long value.

Back to TOC

# _now_s

syntax: long _now_s(void)

Returns the current UNIX epoch time in (integer) seconds.

Back to TOC

# _now_ms

syntax: long _now_ms(void)

Returns the current UNIX epoch time in (integer) milliseconds.

Back to TOC

# _now_us

syntax: long _now_us(void)

Returns the current UNIX epoch time in (integer) microseconds.

Back to TOC

# _now_ns

syntax: long _now_ns(void)

Returns the current UNIX epoch time in (integer) nanoseconds.

Please note that the GDB python backend does not really support nanosecond precision right now.

Back to TOC

# _uaddr

syntax: void *_uaddr(void)

Returns the address of the current program counter (PC) value as a void * pointer value.

In case that there is no user thread running in the current context, returns NULL.

To fetch the currently executing C function name, we could use the following expression:

_usym(_uaddr())

Back to TOC

# _len

syntax: int _len(s)

Returns the length of the string value (could be a _str type value or a C data value which is treated as a NULL-terminated C string).

Back to TOC

# _chop_token

syntax: _str _chop_token(_str s, _str delim)

Removes the last token from the input string specified by the 1st argument using the delimiter specified by the 2nd argument.

Returns the resulting string without the last token.

The original input string is left intact.

Back to TOC

# _max

syntax: double _max(_agg agg)

Returns the maximum value in an aggregate object. For example:

_agg stats;
// add new value entries to the stats via the `<<<` operator here...
printf("max: %lf\n", _max(stats);

Back to TOC

# _min

syntax: double _min(_agg agg)

Returns the minimum value in an aggregate object. For example:

_agg stats;
// add new value entries to the stats via the `<<<` operator here...
printf("min: %lf\n", _min(stats);

Back to TOC

# _sum

syntax: double _sum(_agg agg)

Returns the total sum value in an aggregate object. For example:

_agg stats;
// add new value entries to the stats via the `<<<` operator here...
printf("sum: %lf\n", _sum(stats);

Back to TOC

# _avg

syntax: double _avg(_agg agg)

Returns the arithmetic average value in an aggregate object. For example:

_agg stats;
// add new value entries to the stats via the `<<<` operator here...
printf("avg: %lf\n", _avg(stats);

Back to TOC

# _count

syntax: double _count(_agg agg)

Returns the number of data entries in an aggregate object. For example:

_agg stats;
// add new value entries to the stats via the `<<<` operator here...
printf("count: %lf\n", _count(stats);

Back to TOC

# _hist_log

syntax: _str _hist_log(_agg agg)

Returns a _str value containing a textual representation of a base-2 logarithmic histogram from an aggregate object.

Below is a sample string return value:

value	|------------------------------------------------ count
0	|@@@@@                                             1
1	|@@@@@                                             1
2	|@@@@@                                             1
4	|@@@@@@@@@@                                        2
8	|@@@@@@@@@@@@@@@@@@@@                              4
16	|@@@@@                                             1

Back to TOC

# _ubt

syntax: _str _ubt(void)

Returns the raw userland C backtrace (not symbolized) in the systemtap backend. One example return value is

0x40048b 0x40049b 0x4004a6 0x7f704167efea 0x4003da

For the gdb backend, it returns the fully symbolized backtrace like this:

#0  foo () at test.c:2
#1  0x000000000040049b in bar () at test.c:6
#2  0x00000000004004a6 in main () at test.c:10

Back to TOC

# _ubt2

syntax: _str _ubt2(uintptr_t pc, uintptr_t sp)

Similar to _ubt, but returns a backtrace for the specified PC register and SP register values (which are rip and rsp registers on x86_64).

This is usually used to skip C frames for machine code without debug symbols (like from a Just-in-Time compiler).

Back to TOC

# _sym_ubt

syntax: _sym_ubt(bt)

Returns a symbolized backtrace string from the raw backtrace string as returned by functions _ubt() or _ubt2() for the systemtap backend.

One sample output is like this:

foo+0x4 [a.out]
bar+0x9 [a.out]
main+0x9 [a.out]
__libc_start_main+0xea [libc-2.26.so]
_start+0x2a [a.out]

For the gdb backend, it just returns the argument directly.

Back to TOC

# _usym

syntax: _str _usym(void *addr)

Returns a symbol name of the type _str from a C pointer value; returns an empty string value when no symbol can be resolved.

For example, given the following target C program to be traced:

int a;
void foo(void) {}

void *p = (void *)foo;
void *q = &a;

int main(void) {
    return 0;
}

and the followng ylang tracer program:

_target void *p;
_target void *q;

_cmd void test(void) {
    printf("%s\n", _usym(p));
    printf("%s\n", _usym(q));
}

The tracer would output the following lines:

foo
a

Back to TOC

# _print

syntax: void _print(...)

Prints out one string or more string arguments to stdout.

Back to TOC

# puts

syntax: void puts(_str s)

Prints out a string with a trailing new line character.

Just like the standard C function of the same name.

Back to TOC

# fabs

syntax: double fabs(double x)

Returns the absolute value of the double-precision floating-point number x.

Back to TOC

# fabsf

syntax: float fabsf(float x)

Returns the absolute value of the single-precision floating-point number x.

Back to TOC

# fmod

syntax: double fmod(double x, double y)

Compute the floating-point remainder of dividing x by y. The return value is x - n * y, where n is the quotient of x / y, rounded toward zero to an integer.

Back to TOC

# fmodf

syntax: float fmodf(float x, float y)

Compute the floating-point remainder of dividing x by y. The return value is x - n * y, where n is the quotient of x / y, rounded toward zero to an integer.

Back to TOC

# remainder

syntax: double remainder(double x, double y)

Computes the remainder of dividing x by y. The return value is x-n*y, where n is the value x / y, rounded to the nearest integer. If the absolute value of x-n*y is 0.5, n is chosen to be even.

Back to TOC

# remainderf

syntax: float remainderf(float x, float y)

Computes the remainder of dividing x by y. The return value is x-n*y, where n is the value x / y, rounded to the nearest integer. If the absolute value of x-n*y is 0.5, n is chosen to be even.

Back to TOC

# sqrt

syntax: double sqrt(double x)

Returns the nonnegative square root of x.

Back to TOC

# sqrtf

syntax: float sqrtf(float x)

Returns the nonnegative square root of x.

Back to TOC

# _pid

syntax: int _pid()

Returns the current process's pid.

When running outside any process contexts, it returns 0.

Back to TOC

# _tid

syntax: int _tid()

Returns the current thread's "tid" (assigned by the operating system).

When running outside any process/thread contexts, it returns 0.

Back to TOC

# Author

Yichun Zhang (agentzh) \<yichun@openresty.com>

Back to TOC

Copyright (C) 2018-2019 OpenResty Inc. All rights reserved.

This software is proprietary and must not be redistributed or shared at all.

Back to TOC