OpenResty XRay™ Ylua 使用者手冊
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 運算子和語法:
- 一元運算子
# - 一元運算子
not - 一元運算子
- - 二元運算子
+、-、*、/、%、.. - 二元運算子
>、>=、<、<=、==、!= - 二元運算子
and、or - 字尾
[KEY] - 字尾
.KEY - 括號(
()) - 可變引數(
...) - 布林值(
true、false) if/elseif/else語句return語句do語句for v = fr, to, step do迴圈語句for i, e in ipairs(tb)迴圈語句for k, v in pairs(tb)迴圈語句while語句repeat ... until語句break語句local變數宣告(支援多個變數和初始化表示式)- 賦值語句(支援多重賦值)
nil值- 函式呼叫語法
foo(...)、foo"..."、foo{...}等
支援的內建函式
Lua 內建函式
支援以下 Lua 內建函式:
requireprinttypeselecterrortostringtonumberipairs(僅作為for ... in迴圈中的表示式列表)pairs(僅作為for ... in迴圈中的表示式列表)table.maxn
額外的內建函式
支援以下額外的標準函式:
dump轉儲 Lua 值引數的詳細資訊。
out類似於
print(),但不會自動輸出尾隨的換行符。identity直接返回引數值。
exit退出探測會話(即整個追蹤工具)。
warn向 stderr 發出警告訊息。
contains如果第一個字串引數包含第二個字串引數,則返回
true,否則返回false。upval語法:
upval(name)語法:
upval(func, name)返回由
name引數指定名稱的上值。關聯的函式可以是當前正在探測的函式,也可以是由func引數顯式指定的函式。只有 Lua 函式才有命名的上值。此upval()函式也可以在 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 語言具有以下值型別:
tv:用於儲存目標程序中的 Lua 值(或"TValue"指標)。num:用於在追蹤器空間中儲存雙精度數字。int:用於在追蹤器空間中儲存 32 位有符號整數。bool:用於在追蹤器空間中儲存布林值true和false。str:用於在追蹤器空間中儲存字串。nil:用於追蹤器空間中的nil值(或void)。func:用於追蹤空間中的內建或使用者定義的 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
待辦事項
- 使
for k, v in pairs(tb)迴圈也遍歷表的陣列部分。 - 運算子
%應該直接作用於浮點數。 and和or運算子表示式不應該只返回布林值。- 支援從類數字字串值轉換為數字。
- Lua 的
string.format內建函式。 - Lua 的
string.find內建函式。 - Lua 的
string.byte內建函式。 - Lua 的
string.char內建函式。 - Lua 的
string.sub內建函式。 - Lua 的
table.concat內建函式。 - Lua 的
collectgarbage("count")內建函式。 - Lua 的
math.*API 函式。 - Lua 的
coroutine.status()API 函式。 - Lua 的
getmetatable()API 函式。 - Lua 的
getfenv()API 函式。 - LuaJIT 的
bit.*API 函式。 - LuaJIT 的
ffiAPI,用於操作 cdata 的 C 資料結構和 C 型別,如cdata.field[index]。 - 新的
table.narr()內建函式。 - 新的
table.nrec()內建函式。 - 新的
dump_bt()和dump_full_bt()內建函式,用於轉儲 Lua 回溯。也新增對debug.traceback()的支援,它只是dump_bt()的別名。 - 新的
agg資料型別用於聚合變數,<<<運算子,以及相應的統計內建函式,如agg.count()、agg.avg()、agg.max()、agg.min()、agg.sum()、agg.hist_log()、agg.hist_linear()等。 - 支援在追蹤器空間中定義使用者函式(就像定義新的 Lua 函式一樣)。
- 支援在 cdata 函式上進行探測,如
ffi.C.foo()。 - 新的
_Continue關鍵字和語句,類似於 C 的迴圈continue語句。 - 新增 ylang 風格的內建雜湊表變數和陣列變數。
- 新增內建探針,如
begin、end、syscall.*等。
作者
Yichun Zhang (agentzh) yichun@openresty.com
版權與許可
版權所有 (C) 2018-2025 OpenResty Inc. 保留所有權利。
本軟體為專有軟體,不得以任何方式重新分發或共享。
另請參閱
- Ylang 編譯器。