Skip to content

Commit 911d0dc

Browse files
authored
Merge pull request #69 from parca-dev/unit-test
Unit test for register zero and rsi/esi bugs Remove old mappings before adding new ones to avoid map collisions.
2 parents cb38ee8 + aca46f6 commit 911d0dc

File tree

3 files changed

+117
-7
lines changed

3 files changed

+117
-7
lines changed

interpreter/luajit/luajit.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ package luajit // import "go.opentelemetry.io/ebpf-profiler/interpreter/luajit"
1313

1414
import (
1515
"errors"
16+
"fmt"
1617
"path"
1718
"strings"
1819
"sync"
@@ -256,22 +257,26 @@ func (l *luajitInstance) synchronizeMappings(ebpf interpreter.EbpfHandler, pid l
256257
l.jitRegions[*m] = cycle
257258
}
258259

259-
// Add new ones and remove garbage ones
260+
// Remove old ones
260261
for m, c := range l.jitRegions {
261262
k := regionKey{start: m.Vaddr, end: m.Vaddr + m.Length}
262263
if c != cycle {
263264
for _, prefix := range l.prefixes[k] {
264265
if err := ebpf.DeletePidInterpreterMapping(pid, prefix); err != nil {
265-
return err
266+
return errors.Join(err, fmt.Errorf("failed to delete prefix %v", prefix))
266267
}
267268
}
268269
delete(l.jitRegions, m)
269270
delete(l.prefixes, k)
270-
} else {
271-
if _, ok := l.prefixes[k]; !ok {
272-
if err := l.addJITRegion(ebpf, pid, m.Vaddr, m.Vaddr+m.Length); err != nil {
273-
return err
274-
}
271+
}
272+
}
273+
274+
// Add new ones
275+
for m := range l.jitRegions {
276+
k := regionKey{start: m.Vaddr, end: m.Vaddr + m.Length}
277+
if _, ok := l.prefixes[k]; !ok {
278+
if err := l.addJITRegion(ebpf, pid, m.Vaddr, m.Vaddr+m.Length); err != nil {
279+
return errors.Join(err, fmt.Errorf("failed to add JIT region %v", m))
275280
}
276281
}
277282
}

interpreter/luajit/offsets_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,45 @@ func getLibFromImage(t *testing.T, name, platform, fullPath, target string) {
164164
require.NoError(t, err)
165165
}
166166

167+
func TestX86LuaClose(t *testing.T) {
168+
testdata := []struct {
169+
name string
170+
glRefExpected uint64
171+
curLExpected uint64
172+
code []byte
173+
}{
174+
{
175+
name: "size-optimized-register-zero",
176+
glRefExpected: 0x10,
177+
curLExpected: 0x158,
178+
code: []byte{
179+
0x41, 0x55, // pushq %r13
180+
0x4c, 0x8d, 0x2d, 0x3f, 0xd4, 0xff, 0xff, // leaq -0x2bc1(%rip), %r13
181+
0x41, 0x54, // pushq %r12
182+
0x41, 0xbc, 0x0a, 0x00, 0x00, 0x00, // movl $0xa, %r12d
183+
0x55, // pushq %rbp
184+
0x53, // pushq %rbx
185+
0x51, // pushq %rcx
186+
0x48, 0x8b, 0x5f, 0x10, // movq 0x10(%rdi), %rbx
187+
0x48, 0x8b, 0xab, 0xc8, 0x00, 0x00, 0x00, // movq 0xc8(%rbx), %rbp
188+
0x48, 0x89, 0xef, // movq %rbp, %rdi
189+
0xe8, 0x6e, 0x17, 0x00, 0x00, // callq 0x175f0 <luaJIT_profile_stop>
190+
0x31, 0xf6, // xorl %esi, %esi
191+
0x48, 0x89, 0xef, // movq %rbp, %rdi
192+
0x48, 0x89, 0xb3, 0x58, 0x01, 0x00, 0x00, // movq %rsi, 0x158(%rbx)
193+
},
194+
},
195+
}
196+
197+
for _, test := range testdata {
198+
x := x86Extractor{}
199+
glref, curL, err := x.findOffsetsFromLuaClose(test.code)
200+
require.NoError(t, err)
201+
require.Equal(t, test.glRefExpected, glref)
202+
require.Equal(t, test.curLExpected, curL)
203+
}
204+
}
205+
167206
// spot testing
168207
func TestFiles(t *testing.T) {
169208
files, err := os.ReadDir("./testdata")

tools/dis2go.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/usr/bin/env python3
2+
3+
import re
4+
import sys
5+
6+
def transform_disassembly(lines):
7+
"""
8+
Transform llvm-objdump disassembly to a Go byte array literal.
9+
Written by Claude.
10+
11+
This function:
12+
1. Removes offset prefixes (like "15e5b:")
13+
2. Adds "0x" prefix to each byte and comma separators
14+
3. Comments out the disassembly parts with "//"
15+
16+
Args:
17+
lines: A list of input lines to transform
18+
19+
Returns:
20+
A list of transformed lines
21+
"""
22+
result = []
23+
24+
for line in lines:
25+
# Skip empty lines
26+
if not line.strip():
27+
continue
28+
29+
# Extract the parts using regex
30+
# Match the address prefix, then bytes, then the assembly instruction
31+
match = re.match(r'^\s*[0-9a-f]+:\s+([0-9a-f\s]+)(.+)$', line)
32+
if match:
33+
# Get the machine code bytes
34+
bytes_str = match.group(1).strip()
35+
# Get the assembly instruction
36+
asm_instr = match.group(2).strip()
37+
38+
# Split the bytes and add 0x prefix and commas
39+
bytes_list = [f"0x{b}" for b in bytes_str.split()]
40+
formatted_bytes = ", ".join(bytes_list) + ","
41+
42+
# Pad with spaces to align comments
43+
pad_length = max(40 - len(formatted_bytes), 1)
44+
padding = " " * pad_length
45+
46+
# Create the Go byte array line with commented assembly
47+
result.append(f"{formatted_bytes}{padding}// {asm_instr}")
48+
else:
49+
# If the line doesn't match our expected format, keep it as a comment
50+
result.append(f"// {line.strip()}")
51+
52+
return result
53+
54+
def main():
55+
# Read from stdin
56+
lines = sys.stdin.readlines()
57+
58+
# Transform the lines
59+
transformed_lines = transform_disassembly(lines)
60+
61+
# Write to stdout
62+
sys.stdout.write("\n".join(transformed_lines))
63+
sys.stdout.write("\n") # Add a final newline
64+
65+
if __name__ == "__main__":
66+
main()

0 commit comments

Comments
 (0)