OpenResty XRay™ Y Language User Reference Manual

Documentation of Y Language when creating dynamic tracing analyzers, which closely follows C language syntax

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

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

Back to TOC

##ifdefvar

syntax: ##ifdefvar VAR

This macro is a condition similar to #if, which evaluates 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.

##ifdefenum

syntax: ##ifdefenum ENUM

This macro is a condition similar to #if, which evaluates to true when the top-level (static or global) enum ENUM 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

##ifndefenum

syntax: ##ifndefenum ENUM

This macro condition is similar to ##ifdefenum, but only evaluates to true when the top-level enum ENUM is NOT defined in the target process.

##elifdefenum

syntax: ##elifdefenum ENUM

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

##elifndefenum

syntax: ##elifndefenum ENUM

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

##ifdeffunc

syntax: ##ifdeffunc FUNC

This macro is a condition similar to #if, which evaluates 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 evaluate to true:

##ifdeffield foo_t bar
##ifdeffield foo_t baz

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

typedef struct UpVal {
  CommonHeader;
  union {
    TValue *p;
    ptrdiff_t offset;
  } v;
  union {
    struct {
      struct UpVal *next;
      struct UpVal **previous;
    } open;
    TValue value;
  } u;
} UpVal;

Then the following directive evaluates to true:

##ifdeffield UpVal v.p

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

Back to TOC

##ifndeffield

syntax: ##ifndeffield 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 conditional 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.

##ifdefarg

syntax: ##ifdefarg FUNC PARA

This macro is a condition similar to #if, which evaluates to true when the parameter PARA of function FUNC is defined in the debuginfo of the target process (including dynamically linked libraries).

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

int foo(int a, int b, int c);

Then the following directive evaluate to true:

##ifdefarg foo a

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

Back to TOC

##ifndefarg

syntax: ##ifndefarg FUNC PARA

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

##elifdefarg

syntax: ##elifdefarg FUNC PARA

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

##elifndefarg

syntax: ##elifndefarg FUNC PARA

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

##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 and glibc.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 directives.

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 parameter 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 value: %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 cumbersome 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 example 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 identifier 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 variables 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

Regex matching operator ~~

syntax: str ~~ /regex/

syntax: str ~~ rx{regex}

syntax: str ~~ "regex"

Performs regex matching against the subject string str. Currently the regex must be the common subset of the Perl compatible regex syntax and the POSIX regex syntax.

Below is an example:

_cmd void test(void) {
    _str a = "hello, world";
    if (a ~~ /([a-z]+), ([a-z]+)/) {
        _print("1: ", $1, ", 2: ", $2, "\n");
        return;
    }
    _error("not matched");
}

The expected output is

1: hello, 2: world

Back to TOC

Regex not matching operator !~~

syntax: str !~~ /regex/

syntax: str !~~ rx{regex}

syntax: str !~~ "regex"

Equivalent to !(str ~~ /regex/) and etc.

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_clz

syntax: int __builtin_clz(unsigned int x)

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

_pc_reg

syntax: long _pc_reg()

Returns the value of the PC register. This API returns the value of the CPU register rip on x86_64 and returns the value of CPU register pc on aarch64.

Back to TOC

_sp_reg

syntax: long _sp_reg()

Returns the value of the SP register. This API returns the value of the CPU register rsp on x86_64 and returns the value of CPU register sp on aarch64.

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: bool _contains(_str a, _str b)

syntax: bool _contains(const char *a, const char *b)

syntax: bool _contains(const char *a, _str b)

syntax: bool _contains(_str a, const char *b)

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

Back to TOC

_isprefix

syntax: bool _isprefix(_str a, _str b)

syntax: bool _isprefix(const char *a, const char *b)

syntax: bool _isprefix(const char *a, _str b)

syntax: bool _isprefix(_str a, const char *b)

It accepts two _str values, a, and b. Returns true (1) when a has the prefix b, or false (0) otherwise.

Back to TOC

_issuffix

syntax: bool _issuffix(_str a, _str b)

syntax: bool _issuffix(const char *a, const char *b)

syntax: bool _issuffix(const char *a, _str b)

syntax: bool _issuffix(_str a, const char *b)

It accepts two _str values, a, and b. Returns true (1) when a has the suffix b, or false (0) otherwise.

Back to TOC

_substr

syntax: _str _substr(_str s, long start)

syntax: _str _substr(const char *s, long start)

syntax: _str _substr(_str s, long start, long len)

syntax: _str _substr(const char *s, long start, long len)

Returns a substring in argument s using the starting offset start and an optional length, len.

When len is omitted, it defaults to the length from start to the end of s.

Back to TOC

_strtol

syntax: long _strtol(_str s, int base)

syntax: long _strtol(_str s)

Parses a string, s, into a long int typed number with an optional base. The base defaults to 10.

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

_unshift

syntax: void _unshift(@array)

Prepends (or unshifts) a new element to the beginning of a built-in array. For example,

_unshift(@a, 3);

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

_arg_long

syntax: long _arg_long(int)

Returns the nth argument of the function.

Back to TOC

_execname

syntax: _str name = _execname()

Returns the process command name (excluding any command line arguments and the path part).

Back to TOC

Author

Yichun Zhang (agentzh) yichun@openresty.com

Back to TOC

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

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

Back to TOC