Skip to content

Commit 47547e1

Browse files
committed
Refactor symbol_key. Everything is packed into u64 + i32. Refactor code for these changes.
1 parent 2aaa470 commit 47547e1

File tree

7 files changed

+224
-157
lines changed

7 files changed

+224
-157
lines changed

perforator/agent/collector/pkg/config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ type PodsDeploySystemConfig struct {
9898
type SymbolizerConfig struct {
9999
Python symbolizer.SymbolizerConfig `yaml:"python"`
100100
Php symbolizer.SymbolizerConfig `yaml:"php"`
101+
Lua symbolizer.SymbolizerConfig `yaml:"lua"`
101102
}
102103

103104
// FeatureFlagsConfig holds agent-side [feature-flags](https://trunkbaseddevelopment.com/feature-flags/)

perforator/agent/collector/pkg/profiler/profiler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ func (p *Profiler) initialize(r metrics.Registry) (err error) {
402402
p.log.Error("SPAR: profiler::initialize -> enabled != nil && *enabled -> true")
403403

404404
p.log.Info("SSE4: Lua is enabled, creating Lua Symbolizer")
405-
p.luaSymbolizer, err = symbolizer.NewLuaSymbolizer(&p.conf.Symbolizer.Php, p.bpf, r)
405+
p.luaSymbolizer, err = symbolizer.NewLuaSymbolizer(&p.conf.Symbolizer.Lua, p.bpf, r)
406406
if err != nil {
407407
return err
408408
}

perforator/agent/collector/pkg/profiler/sample_consumer.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -499,14 +499,13 @@ func (c *oneShotSampleConsumer) collectStacksInto(ctx context.Context, builder *
499499
}
500500

501501
if enableLua := c.p.conf.BPF.TraceLua; enableLua != nil && *enableLua {
502+
c.p.log.Error("SPAR: sample_consumer::collectStacksInto -> enableLua != nil && *enableLua -> true", log.Any("LuaStack", c.sample.LuaStack), log.Any("luaMetrics", c.p.metrics.luaMetrics))
502503
c.collectInterpreterStackInto(
503504
&c.p.metrics.luaMetrics,
504505
builder,
505506
c.luaProcessor,
506507
&c.sample.LuaStack,
507508
)
508-
509-
c.p.log.Error("SPAR: sample_consumer::collectStacksInto -> enableLua != nil && *enableLua -> true", log.Any("LuaStack", c.sample.LuaStack), log.Any("luaMetrics", c.p.metrics.luaMetrics))
510509
c.p.log.Info("SSE4: Lua is enabled, collect Lua Interpreter stack info")
511510
} else {
512511
c.p.log.Error("SPAR: sample_consumer::collectStacksInto -> enableLua != nil && *enableLua -> false")
@@ -618,6 +617,7 @@ func (c *oneShotSampleConsumer) recordSample(ctx context.Context) {
618617

619618
// On CPU / perf event profiling.
620619
func (c *oneShotSampleConsumer) recordCPUSample(ctx context.Context) {
620+
println("SPAR: recordCPUSample")
621621
hasWallTime := c.p.conf.BPF.TraceWallTime != nil && *c.p.conf.BPF.TraceWallTime
622622

623623
sampleTypes := []profile.SampleType{{Kind: "cpu", Unit: "cycles"}}

perforator/agent/collector/pkg/profiler/stack_processor.go

Lines changed: 96 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package profiler
22

33
import (
4+
"fmt"
45
"strconv"
56

67
"github.com/yandex/perforator/perforator/agent/collector/pkg/profile"
7-
lua_models "github.com/yandex/perforator/perforator/internal/linguist/lua/models"
88
"github.com/yandex/perforator/perforator/internal/linguist/models"
99
python_models "github.com/yandex/perforator/perforator/internal/linguist/python/models"
1010
"github.com/yandex/perforator/perforator/internal/linguist/symbolizer"
@@ -45,6 +45,7 @@ func newLuaSampleStackProcessor(symbolizer *symbolizer.Symbolizer) *sampleStackP
4545
}
4646

4747
func (s *sampleStackProcessor) Process(builder *profile.SampleBuilder, stack *unwinder.InterpreterStack) interpreterStackMetrics {
48+
println("SPAR: stack_processor::Process")
4849
processFrame := s.getFrameProcessor()
4950
mtr := interpreterStackMetrics{}
5051

@@ -64,9 +65,11 @@ func (s *sampleStackProcessor) Process(builder *profile.SampleBuilder, stack *un
6465
}
6566

6667
func processFrameCommon(s *sampleStackProcessor, mtr *interpreterStackMetrics, loc *profile.LocationBuilder, frame *unwinder.InterpreterFrame) {
68+
println("SPAR: stack_processor::processFrameCommon")
6769
symbol, exists := s.interpreterSymbolizer.Symbolize(&frame.SymbolKey)
6870
if !exists {
6971
mtr.unsymbolizedFramesCount++
72+
7073
loc.AddFrame().
7174
SetName(models.UnsymbolizedInterpreterLocation).
7275
SetStartLine(int64(frame.SymbolKey.Linestart)).
@@ -83,6 +86,7 @@ func processFrameCommon(s *sampleStackProcessor, mtr *interpreterStackMetrics, l
8386

8487
func processPythonFrame(s *sampleStackProcessor, mtr *interpreterStackMetrics, loc *profile.LocationBuilder, frame *unwinder.InterpreterFrame) {
8588
loc.SetMapping().SetPath(string(s.langMapping)).Finish()
89+
8690
if frame.SymbolKey.Linestart == -1 {
8791
loc.AddFrame().SetName(python_models.PythonTrampolineFrame).Finish()
8892
return
@@ -320,21 +324,8 @@ var ffnames = []string{
320324
"buffer.decode",
321325
}
322326

323-
// see lj_frame.h
324-
var ftsz = []string{
325-
"LUA",
326-
"C",
327-
"CONT",
328-
"VARG",
329-
"LUAP",
330-
"CP",
331-
"PCALL",
332-
"PCALLH",
333-
}
334-
335327
// internal frame decoding errors, see LuaUnwindError at perforator/agent/collector/progs/unwinder/lua/types.h
336328
var frame_decoding_errors = []string{
337-
"",
338329
"frame is null",
339330
"gc func is null",
340331
"bad frame function",
@@ -369,62 +360,131 @@ var entities = []string{
369360
"upvalue",
370361
}
371362

363+
type LuaData struct {
364+
frame *unwinder.InterpreterFrame
365+
}
366+
367+
const (
368+
LuaPointerMask = 0xFFFFFFFFFFFF
369+
LuaPointerBitSize = 48
370+
371+
LuaContextTypeMask = 0x7
372+
373+
LuaFfidMask = 0xFF
374+
LuaFfidLua = 0
375+
LuaFfidC = 1
376+
LuaFfidInvalid = -1
377+
378+
LuaUnwindErrorMask = 0x3
379+
LuaUnwindErrorBitSize = 2
380+
381+
LuaFrameGctMask = 0xF
382+
383+
LuaLineStartNotUsed = -1
384+
)
385+
386+
func (ld *LuaData) getObjectAddress() uint64 {
387+
return ld.frame.SymbolKey.ObjectAddr
388+
}
389+
390+
func (ld *LuaData) GetLineStart() int32 {
391+
return ld.frame.SymbolKey.Linestart
392+
}
393+
394+
func (ld *LuaData) GetFrameType() int {
395+
if ld.GetLineStart() != LuaLineStartNotUsed {
396+
return LuaFfidLua
397+
} else if ld.getObjectAddress() == 0 {
398+
return LuaFfidInvalid
399+
}
400+
401+
return int(ld.GetFfid())
402+
}
403+
404+
func (ld *LuaData) GetPtr() uint64 {
405+
return ld.getObjectAddress() & LuaPointerMask
406+
}
407+
408+
func (ld *LuaData) GetContextType() unwinder.LuaContextType {
409+
return unwinder.LuaContextType((ld.getObjectAddress() >> LuaPointerBitSize) & LuaContextTypeMask)
410+
}
411+
412+
func (ld *LuaData) GetFfid() uint8 {
413+
return uint8((ld.getObjectAddress() >> LuaPointerBitSize) & LuaFfidMask)
414+
}
415+
416+
func (ld *LuaData) GetErrorKind() unwinder.LuaUnwindError {
417+
return unwinder.LuaUnwindError((ld.getObjectAddress() >> LuaPointerBitSize) & LuaContextTypeMask)
418+
}
419+
420+
func (ld *LuaData) GetFrameGct() uint8 {
421+
return uint8((ld.getObjectAddress() >> (LuaPointerBitSize + LuaUnwindErrorBitSize)) & LuaFrameGctMask)
422+
}
423+
372424
func processLuaFrame(s *sampleStackProcessor, mtr *interpreterStackMetrics, loc *profile.LocationBuilder, frame *unwinder.InterpreterFrame) {
425+
println("SPAR: stack_processor::processLuaFrame")
373426

374427
loc.SetMapping().SetPath(string(s.langMapping)).Finish()
428+
375429
symbol, exists := s.interpreterSymbolizer.Symbolize(&frame.SymbolKey)
430+
376431
if !exists {
377432
mtr.unsymbolizedFramesCount++
433+
378434
loc.AddFrame().
379435
SetName(models.UnsymbolizedInterpreterLocation).
380436
SetStartLine(int64(frame.SymbolKey.Linestart)).
381437
Finish()
382438
return
383439
}
440+
441+
luaData := LuaData{frame}
384442
name := symbol.Name
385443
filename := symbol.FileName
386444

387-
if frame.SymbolKey.LuaInvalidFrame != 0 {
388-
// Invalid frame, wasn't decoded as Lua function
445+
switch luaData.GetFrameType() {
446+
case LuaFfidInvalid:
447+
// Invalid frame
389448
name = "<Invalid Lua Frame>"
390-
if int(frame.SymbolKey.LuaGct) < len(gct) {
391-
name += "#" + gct[frame.SymbolKey.LuaGct]
449+
450+
if int(luaData.GetFrameGct()) < len(gct) {
451+
name += "#" + gct[luaData.GetFrameGct()]
392452
}
393-
if int(frame.SymbolKey.LuaInvalidFrame) < len(frame_decoding_errors) {
394-
name += ": " + frame_decoding_errors[frame.SymbolKey.LuaInvalidFrame]
453+
454+
if int(luaData.GetErrorKind()) < len(frame_decoding_errors) {
455+
name += ": " + frame_decoding_errors[luaData.GetErrorKind()]
395456
}
396-
} else if frame.SymbolKey.LuaFfid == 0 {
397-
// Lua function
398-
if frame.SymbolKey.LuaEntity != 0 {
399-
name = entities[frame.SymbolKey.LuaEntity] + " " + name
457+
case LuaFfidLua:
458+
if luaData.GetContextType() != 0 {
459+
name = entities[luaData.GetContextType()] + " " + name
400460
}
401-
name = "[" + ftsz[frame.SymbolKey.LuaFtsz] + "] " + name
402461

403462
// Usually scripts has @ appended at the beginning.
404463
// Perforator has same symbol, removing here.
405464
if filename[0] == '@' {
406465
filename = symbol.FileName[1:]
407466
}
408-
filename += ":" + strconv.Itoa(int(frame.SymbolKey.LuaLine))
409-
} else if frame.SymbolKey.LuaFfid == 1 {
410467

411-
// this C function will be automatically symbolized by the native unwinder, no need to emit duplicated frame
412-
// nevertheless, adding a native frame into interpreter stack confused merge algorithm (see postprocess.go)
413-
name = lua_models.LuaTrampolineFrame
414-
415-
} else {
416-
// interpreter builtin aka fast function (ff)
417-
name = "[builtin] " + ffnames[frame.SymbolKey.LuaFfid] + "#" + strconv.Itoa(int(frame.SymbolKey.LuaFfid))
468+
filename += ":" + strconv.Itoa(int(frame.SymbolKey.Linestart))
469+
case LuaFfidC:
470+
// The frame will be symbolized by postprocess
471+
name = fmt.Sprintf("function: 0x%x", luaData.GetPtr())
472+
default:
473+
// The frame will be symbolized by postprocess
474+
// If postprocess will fail, at least print its builtin number to find the name manually
475+
name = "[builtin] " + ffnames[luaData.GetFfid()] + "#" + strconv.Itoa(int(luaData.GetFfid()))
418476
}
477+
419478
loc.AddFrame().
420-
SetName(name).
479+
SetName("[lua] " + name).
421480
SetFilename(filename).
422481
SetStartLine(int64(frame.SymbolKey.Linestart)).
423482
Finish()
424-
425483
}
426484

427485
func (s *sampleStackProcessor) getFrameProcessor() func(s *sampleStackProcessor, mtr *interpreterStackMetrics, loc *profile.LocationBuilder, frame *unwinder.InterpreterFrame) {
486+
println("SPAR: stack_processor::getFrameProcessor")
487+
428488
switch s.langMapping {
429489
case profile.PythonSpecialMapping:
430490
return processPythonFrame

perforator/agent/collector/progs/unwinder/interpreter/types.h

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,6 @@ struct symbol_key {
44
u64 object_addr;
55
u32 pid;
66
i32 linestart;
7-
// Lua function ID
8-
// FF_LUA = 0: Lua function
9-
// FF_C = 1 C: function
10-
// everything else: fast function (interpreter builtin)
11-
// (think: maybe reuse linestart for FFI, as it's not occupied for FFI?)
12-
u8 lua_ffid;
13-
/* Frame type markers in LSB of PC (4-byte aligned) or delta (8-byte aligned:
14-
**
15-
** PC 00 Lua frame
16-
** delta 001 C frame
17-
** delta 010 Continuation frame
18-
** delta 011 Lua vararg frame
19-
** delta 101 cpcall() frame
20-
** delta 110 ff pcall() frame
21-
** delta 111 ff pcall() frame with active hook
22-
*/
23-
u8 lua_ftsz;
24-
// if non-zero, the frame is invalid, and error code is written (see LuaUnwindError)
25-
u8 lua_invalid_frame;
26-
// see lj_obj.h
27-
u8 lua_gct;
28-
// BCPos type (think: just write to linestart?)
29-
i32 lua_line;
30-
// see LuaEntity
31-
u8 lua_entity;
327
};
338

349
enum {

perforator/agent/collector/progs/unwinder/lua/types.h

Lines changed: 68 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -78,26 +78,77 @@ enum {
7878

7979
// Keep in sync with frame_decoding_errors @
8080
// perforator/agent/collector/pkg/profiler/stack_processor.go
81-
typedef enum {
82-
// zero is reserved to indicate successful frame decoding
83-
LUA_UNWIND_OK = 0,
81+
enum lua_unwind_error : u8 {
8482
LUA_UNWIND_ERROR_FRAME_IS_NULL,
8583
LUA_UNWIND_ERROR_GCFUNC_IS_NULL,
8684
LUA_UNWIND_ERROR_FRAME_IS_NOT_FUNC
87-
} LuaUnwindError;
88-
89-
// actually, there are no true "function names" in lua
90-
// the name of the function is a name of entity (variable) holding it
91-
92-
typedef enum {
93-
LUA_UNKNOWN = 0,
94-
LUA_METAMETHOD,
95-
LUA_LOCAL,
96-
LUA_GLOBAL,
97-
LUA_METHOD,
98-
LUA_FIELD,
99-
LUA_UPVALUE,
100-
} LuaEntity;
85+
};
86+
87+
/**
88+
* @brief
89+
*
90+
*/
91+
enum lua_context_type : u8 {
92+
LUA_CONTEXT_UNKNOWN,
93+
LUA_CONTEXT_METAMETHOD,
94+
LUA_CONTEXT_LOCAL,
95+
LUA_CONTEXT_GLOBAL,
96+
LUA_CONTEXT_METHOD,
97+
LUA_CONTEXT_FIELD,
98+
LUA_CONTEXT_UPVALUE,
99+
};
100+
101+
/**
102+
* @brief Tagged pointer union for lua frames
103+
* In canonical form in most architectures user space addresses are 48 bits
104+
* long.
105+
* This allows to store additional data in upper 16 bits.
106+
*
107+
* |-------------MSW--------------.--------------LSW-------------|
108+
* Raw |-----------------------------u64-----------------------------|
109+
*
110+
* |64-51|---50-48---|-------------------47-0--------------------|
111+
* Lua frame |0...0|---ctx_t---|-------------------proto-------------------|
112+
*
113+
* |64-56|---55-48---|-------------------47-0--------------------|
114+
* C or FF frame |0...0|---ffid----|-------------------addr--------------------|
115+
*
116+
* |64-54|53-50|49-48|-------------------47-0--------------------|
117+
* Invalid frame |0...0|-gct-|-err-|0.........................................0|
118+
*/
119+
union lua_symbol_key_data {
120+
struct {
121+
u64 ptr : 48; // User-space pointer, not necessarily aligned as function
122+
// pointer. Takes 0-47 bits.
123+
union {
124+
struct {
125+
// Origin of the call like global/local/upvalue.
126+
enum lua_context_type context_type : 3;
127+
} lua_frame_data; // Lua frame info. Takes 48-50 bits.
128+
struct {
129+
u8 ffid; // ID of FF function. 1 = Regular C function.
130+
// See isffunc@lj_obj.h
131+
} c_frame_data; // C or FF frame info. Takes 48-55 bits.
132+
struct {
133+
enum lua_unwind_error error_kind : 2; // Encountered error type
134+
u8 frame_gct : 4; // The GC type of this frame
135+
} invalid_frame_data; // Invalid frame info. Takes 48-53 bits.
136+
};
137+
} lua_data; // Tagged pointer with lua frame info
138+
u64 raw; // Raw 64-bit value
139+
};
140+
141+
BTF_EXPORT(enum lua_context_type);
142+
BTF_EXPORT(enum lua_unwind_error);
143+
144+
_Static_assert(LJ_ARCH_ENDIAN == LUAJIT_LE); // TODO: We currently support little-endian architectures only
145+
_Static_assert(sizeof(union lua_symbol_key_data) ==
146+
sizeof(((struct symbol_key *)0)->object_addr),
147+
"Union must match the size of struct symbol_key::object_addr");
148+
149+
// Lua functions do have line information in the range [0; 0x7fffff00).
150+
// Using -1 as indication of C, FF or invalid frame.
151+
static const i32 lua_line_start_not_used = -1;
101152

102153
struct lua_config {
103154
u32 version; // Version of LuaJIT. Encoded as (minor << 8) + (major << 16).

0 commit comments

Comments
 (0)