Skip to content

Commit 7378461

Browse files
committed
feat: support memcmp built-in function
1 parent bcfaa50 commit 7378461

15 files changed

Lines changed: 653 additions & 63 deletions

File tree

docs/configuration.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,12 @@ proc_module_offsets_max_entries = 4096 # Default
289289
# Increase to dump more bytes per argument.
290290
mem_dump_cap = 4096
291291

292+
# Compare cap for built-in comparisons (strncmp/starts_with/memcmp).
293+
# Controls the maximum number of bytes compared per call.
294+
# Effective compare length is min(max(len, 0), compare_cap).
295+
# Default: 64 bytes.
296+
compare_cap = 64
297+
292298
# Maximum size of a single trace event (bytes). Applies to PerfEventArray accumulation buffer.
293299
max_trace_event_size = 32768
294300

@@ -364,6 +370,7 @@ max_entries = 10000
364370
[ebpf]
365371
ringbuf_size = 1048576 # 1MB buffer for high event rates
366372
mem_dump_cap = 4096 # Larger per-arg dump
373+
compare_cap = 64 # Max bytes for built-in compares (strncmp/memcmp)
367374
max_trace_event_size = 65536 # Larger event size for big formatted prints
368375
proc_module_offsets_max_entries = 8192 # Support many modules
369376

@@ -379,6 +386,7 @@ enable_console_logging = false
379386
[ebpf]
380387
ringbuf_size = 131072 # 128KB minimal buffer
381388
mem_dump_cap = 512
389+
compare_cap = 32 # Smaller compare cap for minimal overhead
382390
max_trace_event_size = 16384
383391
proc_module_offsets_max_entries = 1024 # Single process only
384392

