Skip to content

Commit c9d1322

Browse files
committed
Store all information needed to symbolize a frame in one frame instead of using previousFrame hack
1 parent c7e13bd commit c9d1322

File tree

10 files changed

+162
-135
lines changed

10 files changed

+162
-135
lines changed

host/host.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ type Frame struct {
4444
Lineno libpf.AddressOrLineno
4545
Type libpf.FrameType
4646
ReturnAddress bool
47+
LJCalleePC uint32
48+
LJCallerPC uint32
4749
}
4850

4951
type Trace struct {

interpreter/luajit/luajit.go

Lines changed: 16 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,7 @@ type luajitInstance struct {
7979
traceHashes map[libpf.Address]uint64
8080
cycle int
8181

82-
// In order to symbolize frame n we need to know the caller or frame n+1 so we don't
83-
// symbolize frame n until symbolize is called fo frame n+1.
84-
previousFrame *host.Frame
85-
g2Traces uint16
82+
g2Traces uint16
8683
}
8784

8885
var (
@@ -349,12 +346,7 @@ func (l *luajitInstance) getGCproto(pt libpf.Address) (*proto, error) {
349346

350347
// symbolizeFrame symbolizes the previous (up the stack)
351348
func (l *luajitInstance) symbolizeFrame(symbolReporter reporter.SymbolReporter,
352-
funcName string, trace *libpf.Trace) error {
353-
if l.previousFrame == nil || l.previousFrame.File == 0 {
354-
return errors.New("previous frame not set")
355-
}
356-
ptAddr := libpf.Address(l.previousFrame.File)
357-
pc := uint32(l.previousFrame.Lineno)
349+
funcName string, trace *libpf.Trace, ptAddr libpf.Address, pc uint32) error {
358350
var line uint32
359351
var fileName string
360352
if ptAddr != C.LUAJIT_FFI_FUNC {
@@ -388,29 +380,25 @@ func (l *luajitInstance) symbolizeFrame(symbolReporter reporter.SymbolReporter,
388380
return nil
389381
}
390382

391-
// Symbolize is a little weird in lua since we need the caller frame to get the name of the
392-
// function being called. So we stash the info for the current frame and each symbolize call
393-
// actually symolizes the previous frame. The unwinder emits a special frame with file 0 to
394-
// indicate the end of the lua stack.
395383
func (l *luajitInstance) Symbolize(symbolReporter reporter.SymbolReporter, frame *host.Frame,
396384
trace *libpf.Trace) error {
397385
if !frame.Type.IsInterpType(libpf.LuaJIT) {
398386
return interpreter.ErrMismatchInterpreterType
399387
}
400388

401-
// We need the calling frame to find the name of the current frame so save it and do it on next
402-
// frame. This assumes that the harness calls Symbolize from top to bottom of the stack in
403-
// order!! The final luajit frame will have a 0 frame.File.
404-
if l.previousFrame == nil {
405-
l.previousFrame = frame
406-
logf("lj: new LuaJIT frame")
389+
if frame.File == 0 && frame.Lineno != 0 {
390+
// The BPF program will stash pointer to "G" when it sees a JIT frame w/o trace information
391+
// which may fail to unwind, we use it to see if the traces for this VM have changed. When
392+
// we reach a steady state where there's no new JIT activity this will always be 0.
393+
g := libpf.Address(frame.Lineno)
394+
if g != 0 {
395+
l.mu.Lock()
396+
defer l.mu.Unlock()
397+
l.vms[g] = struct{}{}
398+
}
407399
return nil
408400
}
409401

410-
// The function being invoked at this frame (caller) is the name for the previous
411-
// frame (callee). For the last frame frame.File will be 0 and pt will be nil and funcName
412-
// will be "main". Lua is a real deal dynamic functional language, functions don't have
413-
// inherent names they are just values.
414402
var funcName string
415403
if frame.File == C.LUAJIT_FFI_FUNC {
416404
switch frame.Lineno & 7 {
@@ -431,32 +419,15 @@ func (l *luajitInstance) Symbolize(symbolReporter reporter.SymbolReporter, frame
431419
funcName = "ff-pcall-hook"
432420
}
433421
} else {
434-
ptaddr := libpf.Address(frame.File)
435-
pc := uint32(frame.Lineno)
436-
pt, err := l.getGCproto(ptaddr)
422+
callerPT := libpf.Address(frame.Lineno)
423+
pt, err := l.getGCproto(callerPT)
437424
if err != nil {
438425
return err
439426
}
440-
funcName = pt.getFunctionName(pc)
427+
funcName = pt.getFunctionName(frame.LJCallerPC)
441428
}
442429

443-
err := l.symbolizeFrame(symbolReporter, funcName, trace)
444-
445-
if frame.File == 0 {
446-
logf("lj: end LuaJIT frame")
447-
l.previousFrame = nil
448-
// The BPF program will stash pointer to "G" when it sees a JIT frame w/o trace information
449-
// which may fail to unwind, we use it to see if the traces for this VM have changed. When
450-
// we reach a steady state where there's no new JIT activity this will always be 0.
451-
g := libpf.Address(frame.Lineno)
452-
if g != 0 {
453-
l.mu.Lock()
454-
defer l.mu.Unlock()
455-
l.vms[g] = struct{}{}
456-
}
457-
} else {
458-
l.previousFrame = frame
459-
}
430+
err := l.symbolizeFrame(symbolReporter, funcName, trace, libpf.Address(frame.File), frame.LJCalleePC)
460431

461432
return err
462433
}

interpreter/luajit/luajit_test.go

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,14 @@ func TestIntegration(t *testing.T) {
8484
"1.21.4.3-buster",
8585
"1.25.3.2-bullseye",
8686
"jammy",
87+
"alpine",
8788
} {
8889
t.Run(tag, func(t *testing.T) {
8990
image := openrestyBase + ":" + tag
9091
ctx, cancel := context.WithCancel(context.Background())
9192
t.Cleanup(cancel)
9293

9394
defer cancel()
94-
r := &mockReporter{symbols: make(symbolMap)}
95-
traceCh, trc := startTracer(ctx, t, r)
9695

9796
cont := startContainer(ctx, t, image)
9897

@@ -106,13 +105,22 @@ func TestIntegration(t *testing.T) {
106105
defer waitGroup.Wait()
107106
makeRequests(ctx, t, &waitGroup, tc.resource, h, port)
108107

108+
r := &mockReporter{symbols: make(symbolMap)}
109+
traceCh, trc := startTracer(ctx, t, r)
110+
109111
passes, fails, traces := 0, 0, 0
110112
done:
111113
for {
112114
select {
113115
case <-ctx.Done():
114116
break done
115117
case trace := <-traceCh:
118+
st, err := cont.State(ctx)
119+
require.NoError(t, err)
120+
// See if PID is openresty
121+
if int(trace.PID) != st.Pid {
122+
continue
123+
}
116124
traces++
117125
if validateTrace(t, trc, trace, tc.structure, r) {
118126
passes++
@@ -151,18 +159,6 @@ func validateTrace(t *testing.T, trc *tracer.Tracer, trace *host.Trace,
151159
return false
152160
}
153161

154-
var zeroFileIdx, lastFrameIdx int
155-
for i, t := range trace.Frames {
156-
if t.Type == libpf.LuaJITFrame {
157-
if t.File == 0 {
158-
zeroFileIdx = i
159-
}
160-
lastFrameIdx = i
161-
}
162-
}
163-
164-
require.Equal(t, zeroFileIdx, lastFrameIdx)
165-
166162
// Finally convert it to flex all the proto parsing/remote code
167163
ct := trc.TraceProcessor().ConvertTrace(trace)
168164
require.NotNil(t, ct)

interpreter/types.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,7 @@ type Instance interface {
140140

141141
// Symbolize requests symbolization of the given frame, and dispatches this symbolization
142142
// to the collection agent. The frame's contents (frame type, file ID and line number)
143-
// are appended to newTrace. This must be called on each from in order from
144-
// top to bottom of stack to support functional languages which infer callee
145-
// name from caller frame.
143+
// are appended to newTrace.
146144
Symbolize(symbolReporter reporter.SymbolReporter, frame *host.Frame,
147145
trace *libpf.Trace) error
148146

support/ebpf/errors.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,10 @@ typedef enum ErrorCode {
173173
ERR_LUAJIT_FRAME_READ = 7002,
174174

175175
// LuaJIT: context pointer validity check failed
176-
ERR_LUAJIT_L_MISMATCH = 7003
176+
ERR_LUAJIT_L_MISMATCH = 7003,
177+
178+
// LuaJIT: PC exceeds 24 bits
179+
ERR_LUAJIT_INVALID_PC = 7004
177180
} ErrorCode;
178181

179182
#endif // OPTI_ERRORS_H

0 commit comments

Comments
 (0)