Skip to content

Commit d0b9f63

Browse files
dyninst/decode: decoding of template segments (#43007)
### What does this PR do? This PR implements the decoding of template segments. It tries to respect the size limits of the relevant RFC and to produce an output format that will look broadly familiar to a Go developer while still being reasonable. ### Describe how you validated your changes There's some testing. ### Additional Notes Part of [DEBUG-4034](https://datadoghq.atlassian.net/browse/DEBUG-4034) [DEBUG-4034]: https://datadoghq.atlassian.net/browse/DEBUG-4034?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ Co-authored-by: grantseltzer <[email protected]> Co-authored-by: andrew.werner <[email protected]>
1 parent 8f92705 commit d0b9f63

File tree

197 files changed

+4634
-2153
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

197 files changed

+4634
-2153
lines changed

pkg/dyninst/compiler/testdata/snapshot/simple.arch=amd64,toolchain=go1.23.11.sm.txt

Lines changed: 285 additions & 256 deletions
Large diffs are not rendered by default.

pkg/dyninst/compiler/testdata/snapshot/simple.arch=amd64,toolchain=go1.24.3.sm.txt

Lines changed: 307 additions & 278 deletions
Large diffs are not rendered by default.

pkg/dyninst/compiler/testdata/snapshot/simple.arch=amd64,toolchain=go1.25.0.sm.txt

Lines changed: 315 additions & 286 deletions
Large diffs are not rendered by default.

pkg/dyninst/compiler/testdata/snapshot/simple.arch=arm64,toolchain=go1.23.11.sm.txt

Lines changed: 285 additions & 256 deletions
Large diffs are not rendered by default.

pkg/dyninst/compiler/testdata/snapshot/simple.arch=arm64,toolchain=go1.24.3.sm.txt

Lines changed: 307 additions & 278 deletions
Large diffs are not rendered by default.

pkg/dyninst/compiler/testdata/snapshot/simple.arch=arm64,toolchain=go1.25.0.sm.txt

Lines changed: 315 additions & 286 deletions
Large diffs are not rendered by default.

pkg/dyninst/decode/decoder.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ type Decoder struct {
7979

8080
// These fields are initialized and reset for each message.
8181
snapshotMessage snapshotMessage
82-
entry captureEvent
82+
entryOrLine captureEvent
8383
_return captureEvent
8484
line lineCaptureData
8585
}
@@ -128,7 +128,7 @@ func NewDecoder(
128128
decoder.typesByGoRuntimeType[goRuntimeType] = id
129129
}
130130
}
131-
decoder.entry.encodingContext = encodingContext{
131+
decoder.entryOrLine.encodingContext = encodingContext{
132132
typesByID: decoder.decoderTypes,
133133
typesByGoRuntimeType: decoder.typesByGoRuntimeType,
134134
typeResolver: typeNameResolver,
@@ -203,8 +203,8 @@ func (d *Decoder) Decode(
203203
}
204204

205205
func (d *Decoder) resetForNextMessage() {
206-
clear(d.entry.dataItems)
207-
d.entry.clear()
206+
clear(d.entryOrLine.dataItems)
207+
d.entryOrLine.clear()
208208
d.line.clear()
209209
d._return.clear()
210210
d.snapshotMessage = snapshotMessage{}
@@ -226,6 +226,7 @@ type snapshotMessage struct {
226226
Debugger debuggerData `json:"debugger"`
227227
Timestamp int `json:"timestamp"`
228228
Duration uint64 `json:"duration,omitzero"`
229+
Message messageData `json:"message,omitempty"`
229230
}
230231

231232
func (s *snapshotMessage) init(
@@ -244,23 +245,23 @@ func (s *snapshotMessage) init(
244245
if event.EntryOrLine == nil {
245246
return nil, fmt.Errorf("entry event is nil")
246247
}
247-
if err := decoder.entry.init(
248+
if err := decoder.entryOrLine.init(
248249
event.EntryOrLine, decoder.program.Types, &s.Debugger.EvaluationErrors,
249250
); err != nil {
250251
return nil, err
251252
}
252-
probeEvent := decoder.probeEvents[decoder.entry.rootType.ID]
253+
probeEvent := decoder.probeEvents[decoder.entryOrLine.rootType.ID]
253254
probe := probeEvent.probe
254255
header, err := event.EntryOrLine.Header()
255256
if err != nil {
256257
return probe, fmt.Errorf("error getting header %w", err)
257258
}
258259
switch probeEvent.event.Kind {
259260
case ir.EventKindEntry:
260-
s.Debugger.Snapshot.Captures.Entry = &decoder.entry
261+
s.Debugger.Snapshot.Captures.Entry = &decoder.entryOrLine
261262
case ir.EventKindLine:
262263
decoder.line.sourceLine = probeEvent.event.SourceLine
263-
decoder.line.capture = &decoder.entry
264+
decoder.line.capture = &decoder.entryOrLine
264265
s.Debugger.Snapshot.Captures.Lines = &decoder.line
265266
}
266267
var returnHeader *output.EventHeader
@@ -389,5 +390,14 @@ func (s *snapshotMessage) init(
389390
s.Logger.ThreadID = int(header.Goid)
390391
s.Debugger.Snapshot.Probe.ID = probe.GetID()
391392
s.Debugger.Snapshot.Stack.frames = stackFrames
393+
394+
if probe.Template != nil {
395+
s.Message = messageData{
396+
entryOrLine: &decoder.entryOrLine,
397+
_return: &decoder._return,
398+
template: probe.Template,
399+
}
400+
}
401+
392402
return probe, nil
393403
}

pkg/dyninst/decode/decoder_test.go

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,9 @@ func FuzzDecoder(f *testing.F) {
4444
EntryOrLine: output.Event(item),
4545
ServiceName: "foo",
4646
}, &noopSymbolicator{}, []byte{})
47-
require.Empty(t, decoder.entry.dataItems)
48-
require.Empty(t, decoder.entry.currentlyEncoding)
47+
require.Empty(t, decoder.entryOrLine.dataItems)
48+
require.Empty(t, decoder.entryOrLine.currentlyEncoding)
49+
require.Empty(t, decoder._return.dataItems)
4950
})
5051
}
5152

@@ -58,7 +59,10 @@ type (
5859
Message string `json:"message"`
5960
} `json:"evaluationErrors,omitempty"`
6061
}
61-
eventCaptures struct{ Debugger debugger }
62+
eventCaptures struct {
63+
Debugger debugger
64+
Message string `json:"message,omitempty"`
65+
}
6266
)
6367