docs/scripting.md

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ Performance and safety notes:
364364
365365
- Comparisons are compiled into bounded, branch-light checks to be verifier-friendly on most kernels. Still, placing many string comparisons in a single probe or attaching at extremely hot sites may add CPU and verifier load.
366366
- Prefer attaching at less-hot lines/functions if you only need occasional confirmation (e.g., update sites for a string field), or split multiple heavy comparisons into separate trace points.
367-
- Builtins (`strncmp`/`starts_with`) cap read length to 64 bytes for safety (see `STRING_BUILTIN_READ_CAP` in `ghostscope-compiler/src/ebpf/expression.rs`).
367+
- Builtins (`strncmp`/`starts_with`) cap read length by `ebpf.compare_cap` (default 64 bytes).
368368
CString equality reads only `L+1` bytes (no extra cap beyond the literal length).
369369
370370
// Floats are not supported in GhostScope scripts.
@@ -379,12 +379,21 @@ Supported built-ins (phase 1):
379379
- Does not require a terminating NUL within `n` bytes.
380380
- `expr` may be a DWARF `char*`, `char[N]`, or a generic pointer expression; GhostScope performs a bounded user-memory read and compares bytes.
381381
- Any read failure evaluates to `false`.
382-
- Read length is capped at 64 bytes; if `n` exceeds the cap or the available array size, it is truncated. See `STRING_BUILTIN_READ_CAP` in `ghostscope-compiler/src/ebpf/expression.rs`.
382+
- Read length is capped by configuration `ebpf.compare_cap` (default 64 bytes). If `n` exceeds the cap or the available array size, it is truncated.
383383
384384
- `starts_with(expr, "lit")`
385385
- Equivalent to `strncmp(expr, "lit", len("lit"))`.
386386
- Same failure and safety semantics as above.
387387
388+
- `memcmp(expr_a, expr_b, len)`
389+
- Boolean variant: returns `true` iff the first `len` bytes at `expr_a` and `expr_b` are identical.
390+
- Pointer vs pointer only; for literal comparisons against strings, use `strncmp`/`starts_with` instead.
391+
- `len` can be a script integer expression; negative values clamp to 0, and a configurable cap (`ebpf.compare_cap`, default 64) applies. The effective compare length is `min(max(len,0), CAP)`.
392+
- No NUL-terminator semantics; compares raw bytes only.
393+
- Any read failure on either side evaluates to `false`.
394+
- If `len == 0`, the result is `true` and no user-memory read is performed (fast-path).
395+
- Implementation is verifier-friendly: reads are bounded and comparisons are accumulated without early exits.
396+
388397
Verifier friendliness and performance:
389398
- Compiles to branch-light byte comparisons (e.g., XOR/OR accumulation) to avoid verifier state explosion.
390399
- Avoid packing many large string checks into a single very hot probe; consider splitting trace points or using less-hot sites when possible.
@@ -408,6 +417,19 @@ trace globals_program.c:32 {
408417
trace process_record {
409418
print "rec_http:{}", strncmp(record, "HTTP", 4); // record: struct* -> false
410419
}
420+
421+
// Raw memory equality between two pointers
422+
trace globals_program.c:32 {
423+
// Equal bytes
424+
if memcmp(&lib_pattern[0], &lib_pattern[0], 16) { print "EQ"; } else { print "NE"; }
425+
// Different due to offset
426+
if memcmp(&lib_pattern[0], &lib_pattern[1], 16) { print "EQ2"; } else { print "NE2"; }
427+
// len=0 → true
428+
if memcmp(&lib_pattern[0], &lib_pattern[1], 0) { print "Z0"; }
429+
// Dynamic length from script variable
430+
let n = 10;
431+
if memcmp(&lib_pattern[0], &lib_pattern[0], n) { print "DYN_EQ"; }
432+
}
411433
```
412434
```
413435

docs/zh/configuration.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,11 @@ proc_module_offsets_max_entries = 4096 # 默认
287287
# 扩展格式占位符({:x}/{:s})单个参数的内存转储上限(字节)。
288288
mem_dump_cap = 4096
289289

290+
# 内置比较(strncmp/starts_with/memcmp)的最大比较字节数。
291+
# 实际比较长度为 min(max(len, 0), compare_cap)。
292+
# 默认:64 字节。
293+
compare_cap = 64
294+
290295
# 单条 trace 事件的最大大小(字节)。适用于 PerfEventArray 累计缓冲区。
291296
max_trace_event_size = 32768
292297

@@ -361,6 +366,8 @@ max_entries = 10000
361366
# 针对高频事件跟踪优化
362367
[ebpf]
363368
ringbuf_size = 1048576 # 1MB 缓冲区用于高事件率
369+
mem_dump_cap = 4096 # 单参数转储上限更高
370+
compare_cap = 64 # 内置比较最大比较字节数(strncmp/memcmp)
364371
proc_module_offsets_max_entries = 8192 # 支持更多模块
365372

366373
[general]
@@ -374,6 +381,8 @@ enable_console_logging = false
374381
# 生产环境最小资源占用
375382
[ebpf]
376383
ringbuf_size = 131072 # 128KB 最小缓冲区
384+
mem_dump_cap = 512
385+
compare_cap = 32 # 降低内置比较上限以减小开销
377386
proc_module_offsets_max_entries = 1024 # 仅单进程
378387

379388
[general]

docs/zh/scripting.md

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,19 +251,28 @@ print "tail={:s.n$}", p; // 长度来自变量 n
251251

252252
## 内置函数
253253

254-
GhostScope 提供两个字符串内置函数,用于高效、对 verifier 友好的比较
254+
GhostScope 提供若干对 verifier 友好的内置比较函数
255255

256256
- `strncmp(expr, "lit", n)`
257257
-`expr` 指向的内存前 `n` 个字节与字面量 `lit` 比较。
258258
-`n` 字节内不要求出现终止符 `\0`
259259
- `expr` 可为 DWARF 的 `char*``char[N]`,也可为通用指针;GhostScope 会做有界的用户态内存读取并按字节比较。
260260
- 任何读取失败均返回 `false`
261-
- 为安全起见比较长度固定上限为 64 字节(编译器常量 `STRING_BUILTIN_READ_CAP`,见 `ghostscope-compiler/src/ebpf/expression.rs`);若 `n` 超过上限或数组可用长度,会自动裁剪。
261+
- 比较长度由配置 `ebpf.compare_cap` 控制(默认 64 字节);若 `n` 超过上限或数组可用长度,会自动裁剪。
262262

263263
- `starts_with(expr, "lit")`
264264
- 等价于 `strncmp(expr, "lit", len("lit"))`
265265
- 失败与安全语义同上。
266266

267+
- `memcmp(expr_a, expr_b, len)`
268+
- 布尔语义:若 `expr_a``expr_b` 所指内存的前 `len` 个字节完全一致,返回 `true`,否则 `false`
269+
- 仅支持指针对指针;若需与字符串字面量比较,请使用 `strncmp`/`starts_with`
270+
- `len` 支持脚本整数表达式;负值会被钳为 0,并受配置上限 `ebpf.compare_cap`(默认 64 字节)裁剪。有效比较长度为 `min(max(len,0), CAP)`
271+
- 不涉及 NUL 终止;按原始字节比较。
272+
- 任一侧读取失败均按 `false` 处理。
273+
-`len == 0`,结果为 `true`,且不会执行任何用户内存读取(快速路径)。
274+
- 实现为固定上界、无早退的按字节累积比较,便于通过 verifier。
275+
267276
Verifier 友好与性能:
268277
- 内置函数生成的比较逻辑尽量少分支(如按字节 XOR/OR 累积),降低 verifier 状态数量。
269278
- 避免在极热的探针里塞入大量大字符串比较;必要时拆分脚本或选择不那么热的落点。
@@ -287,6 +296,19 @@ trace globals_program.c:32 {
287296
trace process_record {
288297
print "rec_http:{}", strncmp(record, "HTTP", 4); // record: struct* -> false
289298
}
299+
300+
// 两个指针之间的原始内存等值比较
301+
trace globals_program.c:32 {
302+
// 完全相等
303+
if memcmp(&lib_pattern[0], &lib_pattern[0], 16) { print "EQ"; } else { print "NE"; }
304+
// 偏移后产生差异
305+
if memcmp(&lib_pattern[0], &lib_pattern[1], 16) { print "EQ2"; } else { print "NE2"; }
306+
// len=0 → true
307+
if memcmp(&lib_pattern[0], &lib_pattern[1], 0) { print "Z0"; }
308+
// 动态长度来自脚本变量
309+
let n = 10;
310+
if memcmp(&lib_pattern[0], &lib_pattern[0], n) { print "DYN_EQ"; }
311+
}
290312
```
291313

292314
## 条件语句

0 commit comments

Comments
 (0)