# OpenResty XRay™ Y Language User Reference Manual

User Reference Manual for the Y Language of OpenResty XRay

# 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.

# ##ifdeffunc

syntax: #ifdeffunc FUNC

This macro is a condition similar to #if, which evalutes to true when the function FUNC 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

# ##ifndeffunc

syntax: #ifndeffunc FUNC

This macro condition is similar to ##ifdeffunc, but only evaluates to true when function FUNC is NOT defined in the target process.

# ##elifdeffunc

syntax: #elifdeffunc FUNC

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

# ##elifndeffunc

syntax: #elifndeffunc FUNC

Similar to ##elifdeffunc, 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.

The effect of ##yexe can be canceled by a subsequent ##yendexe directive. The pairs of ##yexe and ##yendexe can be nested.

Back to TOC

# ##yendexe

syntax: ##yendexe

Cancels the effect of the previous ##yexe directive.

# ##yexeonly

syntax: ##yexeonly PATTERN

This is similar to ##yexe but do not try other executable files (including dynamic library files) when looking up a symbol.

The effect of ##yexeonly can be canceled by a subsequent ##yendexeonly or ##reset directive, otherwise it is until the end of the current (header) file.

The pairs of ##yexeonly and ##yendexe can be nested.

Back to TOC

# ##yendexeonly

syntax: ##yendexeonly

Cancels the effect of the previous ##yexe directive.

# ##nosym

syntax: ##nosym SYMBOL

Add symbol name SYMBOL into the blacklist for introducing enum constant names and/or typedef type names during the preprocessing, parsing and typechecking phases of the ylang compiler.

This is usually used to resolve symbol name conflicts between ylang symbols and typedef type names in the target program.

The effect of ##nosym can be canceled by a subsequent ##reset directive, otherwise it is until the end of the current (header) file.

Multiple instances of this directive are allowed and their effects are accumulated.

Back to TOC

# ##reset

syntax: ##reset

Reset the effect of any previous ##nosym, ##yexe, or ##yexeonly directies.

# Probes

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

Back to TOC

# Process begin/end probes

We can specify probes running at the startup and completion of the target process, for instance,

_probe _process.begin {
    printf("process %d started!\n", _pid());
}

_probe _process.end {
    printf("process %d terminated!\n", _pid());
}

For already running target processes when the ylang tool is started, the _process.begin probes fire for them automatically as well.

Note that use of these probes require specifying the ylang command-line option --exe PATH.

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 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 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 instance,

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

Back to TOC

# Scheduler Probes

# _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/End Probes

# _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

# System Call Probes

# _syscall.NAME

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

Back to TOC

# _syscall.NAME.return

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

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 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, 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 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

# _split

syntax: _split(subject, delimiter, @tokens)

Splits the subject string into the @tokens (built-in) array with the literal delimiter string.

Empty tokens will also be returned. Any existing elements in the @tokens array will get cleared first.

For the first 2 arguments, both char * and _str types are supported.

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 { ... }

Iterates 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

# _tostr_quoted

syntax: _str _tostr_quoted(const char *s)

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

Similar to _tostr, but escape special characters in the original string.

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

# _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 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

# _randint

syntax: int _randint(int n)

Returns a pseudo-random integer in the range [0, n).

Back to TOC

# _log2

syntax: long _log2(long n)

Returns the 2-base logarithm for argument n.

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

# _variance

syntax: double _variance(_agg agg)

Returns the variance of the aggregate object.

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). 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.

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(void)

Returns the current process's pid.

When running outside any process contexts, it returns 0.

Back to TOC

# _tid

syntax: int _tid(void)

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

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

Back to TOC

# _pgid

syntax: int _pgid(void)

Returns the process group ID of the current process.

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

Back to TOC

# _actions

syntax: unsigned long _actions()

Returns the number of ylang statements (or a relatively fixed small multiples of this number) executed so far.

Back to TOC

# Author

Yichun Zhang (agentzh) yichun@openresty.com

Back to TOC

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

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

Back to TOC