User Reference Manual for the Y Language of OpenResty XRay
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:
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.
##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
).
##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 evalutes 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 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
).
##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
.
##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
.
##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 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
directies.
Ylang supports specifying probes in the target processes, just like systemtap or dtrace.
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
.
We support the following systemtap-style timer probes:
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 {
...
}
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) {
...
}
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) {
...
}
This probe handler runs when the OS kernel scheduler switches a process onto a CPU for execution.
This probe handler runs when the OS kernel scheduler switches a process off a CPU for sleeping (or waiting for IO events and etc).
This probe runs at the very beginning of the tracing tool. It does not have any target process contexts yet.
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.
This probes runs at the entry point of the system call named NAME
.
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.
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.
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);
}
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);
}
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.
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.
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 !=
.
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.
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));
}
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
[a, b, c, ...]
to construct a literal array value,_push()
to append an element to the end of the array,_pop()
to remove an element from the end of the array and return that value,_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.
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 <...>
).
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.
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");
}
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
}
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.
~~
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
!~~
syntax: str !~~ /regex/
syntax: str !~~ rx{regex}
syntax: str !~~ "regex"
Equivalent to !(str ~~ /regex/)
and etc.
The following GCC builtins are accepted (though they are currently equivalent to no-op in the current ylang implementation).
syntax: long __builtin_expect(long exp, long c)
Currently this function is simply compiled down to (long) exp
.
syntax: int __builtin_clz(unsigned int x)
syntax: void __builtin_unreachable(void)
Currently this function is simply compiled down to _error("unreachable")
.
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()
.
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
.
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.
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.
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.
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.
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.
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
.
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.
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.
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.
syntax: void _exit(void)
Exits the current tracer. It does not take any arguments right now. For example:
exit();
This function never returns.
syntax: void _push(@array, any elem)
Appends (or pushes) a new element to a built-in array. For example,
_push(@a, 3);
syntax: any _pop(@array)
Removes and returns the last element of a built-in array. For instance,
int a = _pop(@a);
syntax: any _shift(@array)
Removes and returns the first element of a built-in array. For instance,
int a = _shift(@a);
syntax: int _elems(@array)
Returns the number of elements currently in a built-in array. For example,
int n = _elems(@a);
syntax: void printf(_str fmt, ...)
Similar to the standard C function printf()
.
syntax: _str sprintf(_str fmt, ...)
Similar to the standard C function sprintf()
, but returns a built-in _str
type value.
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.
syntax: long _now_s(void)
Returns the current UNIX epoch time in (integer) seconds.
syntax: long _now_ms(void)
Returns the current UNIX epoch time in (integer) milliseconds.
syntax: long _now_us(void)
Returns the current UNIX epoch time in (integer) microseconds.
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.
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())
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).
syntax: int _randint(int n)
Returns a pseudo-random integer in the range [0, n)
.
syntax: long _log2(long n)
Returns the 2-base logarithm for argument n
.
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.
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);
syntax: double _variance(_agg agg)
Returns the variance of the aggregate object.
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);
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);
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);
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);
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
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
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).
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.
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
syntax: void _print(...)
Prints out one string or more string arguments to stdout.
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.
syntax: double fabs(double x)
Returns the absolute value of the double-precision floating-point number x
.
syntax: float fabsf(float x)
Returns the absolute value of the single-precision floating-point number x
.
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.
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.
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.
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.
syntax: double sqrt(double x)
Returns the nonnegative square root of x
.
syntax: float sqrtf(float x)
Returns the nonnegative square root of x
.
syntax: int _pid(void)
Returns the current process's pid.
When running outside any process contexts, it returns 0.
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.
syntax: int _pgid(void)
Returns the process group ID of the current process.
When running outside any process/thread contexts, it returns 0.
syntax: unsigned long _actions()
Returns the number of ylang statements (or a relatively fixed small multiples of this number) executed so far.
syntax: long _arg_long(int)
Returns the nth argument of the function.
Yichun Zhang (agentzh) yichun@openresty.com
Copyright (C) 2018-2021 OpenResty Inc. All rights reserved.
This software is proprietary and must not be redistributed or shared at all.