Repository navigation
Conversation
✅ All contributors are covered under a CLA with YandexSee CONTRIBUTING.md for more info about Yandex Contributor License Agreement. The following contributors were found: (Only the first commit for a unique contributor is listed) |
056d81d to
7920ea9
Compare
✅ All contributors are covered under a CLA with YandexSee CONTRIBUTING.md for more info about Yandex Contributor License Agreement. The following contributors were found: (Only the first commit for a unique contributor is listed) |
4a222bc to
7920ea9
Compare
7920ea9 to
96f837a
Compare
✅ All contributors are covered under a CLA with YandexSee CONTRIBUTING.md for more info about Yandex Contributor License Agreement. The following contributors were found: (Only the first commit for a unique contributor is listed) |
GCproto is written as name in symbol then Go side reads remaning parts.
96f837a to
2839b46
Compare
✅ All contributors are covered under a CLA with YandexSee CONTRIBUTING.md for more info about Yandex Contributor License Agreement. The following contributors were found: (Only the first commit for a unique contributor is listed) |
This PR adds current source-line tracking and infers function names from callers, making profiles easier to interpret. Before this only
in main chunkfunction had names, others were<no name>and profiler used first line where the function was defined instead of using current line when the sample happened. This PR also fixes some bugs.Changes
How it works
Collect the frame and source position in eBPF. Now Lua frame contains its prototype address, definition line, current line, and bytecode index where it stopped (depends on the next upper frame). Nextframe is required to get bytecode PC from it, but if sample happened during execution of Lua function (that is it's on top), the unwinder obtains the bytecode PC from
rbx. This part allow to get current source line when the top frames!Capture metadata for later name lookup. The interpreter symbol cache stores the whole
GCprotostruct (104 bytes) and chunk filename. The header occupies the existing symbol-name field as binary data, so the Lua symbolizer preserves it without treating embedded NUL bytes as terminators (probably needs redoing, because it looks like a monkey patching). The Go agent uses this captured data to read the prototype’s bytecode, constants, and debug metadata from process memory. Go doesn't readGCprotowhich means it cheaper, basically comes for free!Infer the callee’s name from its caller. Frames arrive from callee to caller. The caller’s bytecode identifies the operation that entered the callee. For ordinary calls, name lookup checks local-variable scopes and walks backward through register assignments to find a global, field, or upvalue name. Metamethod calls derive their names directly from the caller’s opcode. Basically, what LuaJIT does in its source code.
Cache metadata and preserve distinct names. Reads and caches are bounded. Cached string names belong to a specific decoded prototype snapshot, preventing fresh snapshots from inheriting names through reused addresses. Profile location keys include the current line and resolved name, preserving different caller-derived names for the same function. And each read is guarded by checks to make sure it's still valid.
Unavailable bytecode positions fall back to the definition line and unresolved names still use
<no name>. This usually happens when Lua function was called by a C function. pcall/xpcall also loses information but we might have a fix for this later :-)Synthetic JIT PCs and some hook/error paths remain unsupported.