6468
// TestDecoderManually is a test that manually constructs an event and decodes
@@ -81,11 +85,14 @@ func TestDecoderManually(t *testing.T) {
8185
var e eventCaptures
8286
require.NoError(t, json.Unmarshal(buf, &e))
8387
require.Equal(t, c.expected, e.Debugger.Snapshot.Captures.Entry.Arguments)
84-
require.Empty(t, decoder.entry.dataItems)
85-
require.Empty(t, decoder.entry.currentlyEncoding)
86-
require.Nil(t, decoder.entry.rootType)
87-
require.Nil(t, decoder.entry.rootData)
88-
require.Zero(t, decoder.entry.evaluationErrors)
88+
if c.expectedMessage != "" {
89+
require.Equal(t, c.expectedMessage, e.Message)
90+
}
91+
require.Empty(t, decoder.entryOrLine.dataItems)
92+
require.Empty(t, decoder.entryOrLine.currentlyEncoding)
93+
require.Nil(t, decoder.entryOrLine.rootType)
94+
require.Nil(t, decoder.entryOrLine.rootData)
95+
require.Zero(t, decoder.entryOrLine.evaluationErrors)
8996
require.Zero(t, decoder.snapshotMessage)
9097
})
9198
}
@@ -115,28 +122,33 @@ type testCase struct {
115122
probeName string
116123
eventConstructor func(testing.TB, *ir.Program) []byte
117124
expected any
125+
expectedMessage string
118126
}
119127

