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 instruct 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 inunion foo;
or
typedef union foo foo;
Standard C header files like
stdint.h
andstddef.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:
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.
##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
).
##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
).
##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
).
##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
.
##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
.
##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
.
##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.
##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.
##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.
##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.
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
.
Timer probes
We support the following systemtap-style timer probes:
_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 {
...
}
_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) {
...
}
_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) {
...
}
Scheduler Probes
_scheduler.cpu_on
This probe handler runs when the OS kernel scheduler switches a process onto a CPU for execution.
_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).
Begin/End Probes
_begin
This probe runs at the very beginning of the tracing tool. It does not have any target process contexts yet.
_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.
System Call Probes
_syscall.NAME
This probes runs at the entry point of the system call named NAME
.
_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.
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.
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);
}
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);
}
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.
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.
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 !=
.
_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.
_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));
}
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
- use
[a, b, c, ...]
to construct a literal array value, - use
_push()
to append an element to the end of the array, - use
_pop()
to remove an element from the end of the array and return that value, - 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.
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 <...>
).
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.
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");
}
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
}
_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.
~~
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
!~~
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).
__builtin_expect
syntax: long __builtin_expect(long exp, long c)
Currently this function is simply compiled down to (long) exp
.
__builtin_clz
syntax: int __builtin_clz(unsigned int x)
__builtin_unreachable
syntax: void __builtin_unreachable(void)
Currently this function is simply compiled down to _error("unreachable")
.
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()
.
_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
.
_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
.
_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
.
_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.
_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.
_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.
_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.
_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.
_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
.
_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.
_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.
_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.
_exit
syntax: void _exit(void)
Exits the current tracer. It does not take any arguments right now. For example:
exit();
This function never returns.
_push
syntax: void _push(@array, any elem)
Appends (or pushes) a new element to a built-in array. For example,
_push(@a, 3);
_pop
syntax: any _pop(@array)
Removes and returns the last element of a built-in array. For instance,
int a = _pop(@a);
_shift
syntax: any _shift(@array)
Removes and returns the first element of a built-in array. For instance,
int a = _shift(@a);
_unshift
syntax: void _unshift(@array)
Prepends (or unshifts) a new element to the beginning of a built-in array. For example,
_unshift(@a, 3);
_elems
syntax: int _elems(@array)
Returns the number of elements currently in a built-in array. For example,
int n = _elems(@a);
printf
syntax: void printf(_str fmt, ...)
Similar to the standard C function printf()
.
sprintf
syntax: _str sprintf(_str fmt, ...)
Similar to the standard C function sprintf()
, but returns a built-in _str
type value.
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.
_now_s
syntax: long _now_s(void)
Returns the current UNIX epoch time in (integer) seconds.
_now_ms
syntax: long _now_ms(void)
Returns the current UNIX epoch time in (integer) milliseconds.
_now_us
syntax: long _now_us(void)
Returns the current UNIX epoch time in (integer) microseconds.
_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.
_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())
_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).
_randint
syntax: int _randint(int n)
Returns a pseudo-random integer in the range [0, n)
.
_log2
syntax: long _log2(long n)
Returns the 2-base logarithm for argument n
.
_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.
_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);
_variance
syntax: double _variance(_agg agg)
Returns the variance of the aggregate object.
_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);
_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);
_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);
_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);
_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
_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
_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).
_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.
_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
_print
syntax: void _print(...)
Prints out one string or more string arguments to stdout.
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.
fabs
syntax: double fabs(double x)
Returns the absolute value of the double-precision floating-point number x
.
fabsf
syntax: float fabsf(float x)
Returns the absolute value of the single-precision floating-point number x
.
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.
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.
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.
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.
sqrt
syntax: double sqrt(double x)
Returns the nonnegative square root of x
.
sqrtf
syntax: float sqrtf(float x)
Returns the nonnegative square root of x
.
_pid
syntax: int _pid(void)
Returns the current process’s pid.
When running outside any process contexts, it returns 0.
_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.
_pgid
syntax: int _pgid(void)
Returns the process group ID of the current process.
When running outside any process/thread contexts, it returns 0.
_actions
syntax: unsigned long _actions()
Returns the number of ylang statements (or a relatively fixed small multiples of this number) executed so far.
_arg_long
syntax: long _arg_long(int)
Returns the nth argument of the function.
_execname
syntax: _str name = _execname()
Returns the process command name (excluding any command line arguments and the path part).
Author
Yichun Zhang (agentzh) yichun@openresty.com
Copyright & Licenses
Copyright (C) 2018-2021 OpenResty Inc. All rights reserved.
This software is proprietary and must not be redistributed or shared at all.