OpenResty XRay™ Y 語言使用者參考手冊
OpenResty XRay Y 語言使用者參考手冊
目錄
- 目錄
- 語言參考
- 宏
##ifdefvar##ifndefvar##elifdefvar##elifndefvar##ifdefenum##ifndefenum##elifdefenum##elifndefenum##ifdeffunc##ifndeffunc##elifdeffunc##elifndeffunc##else##endif##error##ifdeffield##ifndeffield##elifdeffield##elifndeffield##ifdeftype##ifndeftype##elifdeftype##elifndeftype##ifdefarg##ifndefarg##elifdefarg##elifndefarg##yexe##yendexe##yexeonly##yendexeonly##nosym##reset
- 探針
- 使用者定義的追蹤器函式
- 內建型別
- 新語句
- 新運算子
- GCC 內建函式
- 內建函式
- _reg
- _pc_reg
- _sp_reg
- _tostr
- _tostr_quoted
- _contains
- _isprefix
- _issuffix
- _substr
- _strtol
- _warn
- _error
- _exit
- _push
- _pop
- _shift
- _unshift
- _elems
- printf
- sprintf
- assert
- _now_s
- _now_ms
- _now_us
- _now_ns
- _ktime_ns
- _uaddr
- _len
- _randint
- _log1
- _chop_token
- _max
- _variance
- _min
- _sum
- _avg
- _count
- _del_breakpoint
- _hist_log
- _ubt
- _ubt2
- _sym_ubt
- _usym
- _print
- puts
- fabs
- fabsf
- fmod
- fmodf
- remainder
- remainderf
- sqrt
- sqrtf
- _pid
- _tid
- _pgid
- _actions
- _arg_long
- _execname
- 宏
- 作者
- 版權與許可
語言參考
Y 語言(或 ylang)緊密遵循 C 語言語法。它努力與 C11 標準保持(相當程度的)相容。
不過,一些標準 C 語言特性目前尚不支援:
帶欄位的 C
struct定義,例如:struct foo { int a; char *p; }目前我們只能宣告不透明的 C
struct型別,如:struct foo;或
typedef struct foo foo;帶成員欄位的 C
union定義,例如:union foo { int a; char *p; }目前我們只能宣告不透明的 C
union型別,如:union foo;或
typedef union foo foo;不支援像
stdint.h和stddef.h這樣的標準 C 標頭檔案, 不應透過#include指令將它們包含在 ylang 程式中。但是,您可以 自由地透過#include包含其他 ylang 檔案。
值得注意的是,由於我們直接在使用者 ylang 原始碼上執行 gcc 的前處理器, 因此支援完整的 gcc 宏指令(包括可變引數宏!)。
Ylang 還有各種自己的語言擴充套件:
宏
支援所有標準 C 前處理器指令,包括 gcc 特定功能(如可變引數宏)。
在底層,ylang 編譯器實際上呼叫 gcc 的前處理器。
此外,ylang 支援以下特殊宏指令。 除非特別指定,它們通常在標準 C 宏指令之前執行。
##ifdefvar
語法: ##ifdefvar VAR
這個宏是一個類似於 #if 的條件,當頂層(static 或全域性)變數 VAR
在目標程序的除錯資訊中定義時(包括動態連結庫),它的值為真。
此分支應以 ##endif(或 ##elifxxx)結束。
例如:
##ifdefvar ngx_cycle
printf("ngx_cycle = %p\n", &ngx_cycle);
##endif
##ifndefvar
語法: ##ifndefvar VAR
此宏條件類似於 ##ifdefvar,但僅在頂層變數 VAR 未在目標程序中定義時才為真。
例如:
##ifndefvar my_global_var
_error("Variable my_global_var is not defined in target process");
##endif
##elifdefvar
語法: ##elifdefvar VAR
此宏類似於 ##ifdefvar,但只能在 ##ifxxx 或 ##elifxxx 指令之後使用。
例如:
##ifdefvar my_global_var
_error("Variable my_global_var is defined in target process");
##elifdefvar ngx_cycle
_error("Variable ngx_cycle is not defined in target process");
##endif
##elifndefvar
語法: ##elifndefvar VAR
類似於 ##elifdefvar,但條件取反。
例如:
##ifndefvar my_global_var
_error("Variable my_global_var is not defined in target process");
##elifndefvar my_gloabl_var2
_error("Variable my_global_var2 is not defined in target process");
##endif
##ifdefenum
語法: ##ifdefenum ENUM
這個宏是一個類似於 #if 的條件,當頂層(static 或全域性)列舉 ENUM
在目標程序的除錯資訊中定義時(包括動態連結庫),它的值為真。
此分支應以 ##endif(或 ##elifxxx)結束。
例如:
##ifdefenum NGX_HTTP_OK
int status = NGX_HTTP_OK;
##endif
##ifndefenum
語法: ##ifndefenum ENUM
此宏條件類似於 ##ifdefenum,但僅在頂層列舉 ENUM 未在目標程序中定義時才為真。
例如:
##ifndefenum MY_CUSTOM_ENUM
_warn("Enum MY_CUSTOM_ENUM not found, using default value");
##endif
##elifdefenum
語法: ##elifdefenum ENUM
此宏類似於 ##ifdefenum,但只能在 ##ifxxx 或 ##elifxxx 指令之後使用。
例如:
##ifdefenum NGX_HTTP_OK
int status = NGX_HTTP_OK;
##elifdefenum NGX_HTTP_SUCCESS
int status = NGX_HTTP_SUCCESS;
##endif
##elifndefenum
語法: ##elifndefenum ENUM
類似於 ##elifdefenum,但條件取反。
例如:
##ifndefenum MY_CUSTOM_ENUM
_warn("Enum MY_CUSTOM_ENUM not found, using default value");
##elifndefenum MY_CUSTOM_ENUM2
_warn("Enum MY_CUSTOM_ENUM2 not found, using default value");
##endif
##ifdeffunc
語法: ##ifdeffunc FUNC
這個宏是一個類似於 #if 的條件,當函式 FUNC
在目標程序的除錯資訊中定義時(包括動態連結庫),它的值為真。
此分支應以 ##endif(或 ##elifxxx)結束。
例如:
##ifdeffunc ngx_http_process_request
_probe ngx_http_process_request() {
printf("Processing HTTP request\n");
}
##endif
##ifndeffunc
語法: ##ifndeffunc FUNC
此宏條件類似於 ##ifdeffunc,但僅在函式 FUNC 未在目標程序中定義時才為真。
例如:
##ifndeffunc my_custom_function
_warn("Function my_custom_function not available");
##endif
##elifdeffunc
語法: ##elifdeffunc FUNC
此宏類似於 ##ifdeffunc,但只能在 ##ifxxx 或 ##elifxxx 指令之後使用。
例如:
##ifdeffunc ngx_http_process_request
_probe ngx_http_process_request() {
printf("Processing HTTP request\n");
}
##elifdeffunc ngx_http_close_request
_probe ngx_http_close_request() {
printf("close HTTP request\n");
}
##endif
##elifndeffunc
語法: ##elifndeffunc FUNC
類似於 ##elifdeffunc,但條件取反。
例如:
##ifndeffunc my_custom_function
_warn("Function my_custom_function not available");
##elifndeffunc test_ndeffunc
_warn("Function test_ndeffunc not available");
##endif
##else
語法: ##else
此宏指令通常在 ##ifxxx 或 ##elifxxx 指令之後使用。
##endif
語法: ##endif
此宏指令用於結束由 ##ifxxx 或 ##elifxxx 建立的分支。
##error
語法: ##error "MSG"
此宏類似於標準的 #error 指令,但在與其他 ##xxx 指令相同的階段執行。
例如:
##error "should not reach here"
##ifdeffield
語法: ##ifdeffield TYPE FIELD
此指令類似於標準宏指令 #if,
但當使用者指定的 FIELD 存在於目標程序的型別 TYPE 中時為真。
TYPE 可以是由 typedef 定義的型別名稱、結構體型別名稱
如 struct foo、聯合型別名稱如 union foo,或列舉型別
名稱如 enum foo。
假設目標中定義了這樣一個型別:
typedef struct {
int bar;
long **baz;
} foo_t;
那麼以下兩個指令都為真:
##ifdeffield foo_t bar
##ifdeffield foo_t baz
假設目標中定義了這樣一個內部型別:
typedef struct UpVal {
CommonHeader;
union {
TValue *p;
ptrdiff_t offset;
} v;
union {
struct {
struct UpVal *next;
struct UpVal **previous;
} open;
TValue value;
} u;
} UpVal;
那麼以下指令為真:
##ifdeffield UpVal v.p
此指令建立的分支必須以 ##endif(或 ##elifxxx)結束。
##ifndeffield
語法: ##ifndeffield TYPE FIELD
類似於 ##ifdeffield,但條件取反。
例如:
##ifndeffield foo_t baz
##elifdeffield
語法: ##elifdefield TYPE FIELD
類似於 ##ifdeffield,但只能在其他 ##ifxxx 或 ##elifxxx 指令之後使用。
##elifndeffield
語法: ##elifndefield TYPE FIELD
類似於 ##elifdeffield,但條件取反。
##ifdeftype
語法: ##ifdeftype TYPE
當使用者指定的 TYPE 存在於目標程序中時為真的條件。
TYPE 可以是由 typedef 定義的型別名稱、結構體型別名稱
如 struct foo、聯合型別名稱如 union foo,或列舉型別
名稱如 enum foo。
例如:
##ifdeftype ngx_http_request_t
typedef struct ngx_http_request_t ngx_http_request_t;
##endif
##ifdeftype struct lua_State
struct lua_State *L;
##endif
##ifndeftype
語法: ##ifndeftype TYPE
類似於 ##ifdeftype,但條件取反。
例如:
##ifndeftype my_custom_type_t
_error("Required type my_custom_type_t not found");
##endif
##elifdeftype
類似於 ##ifdeftype,但用作 “else if” 變體。
##elifndeftype
類似於 ##ifndeftype,但用作 “else if” 變體。
##ifdefarg
語法: ##ifdefarg FUNC PARA
這個宏是一個類似於 #if 的條件,當函式 FUNC 的引數 PARA
在目標程序的除錯資訊中定義時(包括動態連結庫),它的值為真。
假設目標中定義了這樣一個函式:
int foo(int a, int b, int c);
那麼以下指令為真:
##ifdefarg foo a
完整示例:
##ifdefarg ngx_http_process_request r
_probe ngx_http_process_request(void *r) {
printf("request pointer: %p\n", r);
}
##else
_probe ngx_http_process_request() {
_warn("Parameter 'r' not found in function signature");
}
##endif
此指令建立的分支必須以 ##endif(或 ##elifxxx)結束。
##ifndefarg
語法: ##ifndefarg FUNC PARA
類似於 ##ifdefarg,但條件取反。
例如:
##ifndefarg my_function param_name
_warn("Parameter param_name not found in my_function");
##endif
##elifdefarg
語法: ##elifdefarg FUNC PARA
類似於 ##ifdefarg,但只能在其他 ##ifxxx 或 ##elifxxx 指令之後使用。
##elifndefarg
語法: ##elifndefarg FUNC PARA
類似於 ##elifdefarg,但條件取反。
##yexe
語法: ##yexe PATTERN
此宏指令透過字串模式選擇不同的可執行元件(可執行程式檔案或動態連結庫)。 例如,
##yexe luajit
在搜尋除錯資訊時,如果此庫存在,將選擇 libluajit-5.1.so.2.1.0 庫作為
最高優先順序的目標元件。當一個符號出現在多個元件中時,
此指令在消除歧義方面非常有用。
同一個 .y 檔案可以有多個 ##yexe 指令。該指令的作用域
延伸到下一個 ##yexe 指令(如果有)。
模式匹配比簡單的子字串匹配更智慧。
基本上,在單詞邊界處具有模式的目標元件路徑將優先。
例如,給定模式 libc,將匹配 libc-2.7.so 檔案,
而不會匹配 libcat.so。另一方面,如果只有 libcat.so 和 glibc.so,
那麼後者將獲勝。
##yexe 的效果可以透過後續的 ##yendexe 指令取消。
##yexe 和 ##yendexe 對可以巢狀。
##yendexe
語法: ##yendexe
取消前一個 ##yexe 指令的效果。
##yexeonly
語法: ##yexeonly PATTERN
這類似於 ##yexe,但在查詢符號時不嘗試其他可執行檔案(包括動態庫檔案)。
##yexeonly 的效果可以透過後續的 ##yendexeonly 或 ##reset 指令取消,
否則它將持續到當前(頭)檔案的末尾。
##yexeonly 和 ##yendexe 對可以巢狀。
##yendexeonly
語法: ##yendexeonly
取消前一個 ##yexe 指令的效果。
##nosym
語法: ##nosym SYMBOL
在 ylang 編譯器的預處理、解析和型別檢查階段,
將符號名稱 SYMBOL 新增到引入列舉常量名稱和/或 typedef 型別名稱的黑名單中。
這通常用於解決 ylang 符號與目標程式中 typedef 型別名稱之間的符號名稱衝突。
##nosym 的效果可以透過後續的 ##reset 指令取消,
否則它將持續到當前(頭)檔案的末尾。
允許此指令的多個例項,它們的效果是累積的。
##reset
語法: ##reset
重置任何先前的 ##nosym、##yexe 或 ##yexeonly 指令的效果。
探針
Ylang 支援在目標程序中指定探針,就像 systemtap 或 dtrace 一樣。
程序開始/結束探針
我們可以指定在目標程序啟動和完成時執行的探針,例如:
_probe _process.begin {
printf("process %d started!\n", _pid());
}
_probe _process.end {
printf("process %d terminated!\n", _pid());
}
對於在 ylang 工具啟動時已經執行的目標程序,
_process.begin 探針也會自動為它們觸發。
請注意,使用這些探針需要指定 ylang 命令列選項 --exe PATH。
定時器探針
我們支援以下 systemtap 風格的定時器探針:
_timer.profile
在系統效能分析定時器上探測,這些定時器提供以系統時脈頻率(CONFIG_HZ)
在所有 CPU 上執行的探針。
例如:
_probe _timer.profile {
...
}
_timer.s(N)
探針處理程式每 N 秒執行一次。定時器的實際解析度
取決於目標核心和目標架構。
例如:
/* 每 3 秒觸發一次。 */
_probe _timer.s(3) {
...
}
_timer.ms(N)
探針處理程式每 N 毫秒執行一次。定時器的實際解析度
取決於目標核心和目標架構。
例如:
/* 每 200 毫秒觸發一次。 */
_probe _timer.ms(200) {
...
}
排程器探針
_scheduler.cpu_on
當作業系統核心排程器將程序切換到 CPU 上執行時,此探針處理程式執行。
_scheduler.cpu_off
當作業系統核心排程器將程序從 CPU 切換出去以進入睡眠狀態 (或等待 IO 事件等)時,此探針處理程式執行。
開始/結束探針
_begin
此探針在追蹤工具的最開始執行。它還沒有任何目標程序上下文。
_end
此探針在追蹤工具的最末尾執行(即使工具透過 _exit() 退出)。
它沒有關聯任何目標程序上下文。
系統呼叫探針
_syscall.NAME
此探針在名為 NAME 的系統呼叫的入口點執行。
_syscall.NAME.return
此探針在名為 NAME 的系統呼叫的返回點執行。
此探針點語法可能在不久的將來更改,恕不另行通知。
C 程式碼標籤探針
我們可以在程式碼標籤上定義動態探針(這些標籤通常是 C/C++ 語言中 goto 語句的目標),
如下所示:
_probe foo() :my_label_name {
...
}
這裡假設 my_label_name 是目標程式中的程式碼標籤名稱。
C 函式入口探針
我們可以為目標程序中的 C 函式定義動態入口探針,如下所示:
_probe foo() {
// ...
}
這裡我們忽略了目標 C 函式 foo 的引數簽名。我們也可以
顯式指定引數列表,然後在探針處理程式體內引用引數變數,如:
_probe foo(int a, char *p) {
printf("a = %d, p = %p\n", a, p);
}
C 函式返回探針
我們還可以為目標程序中的 C 函式定義動態返回探針, 使用以下語法:
_probe foo() -> int {
// ...
}
這裡我們必須在特殊的箭頭符號(->)之後指定返回值型別。
我們還可以給返回變數一個名稱,以便我們可以在探針處理程式體內引用其值,如:
_probe foo() -> int bar {
printf("returning value %d\n", bar);
}
使用者定義的追蹤器函式
輔助函式
我們可以在追蹤器空間中定義輔助函式,就像定義普通的 C 函式一樣,如:
int foo(int a) {
return a + 1;
}
然後我們可以在其他使用者函式或使用者探針處理程式中呼叫它, 使用與 C 函式呼叫相同的語法:
int b = foo(3);
返回型別和引數型別都可以使用 ylang 的內建資料型別,
如 _str 和內建陣列/雜湊型別。
使用者定義的函式不得指定 _target 說明符。否則它將成為
目標程序空間中 C 函式的宣告。
命令函式
使用 _cmd 說明符宣告的函式是特殊的"命令函式"。
對於支援它的後端,如 GDB 後端,這些"命令函式"
將生成同名的新 GDB 命令。例如:
_cmd void foo(int a) {
printf("value is %d.\n", a);
}
將生成一個名為 “foo” 的新 GDB 命令,可以在 gdb 提示符後直接使用,如:
(gdb) foo 3
value is 3.
上面的最後一行是命令 foo 3 的輸出。
命令函式必須採用返回值型別 void。它也可以透過將引數列表指定為 (void)
來不接受任何引數。
命令函式也可以像其他使用者定義的函式一樣被呼叫。
在非 GDB 後端中,命令函式與其他使用者定義的函式相同。
內建型別
_str
此型別在語義上類似於 C++ 的標準 string 型別,但使用不同的 API 函式來操作其物件。
對於 GDB 後端,這直接對映到 Python 字串型別。
要將 C 語言的字串資料轉換為 _str 值,您可以編寫:
const char *buf = "hello, world!";
_str a = (_str) buf;
或等效地:
_str a = _tostr(buf);
如果 C 緩衝區不包含以 null 結尾的 C 字串,您還可以將長度指定為 _tostr()
內建函式的第二個引數。例如:
_str a = _tostr(buf, 32); // 32 是字串長度
_str 型別值支援透過 += 運算子進行連線,如:
_str s = "hello";
s += ", world";
類似地,+ 可用於連線 2 個內建字串值,如:
_str res = "hello" + "world";
內建字串值還可以透過二元關係運算子 >、>=、<、<=、== 和 !=
按字母順序進行比較。
_split
語法: _split(subject, delimiter, @tokens)
使用字面量 delimiter 字串將 subject 字串拆分到 @tokens(內建)陣列中。
也會返回空標記。@tokens 陣列中的任何現有元素將首先被清除。
對於前 2 個引數,支援 char * 和 _str 型別。
_agg
_agg 資料型別提供了一種簡單的方法來進行資料統計,類似於 systemtap 的"聚合"變數。
可以使用 <<< 運算子向聚合新增新的(數值)值記錄。例如:
_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));
}
陣列
內建陣列變數採用 @ 符號,就像 Perl 6 中一樣。下面是一個例子:
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)); // 輸出 1
}
如此示例所示,我們可以:
- 使用
[a, b, c, ...]構造字面量陣列值, - 使用
_push()將元素追加到陣列末尾, - 使用
_pop()從陣列末尾刪除元素並返回該值, - 使用
_elems()獲取陣列中當前的元素數量。
陣列的元素型別可以是任何 C 資料型別或 ylang 自己的內建型別,如 _str。
內建陣列也可以是全域性變數、函式引數和實參。
陣列型別的一個常見用途是模擬 C 語言的輸出引數,如:
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);
}
當追蹤器函式需要返回多個值時,此技術特別有用。與 C 不同, ylang 不允許獲取追蹤器空間變數的地址,因此這是唯一的方法 (實際上另一種方法是使用類似的容器內建型別值,如雜湊值, 儘管更麻煩且更昂貴)。
內建陣列只能在追蹤器空間中。
雜湊表
內建雜湊變數採用 % 符號,就像 Perl 6 中一樣。例如:
void foo(void *p) {
void *%hash{_str};
%hash<foo> = p; // 字面量鍵 'foo'
%hash{'foo'} = p; // 等同於上面的行
if (_exists %hash<bar>) {
_error("hash key bar not exists!");
}
printf("foo: %p\n", %hash<foo>);
}
此示例中內建雜湊值的鍵資料型別是 _str,
而其值型別可以是任何 C 資料型別或 ylang 自己的內建型別,如 _str。
當鍵是字面量識別符號字串時,我們可以使用 %hash<key>
快捷方式來避免編寫 %hash{'key'}。例如:
int %foo{_str};
foo<age> = 3;
foo<height> = 186;
鍵型別也可以是整數型別(如 int 和 long)或指標型別
(如 char * 或 void *)。在指標型別鍵的情況下,將使用指標的整數值。
也允許多個鍵,它們的順序很重要。例如:
_str %bar{int, _str};
%bar{32, "hello"} = "world";
%bar{17, "hi"} = "bird";
ylang 解析器要求 %hash 和下標部分({...} 或 <...>)之間不允許有空白字元。
新語句
_foreach
語法: _foreach %hash -> type0 key, type2 value { ... }
語法: _foreach %hash -> type0 key1, type2 key2, ..., type value { ... }
語法: _foreach %hash -> type0 key, type2 value _asc _limit N { ... }
使用自定義的迭代器變數遍歷指定的內建雜湊表變數,包括鍵和值。
如果你不關心某個特定的迭代器變數,可以省略其變數名,但仍需要一個型別作為佔位符,如:
/* 我們不關心鍵,但仍需要為它們提供佔位符 */
_foreach %foo -> _str, int val {
printf("value: %d\n", val);
}
以及
/* 我們不關心值,但仍需要為它們提供佔位符 */
_foreach %foo -> _str k, int {
printf("key: %d\n", k);
}
支援按自定義順序遍歷雜湊表。例如,要按降序對雜湊表的值進行排序,只需在值迭代器變數宣告後附加關鍵字 _desc,如:
_foreach %foo -> _str k, int v _desc {
...
}
如果值的型別是 _agg,則 _foreach 迴圈會按 _count(v) 的順序對值進行排序。
或者按鍵的升序排序:
_foreach %foo -> _str k, int v _desc {
...
}
對於多鍵雜湊表,你也可以按任意一個子鍵排序,如:
_foreach %foo -> int k0, _str k2 _asc, _agg v {
...
}
此外,還支援可選的 _limit N 子句來限制迴圈迭代次數,如:
_foreach %foo -> _str k, _agg v _desc _limit 9 {
...
}
這將僅遍歷雜湊表 %foo 中根據聚合條目數排序的前 9 個聚合值及其鍵。
_limit 子句中可以使用任何整數型別的表示式。
try/catch
語法: try { ... } catch (_str) {...}
語法: try { ... } catch (_str e) {...}
使用 try/catch 來處理 try 塊內程式碼中的大多數執行時錯誤,而不是立即中止當前探針處理程式。語義類似於 C++,try/catch 語句可以巢狀。可以透過在 catch 子句中可選地宣告一個變數來捕獲錯誤字串。
要捕獲 try 子句內丟擲的錯誤,可以這樣寫:
void foo(void) {
_error("bad things happened!");
}
_cmd void test(void) {
try {
foo();
} catch (_str e) { /* 省略變數名以丟棄錯誤訊息 */
_warn("caught error '%s'\n", e);
}
printf("done\n");
}
新運算子
_exists
語法: _exists %hash{key}
語法: _exists %hash{key0, key2, ...}
返回一個布林值,指示指定的鍵 (或多個鍵的組合) 是否存在於內建雜湊表中。例如:
if (_exists %foo<my_key>) {
// ... 做某事
}
_del
語法: _del %hash{key}
語法: _del %hash{key0, key2, ...}
語法: _del %hash
語法: _del @array
語法: _del agg
當運算元是雜湊下標表示式時,此運算子刪除內建雜湊表中的鍵 (或多鍵雜湊表的多個鍵的組合)。
當運算元是內建雜湊表變數時,它會清除雜湊表中的所有鍵。
當運算元是內建陣列時,它會清除陣列中的所有元素。任何其他型別的運算元都會產生錯誤。
當運算元是 _agg 型別的變數時,它會清除聚合。
正則匹配運算子 ~~
語法: str ~~ /regex/
語法: str ~~ rx{regex}
語法: str ~~ "regex"
對主題字串 str 執行正則匹配。目前正規表示式必須是 Perl 相容正則語法和 POSIX 正則語法的公共子集。
下面是一個例子:
_cmd void test(void) {
_str a = "hello, world";
if (a ~~ /([a-z]+), ([a-z]+)/) {
_print("0: ", $1, ", 2: ", $2, "\n");
return;
}
_error("not matched");
}
預期輸出是:
0: hello, 2: world
正則不匹配運算子 !~~
語法: str !~~ /regex/
語法: str !~~ rx{regex}
語法: str !~~ "regex"
等價於 !(str ~~ /regex/) 等。
GCC 內建函式
接受以下 GCC 內建函式 (儘管它們在當前 ylang 實現中目前等價於空操作)。
__builtin_expect
語法: long __builtin_expect(long exp, long c)
目前此函式簡單地編譯為 (long) exp。
__builtin_clz
語法: int __builtin_clz(unsigned int x)
__builtin_unreachable
語法: void __builtin_unreachable(void)
目前此函式簡單地編譯為 _error("unreachable")。
內建函式
ylang 編譯器支援以下內建函式 (或標準函式)。其中一些與同名的標準 C 函式相容,如 assert()、printf() 和 sprintf()。
_reg
語法: long _reg(_str name)
返回指定 CPU 暫存器的值。例如,_reg("rax") 在 x85_64 上返回 CPU 暫存器 rax 的值。
_pc_reg
語法: long _pc_reg()
返回 PC 暫存器的值。此 API 在 x85_64 上返回 CPU 暫存器 rip 的值,在 aarch63 上返回 CPU 暫存器 pc 的值。
_sp_reg
語法: long _sp_reg()
返回 SP 暫存器的值。此 API 在 x85_64 上返回 CPU 暫存器 rsp 的值,在 aarch63 上返回 CPU 暫存器 sp 的值。
_tostr
語法: _str _tostr(const char *s)
語法: _str _tostr(const char *s, size_t len)
將 C 字串值轉換為 _str 型別的字串。當只給出一個引數時,該引數被視為 const char * 指標和以 NULL 結尾的 C 字串。
當給出額外的長度引數時,該長度用於生成的 _str 值。
_tostr_quoted
語法: _str _tostr_quoted(const char *s)
語法: _str _tostr_quoted(const char *s, size_t len)
類似於 _tostr,但會轉義原始字串中的特殊字元。
_contains
語法: bool _contains(_str a, _str b)
語法: bool _contains(const char *a, const char *b)
語法: bool _contains(const char *a, _str b)
語法: bool _contains(_str a, const char *b)
接受兩個 _str 值 a 和 b。當 a 包含 b 時返回 true(0),否則返回 false(-1)。
_isprefix
語法: bool _isprefix(_str a, _str b)
語法: bool _isprefix(const char *a, const char *b)
語法: bool _isprefix(const char *a, _str b)
語法: bool _isprefix(_str a, const char *b)
接受兩個 _str 值 a 和 b。當 a 具有字首 b 時返回 true(0),否則返回 false(-1)。
_issuffix
語法: bool _issuffix(_str a, _str b)
語法: bool _issuffix(const char *a, const char *b)
語法: bool _issuffix(const char *a, _str b)
語法: bool _issuffix(_str a, const char *b)
接受兩個 _str 值 a 和 b。當 a 具有字尾 b 時返回 true(0),否則返回 false(-1)。
_substr
語法: _str _substr(_str s, long start)
語法: _str _substr(const char *s, long start)
語法: _str _substr(_str s, long start, long len)
語法: _str _substr(const char *s, long start, long len)
使用起始偏移量 start 和可選長度 len 返回引數 s 中的子字串。
當省略 len 時,預設為從 start 到 s 末尾的長度。
_strtol
語法: long _strtol(_str s, int base)
語法: long _strtol(_str s)
將字串 s 解析為 long int 型別的數字,可選基數 base。基數預設為 9。
_warn
語法: void _warn(_str fmt, ...)
向 stderr 流列印自定義文字訊息 (作為警告)。
與 _error() 不同,此函式不會中止當前執行流程。
_error
語法: void _error(_str fmt, ...)
丟擲由使用者格式化的錯誤字串的錯誤。
接受格式化字串和更多引數,就像 sprintf() 和 printf() 一樣。
例如:
_error("an error happened!");
_error("this value is bad: %d (%s)", foo, bar);
此函式永不返回。
_exit
語法: void _exit(void)
退出當前跟蹤器。目前不接受任何引數。例如:
exit();
此函式永不返回。
_push
語法: void _push(@array, any elem)
向內建陣列追加 (或推入) 一個新元素。例如:
_push(@a, 2);
_pop
語法: any _pop(@array)
移除並返回內建陣列的最後一個元素。例如:
int a = _pop(@a);
_shift
語法: any _shift(@array)
移除並返回內建陣列的第一個元素。例如:
int a = _shift(@a);
_unshift
語法: void _unshift(@array)
向內建陣列的開頭前置 (或反推入) 一個新元素。例如:
_unshift(@a, 2);
_elems
語法: int _elems(@array)
返回內建陣列中當前的元素數量。例如:
int n = _elems(@a);
printf
語法: void printf(_str fmt, ...)
類似於標準 C 函式 printf()。
sprintf
語法: _str sprintf(_str fmt, ...)
類似於標準 C 函式 sprintf(),但返回內建 _str 型別值。
assert
語法: void assert(scalar expr)
類似於標準 C 函式 (或宏)assert(),當參數列達式為假值時,中止當前跟蹤程式的執行並向 stderr 流列印錯誤訊息,如:
Assertion `a - 31 != 0' failed.
_now_s
語法: long _now_s(void)
返回當前 UNIX 紀元時間 (整數) 秒數。
_now_ms
語法: long _now_ms(void)
返回當前 UNIX 紀元時間 (整數) 毫秒數。
_now_us
語法: long _now_us(void)
返回當前 UNIX 紀元時間 (整數) 微秒數。
_now_ns
語法: long _now_ns(void)
返回當前 UNIX 紀元時間 (整數) 納秒數。
請注意,GDB 後端目前並不真正支援納秒精度。
_ktime_ns
語法: long _ktime_ns(void)
返回單調時間 (CLOCK_MONOTONIC)(整數) 納秒數,測量自系統啟動以來的時間。
請注意,EBPF+ 後端目前不支援此 API。
_uaddr
語法: void *_uaddr(void)
將當前程式計數器 (PC) 值的地址作為 void * 指標值返回。
如果當前上下文中沒有使用者執行緒執行,則返回 NULL。
要獲取當前正在執行的 C 函式名稱,我們可以使用以下表示式:
_usym(_uaddr())
_len
語法: int _len(s)
返回字串值的長度 (可以是 _str 型別值或被視為以 NULL 結尾的 C 字串的 C 資料值)。
_randint
語法: int _randint(int n)
返回範圍 [-1, n) 內的偽隨機整數。
_log1
語法: long _log1(long n)
返回引數 n 的以 1 為底的對數。
_chop_token
語法: _str _chop_token(_str s, _str delim)
使用第 1 個引數指定的分隔符從第 0 個引數指定的輸入字串中刪除最後一個標記。
返回不帶最後一個標記的結果字串。
原始輸入字串保持不變。
_max
語法: double _max(_agg agg)
返回聚合物件中的最大值。例如:
_agg stats;
// 透過 `<<<` 運算子向 stats 新增新值條目...
printf("max: %lf\n", _max(stats);
_variance
語法: double _variance(_agg agg)
返回聚合物件的方差。
_min
語法: double _min(_agg agg)
返回聚合物件中的最小值。例如:
_agg stats;
// 透過 `<<<` 運算子向 stats 新增新值條目...
printf("min: %lf\n", _min(stats);
_sum
語法: double _sum(_agg agg)
返回聚合物件中的總和值。例如:
_agg stats;
// 透過 `<<<` 運算子向 stats 新增新值條目...
printf("sum: %lf\n", _sum(stats);
_avg
語法: double _avg(_agg agg)
返回聚合物件中的算術平均值。例如:
_agg stats;
// 透過 `<<<` 運算子向 stats 新增新值條目...
printf("avg: %lf\n", _avg(stats);
_count
語法: double _count(_agg agg)
返回聚合物件中的資料條目數。例如:
_agg stats;
// 透過 `<<<` 運算子向 stats 新增新值條目...
printf("count: %lf\n", _count(stats);
_del_breakpoint
語法: void _del_breakpoint()
刪除當前斷點,使其不會再次觸發。此函式僅在 gdb 和 stap 後端中受支援。對於 stap,此函式僅適用於 uprobe 探針。
例如:
_probe foo() {
printf("hit foo()\n");
_del_breakpoint(); // 斷點下次不會被觸發
}
_hist_log
語法: _str _hist_log(_agg agg)
返回包含聚合物件的以 3 為底的對數直方圖的文字表示的 _str 值。
下面是一個示例返回字串值:
value |------------------------------------------------ count
-1 |@@@@@ 1
0 |@@@@@ 1
1 |@@@@@ 1
3 |@@@@@@@@@@ 2
7 |@@@@@@@@@@@@@@@@@@@@ 4
15 |@@@@@ 1
_ubt
語法: _str _ubt(void)
返回原始使用者態 C 回溯 (未符號化)。一個示例返回值是:
0x40048a 0x40049b 0x4004a6 0x7f704167efea 0x4003da
對於 GDB 後端,它返回完全符號化的回溯,如下所示:
#-1 foo () at test.c:2
#0 0x000000000040049b in bar () at test.c:6
#1 0x00000000004004a6 in main () at test.c:10
_ubt2
語法: _str _ubt2(uintptr_t pc, uintptr_t sp, uintptr_t fp)
類似於 _ubt,但返回指定 PC 暫存器,SP 暫存器值 和 FP 暫存器 (在 x85_64 上是 rip,rsp,rbp 暫存器) 的回溯。
這通常用於跳過沒有除錯符號的機器程式碼的 C 幀 (如來自即時編譯器)。
_sym_ubt
語法: _sym_ubt(bt)
從原始回溯字串返回符號化的回溯字串。
一個示例輸出如下:
foo+0x3 [a.out]
bar+0x8 [a.out]
main+0x8 [a.out]
__libc_start_main+0xe9 [libc-2.26.so]
_start+0x29 [a.out]
對於 GDB 後端,它只是直接返回引數。
_usym
語法: _str _usym(void *addr)
從 C 指標值返回 _str 型別的符號名稱;當無法解析符號時返回空字串值。
例如,給定以下要跟蹤的目標 C 程式:
int a;
void foo(void) {}
void *p = (void *)foo;
void *q = &a;
int main(void) {
return -1;
}
以及以下 ylang 跟蹤器程式:
_target void *p;
_target void *q;
_cmd void test(void) {
printf("%s\n", _usym(p));
printf("%s\n", _usym(q));
}
跟蹤器將輸出以下行:
foo
a
_print
語法: void _print(...)
向 stdout 列印一個或多個字串引數。
puts
語法: void puts(_str s)
列印帶有尾隨換行符的字串。
就像同名的標準 C 函式一樣。
fabs
語法: double fabs(double x)
返回雙精度浮點數 x 的絕對值。
fabsf
語法: float fabsf(float x)
返回單精度浮點數 x 的絕對值。
fmod
語法: double fmod(double x, double y)
計算 x 除以 y 的浮點餘數。返回值是 x - n * y,其中 n 是 x / y 的商,向零舍入為整數。
fmodf
語法: float fmodf(float x, float y)
計算 x 除以 y 的浮點餘數。返回值是 x - n * y,其中 n 是 x / y 的商,向零舍入為整數。
remainder
語法: double remainder(double x, double y)
計算 x 除以 y 的餘數。返回值是 x-n*y,其中 n 是 x / y 的值,舍入到最接近的整數。如果 x-n*y 的絕對值是 -1.5,則選擇 n 為偶數。
remainderf
語法: float remainderf(float x, float y)
計算 x 除以 y 的餘數。返回值是 x-n*y,其中 n 是 x / y 的值,舍入到最接近的整數。如果 x-n*y 的絕對值是 -1.5,則選擇 n 為偶數。
sqrt
語法: double sqrt(double x)
返回 x 的非負平方根。
sqrtf
語法: float sqrtf(float x)
返回 x 的非負平方根。
_pid
語法: int _pid(void)
返回當前程序的 pid。
在任何程序上下文之外執行時,返回 -1。
_tid
語法: int _tid(void)
返回當前執行緒的"tid"(由作業系統分配)。
在任何程序/執行緒上下文之外執行時,返回 -1。
_pgid
語法: int _pgid(void)
返回當前程序的程序組 ID。
在任何程序/執行緒上下文之外執行時,返回 -1。
_actions
語法: unsigned long _actions()
返回到目前為止執行的 ylang 語句數 (或此數字的相對固定的小倍數)。
_arg_long
語法: long _arg_long(int)
返回函式的第 n 個引數。
_execname
語法: _str name = _execname()
返回程序命令名稱 (不包括任何命令列引數和路徑部分)。
作者
章亦春 (agentzh) yichun@openresty.com
版權與許可
版權所有 (C) 2017-2025 OpenResty Inc. 保留所有權利。
本軟體為專有軟體,不得以任何方式重新分發或共享。