120128
var cases = []testCase{
121129
{
122130
probeName: "stringArg",
123131
eventConstructor: simpleStringArgEvent,
124132
expected: simpleStringArgExpected,
133+
expectedMessage: "s: abcdefghijklmnop",
125134
},
126135
{
127136
probeName: "mapArg",
128137
eventConstructor: simpleMapArgEvent,
129138
expected: simpleMapArgExpected,
139+
expectedMessage: "m: map[a: 1]",
130140
},
131141
{
132142
probeName: "bigMapArg",
133143
eventConstructor: simpleBigMapArgEvent,
134144
expected: simpleBigMapArgExpected,
145+
expectedMessage: "m: map[b: {Field1: 1, Field2: 0, Field3: 0, Field4: 0, Field5: 0, ...}}]",
135146
},
136147
{
137148
probeName: "PointerChainArg",
138149
eventConstructor: simplePointerChainArgEvent,
139150
expected: simplePointerChainArgExpected,
151+
expectedMessage: "ptr: 17",
140152
},
141153
}
142154

@@ -181,14 +193,14 @@ func simpleStringArgEvent(t testing.TB, irProg *ir.Program) []byte {
181193
}
182194
require.NotNil(t, stringType)
183195
require.NotNil(t, eventType)
184-
require.Equal(t, uint32(17), eventType.GetByteSize())
196+
require.Equal(t, uint32(33), eventType.GetByteSize())
185197

