OpenResty XRay™ YLua User Manual
ylua - Compiler for the ylua language for writing Lua apps’ tracing tools in a Lua-like language.
Table of Contents
Synopsis
Language syntax examples
probe foo(a, b)
print("a = ", a)
print("b = ", b)
end
probe bar()
print("k = ", k) -- k is an upvalue in the target for function bar.
end
Language features
Lua syntax supported
The following Lua operators and syntax are supported:
- unary
#
- unary
not
- unary
-
- binary
+
,-
,*
,/
,%
,..
- binary
>
,>=
,<
,<=
,==
,!=
- binary
and
,or
. - postfix
[KEY]
- postfix
.KEY
- parentheses (
()
) - vararg (
...
) - booleans (
true
,false
) if
/elseif
/else
statementsreturn
statementdo
statementfor v = fr, to, step do
loop statementfor i, e in ipairs(tb)
loop statementfor k, v in pairs(tb)
loop statementwhile
statementrepeat ... until
statementbreak
statementlocal
variable declarations (with multiple variables and initializer expressions)- assignment statement (with multiple assignment support)
nil
value- function call syntax
foo(...)
,foo"..."
,foo{...}
, and etc.
Built-in functions supported
Lua built-in functions
The following Lua built-in functions are supported:
require
print
type
select
error
tostring
tonumber
ipairs
(only as the exp-list in thefor ... in
loop)pairs
(only as the exp-list in thefor ... in
loop)table.maxn
Extra built-in functions
The following extra standard functions are supported:
dump
Dump details of the Lua value argument.
out
Similar to
print()
, but do not output a trailing newline character automaticallyidentity
Directly return the argument value.
exit
Exit the probing session (i.e., the whole tracing tool).
warn
Emit a warning message to stderr.
contains
Returns
true
if the 1st string argument contains the 2nd string argument, orfalse
otherwise.upval
syntax:
upval(name)
syntax:
upval(func, name)
Returns the upvalue of the name specified by the
name
argument. The associated function can be either the current function being probed or the one explicitly specified by thefunc
argument. Only Lua functions take named upvalues. Thisupval()
function can also be used in Lua function probe specifier, as inprobe (upval(require "foo".blah, "uvname")) (a, b) ... end
Probes
Lua function probes
Lua function entry probes
Dynamic probes on Lua function entry points are supported. Certain
Lua built-in functions implemented directly in hand-crafted assembly
code might miss the probes, however, as some of the API functions
under the math
namespace.
Below is an example:
probe foo(a)
print("arg a = ", a)
end
It prints out the value of the actual argument a
of the global Lua
function foo
in the target every time that function is entered (due
to a function call).
Vararg syntax is also supported when the number of actual Lua function arguments in the target process may change. For example:
probe foo(...)
for i = 1, select('#', ...) do
print(i, ": ", select(i, ...))
end
end
This probe handler will output all the actual arguments in every invocation
of the global Lua function foo
in the target. One typical output might
look like this:
1: 3
2: hello
3: 3.140000
In case the target Lua function might return composite Lua values like Lua
tables, we should use the dump()
builtin function instead in this example,
as in
probe foo(...)
for i = 1, select('#', ...) do
print(i, ": ", dump(select(i, ...)))
end
end
One sample output is like below:
1: 171
2: "hello"
3: true
4: table (GCtab*)0x7f96f754d830 (narr=0, nrec=1):
key:
"dogs"
value:
-3.140000
Note how the output of dump()
differ from printing out the Lua target
values directly.
For Lua function entry probes, the parameter variables, upvalue variables, and global variables can all be referenced. Local variables which are not parameters cannot be referenced in such probe handlers since they are not initialized at that point, naturally. Below is an example:
probe foo(a)
print(a + b)
end
Compiling this ylua code example will yield the following warning:
WARNING: bar: symbol 'b' is assumed to be an upvalue or a global variable in the target Lua process at test.ylua line 2
Obviously, the referenced variable b
is not declared and will be assumed
to be either an upvalue for the function foo
in the target, or a global
Lua variable of the current Lua thread when the probe is fired. An upvalue
of the current Lua function take precedence over a global variable lookup,
for obvious reasons.
Any Lua primary expressions can be used as probe specifier, as in
probe (package.loaded["io"].open)(file_name, mode)
print("opening file ", file_name, " with mode ", mode)
end
Or even with tracer-space built-in function calls, as in
probe (require "io".open)(file_name, mode)
print("opening file ", file_name, " with mode ", mode)
end
Note that the parentheses are required to enclose the expression otherwise there is grammatical ambiguities.
Lua function return probes
Dynamic probes on Lua function return points are supported. Tailcalls might miss our probes, however, due to their inherent “goto” nature.
Consider the following example:
probe foo -> ()
print("function foo returning!")
end
This probe handler will prints out the line function foo returning!
every time the global Lua function foo()
is returned in the target.
Here we do not care whether such function exits return any values. But
when we do, we can inspect the return values like this:
probe foo -> (a, b)
print("returning ", a, " and ", b)
end
This probe will output the first 2 return values of every return of the target Lua function.
Similar to the Lua function entry probes, the Lua vararg syntax (...
)
is also supported in return probes to inspect all the
return values when the number of them are not fixed or not known ahead.
Below is such an example:
probe foo -> (...)
for i = 1, select('#', ...) do
print(i, ": ", select(i, ...))
end
end
One typical output is like this:
1: 171
2: hello
3: true
4: -3.140000
We should use the dump()
built-in function in case the return values
might be composite Lua values like Lua tables, as in
probe foo -> (...)
for i = 1, select('#', ...) do
print(i, ": ", dump(select(i, ...)))
end
end
For Lua function return probes, all local variables visible to the corresponding Lua function return point, upvalues, and global variables can all be accessed. Note that the Lua VM might optimize certain local variables away if the return point does not really reference those local variables.
Any Lua primary expressions can be used as probe specifier, as in
probe (package.loaded["io"].open) -> (file_handle, err)
if file_handle ~= nil then
print("opened file as handle ", file_handle)
else
print("failed to open file: ", err)
end
end
Or even with tracer-space built-in function calls, as in
probe (require "io".open) -> (file_handle, err)
if file_handle ~= nil then
print("opened file as handle ", file_handle)
else
print("failed to open file: ", err)
end
end
Note that the parentheses are required to enclose the expression otherwise there is grammatical ambiguities.
C function probes
C function entry probes
To probe on a C function entry point, we can just write something like below:
probe C:lj_cf_collectgarbage()
print("foo: ", package.loaded.foo)
end
Here we probe onto the entry point of the C function lj_cf_collectgarbage
and
then print out the value of the Lua expression package.loaded.foo
.
Standard probes
begin
Identical to ylang’s _begin
probe.
end
Identical to ylang’s _end
probe.
process.begin
Identical to ylang’s _process.begin
probe.
timer.profile
Identical to ylang’s _timer.profile
probe.
timer.s(N)
Identical to ylang’s _timer.s(N)
probe.
timer.ms(N)
Identical to ylang’s _timer.ms(N)
probe.
Type system
The ylua language has the following value types:
tv
: for storing Lua values in the target process (or “TValue” pointers).num
: for storing double-precision numbers in the tracer space.int
: for storing 32-bit signed integers in the tracer space.bool
: for storing boolean valuestrue
andfalse
in the tracer space.str
: for storing strings in the tracer space.nil
: for thenil
value (orvoid
) in the tracer space.func
: for the built-in or user-defined ylua functions in the trace space.
A ylua variable in the tracer space can take all the data types above except
the nil
type. When a ylua variable is initialized with nil
,
it is of the tv
type. A tv
typed variable can also be assigned to by a nil
-typed expression.
When a function returns no value (like the built-in function print()
), it takes
the return value type of nil
.
Every ylua variable can only take data type and its type must be determined at compile time and must not change at run time.
All the ylua variables for storing values in the target process space must be of the type tv
. And naturally, all values coming from the target process take
the type tv
.
Embedding ylang source snippet
It is possible to embed arbitrary ylang source code snippets using the following syntax:
ylang [=[
_probe _begin {
use_ngx_stream_lua_module = true;
_warn("Start tracing...\n");
}
]=]
Basically the embedded ylang code is put into a Lua string literal (to save the trouble of escaping, long-bracketed string literals are preferred here).
Instead of using ylang to embed top-level ylang code snippets, it is also supported to embed ylang code inside any other contexts like inside a Lua function entry probe handler:
probe foo()
print("foo called!")
ylang [=[
printf("from ylang...\n");
]=]
end
TODO
- Make the
for k, v in pairs(tb)
loop iterate through the array part of the table as well. - Operator
%
should work on floating-point numbers directly. - The
and
andor
operator expressions should not return boolean values only. - Support conversion from number-like string values to numbers.
- Lua’s
string.format
builtin function. - Lua’s
string.find
builtin function. - Lua’s
string.byte
builtin function. - Lua’s
string.char
builtin function. - Lua’s
string.sub
builtin function. - Lua’s
table.concat
builtin function. - Lua’s
collectgarbage("count")
builtin function. - Lua’s
math.*
API functions. - Lua’s
coroutine.status()
API function. - Lua’s
getmetatable()
API function. - Lua’s
getfenv()
API function. - LuaJIT’s
bit.*
API functions. - LuaJIT’s
ffi
API for manipulating cdata’s C data structures and C types, likecdata.field[index]
. - New
table.narr()
builtin function. - New
table.nrec()
builtin function. - New
dump_bt()
anddump_full_bt()
builtin functions for dumping Lua backtraces. Add support fordebug.traceback()
too, which is just aliased todump_bt()
. - New
agg
data type for aggregate variables, the<<<
operator, as well as corresponding stats builtin functions likeagg.count()
,agg.avg()
,agg.max()
agg.min()
,agg.sum()
,agg.hist_log()
,agg.hist_linear()
, and etc. - Support defining user functions in the tracer space (just like defining new Lua functions).
- Support probing on cdata functions like
ffi.C.foo()
. - New
_Continue
keyword and statement, similar to C’scontinue
statement for loops. - Add ylang-style built-in hash table variables and array variables.
- Add built-in probes like
begin
,end
,syscall.*
, and etc.
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.