OpenResty XRay™ Ylua 用户手册 (original) (raw)

7 分钟阅读 | 最后修改: 2025-10-28, 3:47:04 pm

ylua - 用于编写 Lua 应用追踪工具的类 Lua 语言编译器。

目录

概要

语言语法示例

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

返回目录

语言特性

支持的 Lua 语法

支持以下 Lua 运算符和语法:

返回目录

支持的内置函数

Lua 内置函数

支持以下 Lua 内置函数:

返回目录

额外的内置函数

支持以下额外的标准函数:

probe (upval(require "foo".blah, "uvname")) (a, b)  
    ...  
end  

返回目录

探针

Lua 函数探针

Lua 函数入口探针

支持在 Lua 函数入口点上进行动态探测。然而,某些直接用手工编写的汇编代码实现的 Lua 内置函数可能会错过探针,例如 math 命名空间下的一些 API 函数。

以下是一个示例:

probe foo(a)
    print("arg a = ", a)
end

每次进入该函数(由于函数调用)时,它都会打印出目标中全局 Lua 函数 foo 的实际参数 a 的值。

当目标进程中实际 Lua 函数参数的数量可能变化时,也支持可变参数语法。例如:

probe foo(...)
    for i = 1, select('#', ...) do
        print(i, ": ", select(i, ...))
    end
end

此探针处理程序将输出目标中全局 Lua 函数 foo 每次调用的所有实际参数。一个典型的输出可能如下所示:

1: 3
2: hello
3: 3.140000

如果目标 Lua 函数可能返回复合 Lua 值(如 Lua 表),我们应该在此示例中使用 dump() 内置函数,如下所示:

probe foo(...)
    for i = 1, select('#', ...) do
        print(i, ": ", dump(select(i, ...)))
    end
end

一个示例输出如下:

1: 171
2: "hello"
3: true
4: table (GCtab*)0x7f96f754d830 (narr=0, nrec=1):
 key:
  "dogs"
 value:
  -3.140000

注意 dump() 的输出与直接打印 Lua 目标值的区别。

对于 Lua 函数入口探针,可以引用参数变量、上值变量和全局变量。非参数的局部变量不能在此类探针处理程序中引用,因为它们在该点自然尚未初始化。以下是一个示例:

probe foo(a)
    print(a + b)
end

编译此 ylua 代码示例将产生以下警告:

WARNING: bar: symbol 'b' is assumed to be an upvalue or a global variable in the target Lua process at test.ylua line 2

显然,引用的变量 b 未声明,将被假定为目标中函数 foo 的上值,或者在探针触发时当前 Lua 线程的全局 Lua 变量。出于显而易见的原因,当前 Lua 函数的上值优先于全局变量查找。

任何 Lua 主表达式都可以用作探针说明符,如下所示:

probe (package.loaded["io"].open)(file_name, mode)
    print("opening file ", file_name, " with mode ", mode)
end

甚至可以使用追踪器空间的内置函数调用,如下所示:

probe (require "io".open)(file_name, mode)
    print("opening file ", file_name, " with mode ", mode)
end

注意,必须使用括号括起表达式,否则会存在语法歧义。

返回目录

Lua 函数返回探针

支持在 Lua 函数返回点上进行动态探测。然而,由于尾调用固有的"goto"性质,可能会错过我们的探针。

考虑以下示例:

probe foo -> ()
    print("function foo returning!")
end

每次目标中的全局 Lua 函数 foo() 返回时,此探针处理程序都会打印出 function foo returning! 这一行。这里我们不关心该函数退出时是否返回任何值。但当我们关心时,可以像这样检查返回值:

probe foo -> (a, b)
    print("returning ", a, " and ", b)
end

此探针将输出目标 Lua 函数每次返回的前 2 个返回值。

与 Lua 函数入口探针类似,返回探针也支持 Lua 可变参数语法(...),以便在返回值数量不固定或事先未知时检查所有返回值。以下是这样一个示例:

probe foo -> (...)
    for i = 1, select('#', ...) do
        print(i, ": ", select(i, ...))
    end
end

一个典型的输出如下:

1: 171
2: hello
3: true
4: -3.140000

如果返回值可能是复合 Lua 值(如 Lua 表),我们应该使用 dump() 内置函数,如下所示:

probe foo -> (...)
    for i = 1, select('#', ...) do
        print(i, ": ", dump(select(i, ...)))
    end
end

对于 Lua 函数返回探针,可以访问对应 Lua 函数返回点可见的所有局部变量、上值和全局变量。请注意,如果返回点实际上不引用某些局部变量,Lua VM 可能会优化掉这些局部变量。

任何 Lua 主表达式都可以用作探针说明符,如下所示:

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

甚至可以使用追踪器空间的内置函数调用,如下所示:

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

注意,必须使用括号括起表达式,否则会存在语法歧义。

返回目录

C 函数探针

C 函数入口探针

要在 C 函数入口点上进行探测,我们可以编写如下内容:

probe C:lj_cf_collectgarbage()
    print("foo: ", package.loaded.foo)
end

这里我们在 C 函数 lj_cf_collectgarbage 的入口点上进行探测,然后打印出 Lua 表达式 package.loaded.foo 的值。

返回目录

标准探针

begin

与 ylang 的 _begin 探针相同。

返回目录

end

与 ylang 的 _end 探针相同。

返回目录

process.begin

与 ylang 的 _process.begin 探针相同。

返回目录

timer.profile

与 ylang 的 _timer.profile 探针相同。

返回目录

timer.s(N)

与 ylang 的 _timer.s(N) 探针相同。

timer.ms(N)

与 ylang 的 _timer.ms(N) 探针相同。

类型系统

ylua 语言具有以下值类型:

追踪器空间中的 ylua 变量可以采用上述所有数据类型,但 nil 类型除外。当 ylua 变量用 nil 初始化时,它是 tv 类型。tv 类型的变量也可以由 nil 类型的表达式赋值。

当函数不返回值时(如内置函数 print()),它的返回值类型为 nil

每个 ylua 变量只能采用一种数据类型,其类型必须在编译时确定,并且在运行时不得更改。

所有用于存储目标进程空间中值的 ylua 变量必须是 tv 类型。自然地,来自目标进程的所有值都采用 tv 类型。

返回目录

嵌入 ylang 源代码片段

可以使用以下语法嵌入任意 ylang 源代码片段:

ylang [=[
    _probe _begin {
        use_ngx_stream_lua_module = true;
        _warn("Start tracing...\n");
    }
]=]

基本上,嵌入的 ylang 代码被放入 Lua 字符串字面量中(为了省去转义的麻烦,这里首选长括号字符串字面量)。

除了使用 ylang 嵌入顶层 ylang 代码片段外,还支持在任何其他上下文中嵌入 ylang 代码,例如在 Lua 函数入口探针处理程序中:

probe foo()
    print("foo called!")
    ylang [=[
        printf("from ylang...\n");
    ]=]
end

返回目录

待办事项

返回目录

作者

Yichun Zhang (agentzh) yichun@openresty.com

返回目录

版权与许可

版权所有 (C) 2018-2025 OpenResty Inc. 保留所有权利。

本软件为专有软件,不得以任何方式重新分发或共享。

返回目录

另请参阅

返回目录