186198
var item []byte
187199
eventHeader := output.EventHeader{
188200
Data_byte_len: uint32(
189201
unsafe.Sizeof(output.EventHeader{}) +
190202
1 /* bitset */ + unsafe.Sizeof(output.DataItemHeader{}) +
191-
16 + 7 /* padding */ +
203+
32 + 7 /* padding */ +
192204
unsafe.Sizeof(output.DataItemHeader{}) +
193205
16,
194206
),
@@ -199,7 +211,7 @@ func simpleStringArgEvent(t testing.TB, irProg *ir.Program) []byte {
199211
}
200212
dataItem0 := output.DataItemHeader{
201213
Type: uint32(eventType.GetID()),
202-
Length: 17,
214+
Length: 33,
203215
Address: 0,
204216
}
205217
dataItem1 := output.DataItemHeader{
@@ -213,7 +225,11 @@ func simpleStringArgEvent(t testing.TB, irProg *ir.Program) []byte {
213225
item = append(item, unsafe.Slice(
214226
(*byte)(unsafe.Pointer(&dataItem0)), unsafe.Sizeof(dataItem0))...,
215227
)
216-
item = append(item, 1) // bitset
228+
item = append(item, 3) // bitset: bit 0 (argument) and bit 1 (template_segment) set
229+
// First expression (argument) at offset 1
230+
item = binary.NativeEndian.AppendUint64(item, 0xdeadbeef)
231+
item = binary.NativeEndian.AppendUint64(item, 16)
232+
// Second expression (template_segment) at offset 17
217233
item = binary.NativeEndian.AppendUint64(item, 0xdeadbeef)
218234
item = binary.NativeEndian.AppendUint64(item, 16)
219235
item = append(item, 0, 0, 0, 0, 0, 0, 0) // padding
@@ -254,8 +270,8 @@ func simpleMapArgEvent(t testing.TB, irProg *ir.Program) []byte {
254270
)
255271

256272
require.NotNil(t, eventType)
257-
// Expect exactly one expression for parameter 'm'
258-
require.NotEmpty(t, eventType.Expressions)
273+
// Expect two expressions: argument and template_segment
274+
require.GreaterOrEqual(t, len(eventType.Expressions), 2)
259275
paramType := eventType.Expressions[0].Expression.Type
260276
var ok bool
261277
mapParamType, ok = paramType.(*ir.GoMapType)
@@ -299,14 +315,18 @@ func simpleMapArgEvent(t testing.TB, irProg *ir.Program) []byte {
299315
strAddr = uint64(0x300000003)
300316
)
301317

302-
// Build root data item (presence bitset + pointer to header)
318+
// Build root data item (presence bitset + pointers to header)
303319
rootData := make([]byte, rootLen)
304-
// Set presence bit for first expression
320+
// Set presence bits for both expressions (bit 0 for argument, bit 1 for template_segment)
305321
if eventType.PresenceBitsetSize > 0 {
306-
rootData[0] = 1
322+
rootData[0] = 3 // bits 0 and 1 set
307323
}
324+
// First expression (argument) at offset 1
308325
ptrOff := int(eventType.Expressions[0].Offset)
309326
binary.NativeEndian.PutUint64(rootData[ptrOff:ptrOff+8], headerAddr)
327+
// Second expression (template_segment) at offset 9
328+
templatePtrOff := int(eventType.Expressions[1].Offset)
329+
binary.NativeEndian.PutUint64(rootData[templatePtrOff:templatePtrOff+8], headerAddr)
310330

311331
// Build header bytes
312332
headerData := make([]byte, headerLen)
@@ -503,11 +523,16 @@ func simpleBigMapArgEvent(t testing.TB, irProg *ir.Program) []byte {
503523
)
504524

505525
rootData := make([]byte, rootLen)
526+
// Set presence bits for both expressions (bit 0 for argument, bit 1 for template_segment)
506527
if eventType.PresenceBitsetSize > 0 {
507-
rootData[0] = 1
528+
rootData[0] = 3 // bits 0 and 1 set
508529
}
530+
// First expression (argument) at offset 1
509531
ptrOff := int(eventType.Expressions[0].Offset)
510532
binary.NativeEndian.PutUint64(rootData[ptrOff:ptrOff+8], headerAddr)
533+
// Second expression (template_segment) at offset 9
534+
templatePtrOff := int(eventType.Expressions[1].Offset)
535+
binary.NativeEndian.PutUint64(rootData[templatePtrOff:templatePtrOff+8], headerAddr)
511536

512537
headerData := make([]byte, headerLen)
513538
binary.NativeEndian.PutUint64(headerData[countOff:countOff+8], 1)
@@ -620,8 +645,9 @@ func simplePointerChainArgEvent(t testing.TB, irProg *ir.Program) []byte {
620645
eventType := events[0].Type
621646
rootLen := int(eventType.GetByteSize())
622647
rootData := make([]byte, rootLen)
648+
// Set presence bits for both expressions (bit 0 for argument, bit 1 for template_segment)
623649
if eventType.PresenceBitsetSize > 0 {
624-
rootData[0] = 1
650+
rootData[0] = 3 // bits 0 and 1 set
625651
}
626652
// Build a fully captured pointer chain *****int → int(17)
627653
argType := eventType.Expressions[0].Expression.Type
@@ -645,9 +671,12 @@ func simplePointerChainArgEvent(t testing.TB, irProg *ir.Program) []byte {
645671
addr4 = uint64(0xa0000004)
646672
addr5 = uint64(0xa0000005)
647673
)
648-
// Root data contains address of first pointer
674+
// First expression (argument) at offset 1: address of first pointer
649675
off := int(eventType.Expressions[0].Offset)
650676
binary.NativeEndian.PutUint64(rootData[off:off+8], addr1)
677+
// Second expression (template_segment) at offset 9: same address
678+
templateOff := int(eventType.Expressions[1].Offset)
679+
binary.NativeEndian.PutUint64(rootData[templateOff:templateOff+8], addr1)
651680

652681
// Helper to build a pointer data item (8-byte address payload)
653682
makePtrItem := func(tid ir.TypeID, addr uint64, pointsTo uint64) (hdr output.DataItemHeader, data []byte) {

0 commit comments

Comments
 (0)