Skip to content

Commit b593d65

Browse files
authored
Update Node custom labels support to use hashmap-based labels. (#91)
The previous approach, which involved resetting the current labelset every time a piece of javascript code ran, was too slow. So we switched to a new approach, where we _only_ run our own code when a new async context is created or destroyed, or when label sets change. Since we are no longer setting the current labelset, we instead store labelsets in a hash map from async ID -> labelset (this is implemented in the custom_labels library). Thus, at profiling time, we need to get the current async ID from the node.js environment, then look it up in the hashmap. This PR accomplishes the profiling side of that change.
1 parent b00d794 commit b593d65

28 files changed

Lines changed: 1607 additions & 65 deletions

customlabelstest/customlabels_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func TestNativeCustomLabels(t *testing.T) {
2222
ctx, cancel := context.WithCancel(context.Background())
2323
defer cancel()
2424

25-
traceCh, _ := testutils.StartTracer(ctx, t, enabledTracers, r)
25+
traceCh, _ := testutils.StartTracer(ctx, t, enabledTracers, r, false)
2626
// TODO - change this to `cargo build --release --bin custom-labels-example`
2727
// once we have the Rust workspace from upstream.
2828
cmd := exec.Command("cargo", "build", "--release",

interpreter/customlabels/customlabels.go

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,21 @@ import (
1616
)
1717

1818
const (
19-
abiVersionExport = "custom_labels_abi_version"
20-
tlsExport = "custom_labels_current_set"
19+
abiVersionExport = "custom_labels_abi_version"
20+
currentSetTlsExport = "custom_labels_current_set"
21+
currentHmTlsExport = "custom_labels_async_hashmap"
2122
)
2223

23-
var dsoRegex = regexp.MustCompile(`.*/libcustomlabels.*\.so|.*/customlabels\.node`)
24+
var dsoRegex = regexp.MustCompile(`.*/libcustomlabels.*\.so`)
25+
var nodeRegex = regexp.MustCompile(`.*/customlabels\.node`)
2426

2527
type data struct {
26-
abiVersionElfVA libpf.Address
27-
tlsAddr libpf.Address
28+
abiVersionElfVA libpf.Address
29+
currentSetTlsAddr libpf.Address
30+
31+
hasCurrentHm bool
32+
currentHmTlsAddr libpf.Address
33+
2834
isSharedLibrary bool
2935
}
3036

@@ -35,7 +41,6 @@ func Loader(_ interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interprete
3541
if err != nil {
3642
return nil, err
3743
}
38-
3944
abiVersionSym, err := ef.LookupSymbol(abiVersionExport)
4045
if err != nil {
4146
if errors.Is(err, pfelf.ErrSymbolNotFound) {
@@ -53,31 +58,41 @@ func Loader(_ interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interprete
5358
// global-dynamic TLS model and have to look up the TLS descriptor.
5459
// Otherwise, assume we're the main binary and just look up the
5560
// symbol.
56-
isSharedLibrary := dsoRegex.MatchString(info.FileName())
57-
var tlsAddr libpf.Address
61+
fn := info.FileName()
62+
isNativeSharedLibrary := dsoRegex.MatchString(fn)
63+
isNodeExtension := (!isNativeSharedLibrary) && nodeRegex.MatchString(fn)
64+
isSharedLibrary := isNativeSharedLibrary || isNodeExtension
65+
66+
var currentSetTlsAddr, currentHmTlsAddr libpf.Address
67+
var hasCurrentHm bool
5868
if isSharedLibrary {
5969
// Resolve thread info TLS export.
6070
tlsDescs, err := ef.TLSDescriptors()
6171
if err != nil {
6272
return nil, errors.New("failed to extract TLS descriptors")
6373
}
6474
var ok bool
65-
tlsAddr, ok = tlsDescs[tlsExport]
75+
currentSetTlsAddr, ok = tlsDescs[currentSetTlsExport]
6676
if !ok {
6777
return nil, errors.New("failed to locate TLS descriptor for custom labels")
6878
}
79+
if isNodeExtension {
80+
currentHmTlsAddr, hasCurrentHm = tlsDescs[currentHmTlsExport]
81+
}
6982
} else {
70-
offset, err := ef.LookupTLSSymbolOffset(tlsExport)
83+
offset, err := ef.LookupTLSSymbolOffset(currentSetTlsExport)
7184
if err != nil {
7285
return nil, fmt.Errorf("failed to get tls symbol offset: %w", err)
7386
}
74-
tlsAddr = libpf.Address(offset)
87+
currentSetTlsAddr = libpf.Address(offset)
7588
}
7689

7790
d := data{
78-
abiVersionElfVA: libpf.Address(abiVersionSym.Address),
79-
tlsAddr: tlsAddr,
80-
isSharedLibrary: isSharedLibrary,
91+
abiVersionElfVA: libpf.Address(abiVersionSym.Address),
92+
currentSetTlsAddr: currentSetTlsAddr,
93+
hasCurrentHm: hasCurrentHm,
94+
currentHmTlsAddr: currentHmTlsAddr,
95+
isSharedLibrary: isSharedLibrary,
8196
}
8297
return &d, nil
8398
}
@@ -98,16 +113,25 @@ func (d data) Attach(ebpf interpreter.EbpfHandler, pid libpf.PID,
98113
" (only 1 is supported)", abiVersion)
99114
}
100115

101-
var tlsOffset uint64
116+
var currentSetTlsOffset uint64
102117
if d.isSharedLibrary {
103118
// Read TLS offset from the TLS descriptor
104-
tlsOffset = rm.Uint64(bias + d.tlsAddr + 8)
119+
currentSetTlsOffset = rm.Uint64(bias + d.currentSetTlsAddr + 8)
105120
} else {
106121
// We're in the main executable: TLS offset is known statically.
107-
tlsOffset = uint64(d.tlsAddr)
122+
currentSetTlsOffset = uint64(d.currentSetTlsAddr)
123+
}
124+
125+
var currentHmTlsOffset uint64
126+
if d.hasCurrentHm {
127+
currentHmTlsOffset = rm.Uint64(bias + d.currentHmTlsAddr + 8)
108128
}
109129

110-
procInfo := C.NativeCustomLabelsProcInfo{tls_offset: C.u64(tlsOffset)}
130+
procInfo := C.NativeCustomLabelsProcInfo{
131+
current_set_tls_offset: C.u64(currentSetTlsOffset),
132+
has_current_hm: C.bool(d.hasCurrentHm),
133+
current_hm_tls_offset: C.u64(currentHmTlsOffset),
134+
}
111135
if err := ebpf.UpdateProcData(libpf.CustomLabels, pid, unsafe.Pointer(&procInfo)); err != nil {
112136
return nil, err
113137
}

0 commit comments

Comments
 (0)