Skip to content

Commit 409a7f5

Browse files
committed
Allow passing options to Go symbolizer constructor
Signed-off-by: Ivan Babrou <github@ivan.computer>
1 parent 7b3ccd3 commit 409a7f5

File tree

4 files changed

+239
-20
lines changed

4 files changed

+239
-20
lines changed

go/.golangci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
version: "2"
2+
linters:
3+
exclusions:
4+
presets:
5+
- std-error-handling

go/symbolizer.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,15 @@ type Symbolizer struct {
2222

2323
// NewSymbolizer creates an instance of a symbolizer.
2424
// See: https://docs.rs/blazesym-c/latest/blazesym_c/fn.blaze_symbolizer_new.html
25-
func NewSymbolizer() (*Symbolizer, error) {
26-
s := C.blaze_symbolizer_new()
25+
func NewSymbolizer(options ...SymbolizerOption) (*Symbolizer, error) {
26+
so := newSymbolizerOptions()
27+
defer so.Close()
28+
29+
for _, option := range options {
30+
option(so)
31+
}
32+
33+
s := C.blaze_symbolizer_new_opts(&so.opts)
2734
if s == nil {
2835
return nil, blazeErr(C.blaze_err_last()).Error()
2936
}

go/symbolizer_option.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package blazesym
2+
3+
/*
4+
#cgo LDFLAGS: -lblazesym_c
5+
#include "blazesym.h"
6+
*/
7+
import "C"
8+
9+
import (
10+
"unsafe"
11+
)
12+
13+
// SymbolizerOptions is options for configuring Symbolizer objects.
14+
// See: https://docs.rs/blazesym-c/latest/blazesym_c/struct.blaze_symbolizer_opts.html
15+
type SymbolizerOptions struct {
16+
opts C.blaze_symbolizer_opts
17+
}
18+
19+
func newSymbolizerOptions() *SymbolizerOptions {
20+
opts := C.blaze_symbolizer_opts{}
21+
opts.type_size = C.ulong(unsafe.Sizeof(opts))
22+
opts.auto_reload = C.bool(true)
23+
opts.code_info = C.bool(true)
24+
opts.inlined_fns = C.bool(true)
25+
opts.demangle = C.bool(true)
26+
27+
return &SymbolizerOptions{opts: opts}
28+
}
29+
30+
// Close frees resources associated with SymbolizerOptions.
31+
func (so *SymbolizerOptions) Close() {
32+
freeCArrayOfStrings(so.opts.debug_dirs, so.opts.debug_dirs_len)
33+
}
34+
35+
// SymbolizerOption configures SymbolizerOptions objects.
36+
type SymbolizerOption func(*SymbolizerOptions)
37+
38+
// WithDebugDirs sets the array of debug directories to search for split debug information.
39+
// See: https://docs.rs/blazesym-c/latest/blazesym_c/struct.blaze_symbolizer_opts.html#structfield.debug_dirs
40+
func WithDebugDirs(dirs []string) SymbolizerOption {
41+
return func(so *SymbolizerOptions) {
42+
if so.opts.debug_dirs_len != 0 {
43+
freeCArrayOfStrings(so.opts.debug_dirs, so.opts.debug_dirs_len)
44+
}
45+
46+
so.opts.debug_dirs = makeCArrayOfStrings(dirs)
47+
so.opts.debug_dirs_len = C.size_t(len(dirs))
48+
}
49+
}
50+
51+
// WithAutoReload sets whether or not to automatically reload file system based
52+
// symbolization sources that were updated since the last symbolization operation.
53+
// See: https://docs.rs/blazesym-c/latest/blazesym_c/struct.blaze_symbolizer_opts.html#structfield.auto_reload
54+
func WithAutoReload(enabled bool) SymbolizerOption {
55+
return func(so *SymbolizerOptions) {
56+
so.opts.auto_reload = C.bool(enabled)
57+
}
58+
}
59+
60+
// WithCodeInfo sets whether to attempt to gather source code location information.
61+
// See: https://docs.rs/blazesym-c/latest/blazesym_c/struct.blaze_symbolizer_opts.html#structfield.code_info
62+
func WithCodeInfo(enabled bool) SymbolizerOption {
63+
return func(so *SymbolizerOptions) {
64+
so.opts.code_info = C.bool(enabled)
65+
}
66+
}
67+
68+
// WithInlinedFns sets whether to report inlined functions as part of symbolization.
69+
// See: https://docs.rs/blazesym-c/latest/blazesym_c/struct.blaze_symbolizer_opts.html#structfield.inlined_fns
70+
func WithInlinedFns(enabled bool) SymbolizerOption {
71+
return func(so *SymbolizerOptions) {
72+
so.opts.inlined_fns = C.bool(enabled)
73+
}
74+
}
75+
76+
// WithDemangle sets whether or not to transparently demangle symbols.
77+
// See: https://docs.rs/blazesym-c/latest/blazesym_c/struct.blaze_symbolizer_opts.html#structfield.demangle
78+
func WithDemangle(enabled bool) SymbolizerOption {
79+
return func(so *SymbolizerOptions) {
80+
so.opts.demangle = C.bool(enabled)
81+
}
82+
}
83+
84+
func makeCArrayOfStrings(input []string) **C.char {
85+
arr := C.malloc(C.size_t(len(input)) * C.size_t(unsafe.Sizeof(uintptr(0))))
86+
87+
pointers := unsafe.Slice((**C.char)(arr), len(input))
88+
89+
for i, s := range input {
90+
pointers[i] = C.CString(s)
91+
}
92+
93+
return (**C.char)(arr)
94+
}
95+
96+
func freeCArrayOfStrings(input **C.char, length C.size_t) {
97+
if length == 0 {
98+
return
99+
}
100+
101+
pointers := unsafe.Slice(input, length)
102+
103+
for i := range length {
104+
C.free(unsafe.Pointer(pointers[i]))
105+
}
106+
107+
C.free(unsafe.Pointer(input))
108+
}

go/symbolizer_test.go

Lines changed: 117 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package blazesym_test
22

33
import (
44
"debug/elf"
5+
"io"
56
"os"
67
"path/filepath"
78
"reflect"
@@ -97,16 +98,20 @@ func testElfDwarfSource(t *testing.T, source *blazesym.ElfSource, hasCodeInfo bo
9798
}
9899

99100
if hasCodeInfo {
100-
if syms[0].CodeInfo.Dir == "" {
101-
t.Errorf("expected non-empty dir, got %q", syms[0].CodeInfo.Dir)
102-
}
101+
if syms[0].CodeInfo == nil {
102+
t.Error("expected code info to be present")
103+
} else {
104+
if syms[0].CodeInfo.Dir == "" {
105+
t.Errorf("expected non-empty dir, got %q", syms[0].CodeInfo.Dir)
106+
}
103107

104-
if syms[0].CodeInfo.File != "test-stable-addrs.c" {
105-
t.Errorf("expected file to be test-stable-addrs.c, got %q", syms[0].CodeInfo.File)
106-
}
108+
if syms[0].CodeInfo.File != "test-stable-addrs.c" {
109+
t.Errorf("expected file to be test-stable-addrs.c, got %q", syms[0].CodeInfo.File)
110+
}
107111

108-
if syms[0].CodeInfo.Line != 10 {
109-
t.Errorf("expected line to be 10, got %d", syms[0].CodeInfo.Line)
112+
if syms[0].CodeInfo.Line != 10 {
113+
t.Errorf("expected line to be 10, got %d", syms[0].CodeInfo.Line)
114+
}
110115
}
111116
} else {
112117
if syms[0].CodeInfo != nil {
@@ -146,16 +151,20 @@ func testElfDwarfSource(t *testing.T, source *blazesym.ElfSource, hasCodeInfo bo
146151
}
147152

148153
if hasCodeInfo {
149-
if syms[i].CodeInfo.Dir == "" {
150-
t.Errorf("expected non-empty dir, got %q", syms[i].CodeInfo.Dir)
151-
}
152-
153-
if syms[i].CodeInfo.File != "test-stable-addrs.c" {
154-
t.Errorf("expected file to be test-stable-addrs.c, got %q", syms[i].CodeInfo.File)
155-
}
156-
157-
if syms[i].CodeInfo.Line == 0 {
158-
t.Error("expected line to non-zero")
154+
if syms[i].CodeInfo == nil {
155+
t.Error("expected code info to be present")
156+
} else {
157+
if syms[i].CodeInfo.Dir == "" {
158+
t.Errorf("expected non-empty dir, got %q", syms[i].CodeInfo.Dir)
159+
}
160+
161+
if syms[i].CodeInfo.File != "test-stable-addrs.c" {
162+
t.Errorf("expected file to be test-stable-addrs.c, got %q", syms[i].CodeInfo.File)
163+
}
164+
165+
if syms[i].CodeInfo.Line == 0 {
166+
t.Error("expected line to non-zero")
167+
}
159168
}
160169
} else {
161170
if syms[i].CodeInfo != nil {
@@ -186,3 +195,93 @@ func TestSymbolizeElfDwarf(t *testing.T) {
186195
})
187196
}
188197
}
198+
199+
func copy(dst, src string) error {
200+
s, err := os.Open(src)
201+
if err != nil {
202+
return err
203+
}
204+
205+
defer s.Close()
206+
207+
d, err := os.Create(dst)
208+
if err != nil {
209+
return err
210+
}
211+
212+
defer d.Close()
213+
214+
_, err = io.Copy(d, s)
215+
if err != nil {
216+
return err
217+
}
218+
219+
return nil
220+
}
221+
222+
func TestConfigurableDebugDirs(t *testing.T) {
223+
tmp, err := os.MkdirTemp(os.TempDir(), "*")
224+
if err != nil {
225+
t.Fatal(err)
226+
}
227+
228+
dst := filepath.Join(tmp, "test-stable-addrs-stripped-with-link.bin")
229+
230+
err = copy(dst, filepath.Join("../data", "test-stable-addrs-stripped-with-link.bin"))
231+
if err != nil {
232+
t.Fatal(err)
233+
}
234+
235+
symbolizer, err := blazesym.NewSymbolizer(blazesym.WithDebugDirs([]string{}))
236+
if err != nil {
237+
t.Fatal(err)
238+
}
239+
240+
syms, err := symbolizer.SymbolizeElfVirtOffsets(&blazesym.ElfSource{Path: dst, DebugSyms: true}, []uint64{0x2000200})
241+
if err != nil {
242+
t.Fatal(err)
243+
}
244+
245+
if syms[0].Name != "" {
246+
t.Errorf("expected symbol to be not resolved, got %q", syms[0].Name)
247+
}
248+
249+
if syms[0].Reason != blazesym.SymbolizeReasonMissingSyms {
250+
t.Errorf("expected reason to be SymbolizeReasonMissingSyms, got %v", syms[0].Reason)
251+
}
252+
253+
debugDir1, err := os.MkdirTemp(os.TempDir(), "*")
254+
if err != nil {
255+
t.Fatal(err)
256+
}
257+
258+
debugDir2, err := os.MkdirTemp(os.TempDir(), "*")
259+
if err != nil {
260+
t.Fatal(err)
261+
}
262+
263+
debugDst := filepath.Join(debugDir2, "test-stable-addrs-dwarf-only.dbg")
264+
265+
err = copy(debugDst, filepath.Join("../data", "test-stable-addrs-dwarf-only.dbg"))
266+
if err != nil {
267+
t.Fatal(err)
268+
}
269+
270+
symbolizer, err = blazesym.NewSymbolizer(blazesym.WithDebugDirs([]string{debugDir1, debugDir2}))
271+
if err != nil {
272+
t.Fatal(err)
273+
}
274+
275+
syms, err = symbolizer.SymbolizeElfVirtOffsets(&blazesym.ElfSource{Path: dst, DebugSyms: true}, []uint64{0x2000200})
276+
if err != nil {
277+
t.Fatal(err)
278+
}
279+
280+
if syms[0].Name != "factorial" {
281+
t.Errorf("expected symbol to resolve to factorial, got %q", syms[0].Name)
282+
}
283+
284+
if syms[0].Module != dst {
285+
t.Errorf("expected module to be %q, got %q", dst, syms[0].Module)
286+
}
287+
}

0 commit comments

Comments
 (0)