Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libpf/apm.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ type APMTraceID [16]byte
type APMTransactionID = APMSpanID

var InvalidAPMSpanID = APMSpanID{0, 0, 0, 0, 0, 0, 0, 0}
var InvalidAPMTraceID = APMTraceID{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
20 changes: 19 additions & 1 deletion reporter/internal/pdata/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,15 @@ func (p *Pdata) Generate(tree samples.TraceEventsTree,
mappingSet := make(orderedset.OrderedSet[libpf.FrameMapping], 64)
stackSet := make(orderedset.OrderedSet[stackInfo], 64)
locationSet := make(orderedset.OrderedSet[locationInfo], 64)
linkSet := make(orderedset.OrderedSet[linkInfo], 64)

// By specification, the first element should be empty.
stringSet.Add("")
funcSet.Add(funcInfo{})
mappingSet.Add(libpf.FrameMapping{})
stackSet.Add(stackInfo{})
locationSet.Add(locationInfo{})
linkSet.Add(linkInfo{})

dic.LinkTable().AppendEmpty()
dic.MappingTable().AppendEmpty()
Expand Down Expand Up @@ -103,7 +105,7 @@ func (p *Pdata) Generate(tree samples.TraceEventsTree,

prof := sp.Profiles().AppendEmpty()
if err := p.setProfile(dic, attrMgr,
stringSet, funcSet, mappingSet, stackSet, locationSet,
stringSet, funcSet, mappingSet, stackSet, locationSet, linkSet,
origin, toEvents.Events[origin], prof,
collectionStartTime, collectionEndTime); err != nil {
return profiles, err
Expand Down Expand Up @@ -143,6 +145,7 @@ func (p *Pdata) setProfile(
mappingSet orderedset.OrderedSet[libpf.FrameMapping],
stackSet orderedset.OrderedSet[stackInfo],
locationSet orderedset.OrderedSet[locationInfo],
linkSet orderedset.OrderedSet[linkInfo],
origin libpf.Origin,
events samples.SampleToEvents,
profile pprofile.Profile,
Expand Down Expand Up @@ -177,6 +180,21 @@ func (p *Pdata) setProfile(
sample.Values().Append(traceInfo.OffTimes...)
}

if sampleKey.SpanID != libpf.InvalidAPMSpanID &&
sampleKey.TraceID != libpf.InvalidAPMTraceID {
link, ok := linkSet.AddWithCheck(linkInfo{
traceID: sampleKey.TraceID,
spanID: sampleKey.SpanID,
})
if !ok {
l := dic.LinkTable().AppendEmpty()
l.SetSpanID(pcommon.SpanID(sampleKey.SpanID))
l.SetTraceID(pcommon.TraceID(sampleKey.TraceID))

}
sample.SetLinkIndex(link)
}

locationIndices := make([]int32, 0, len(traceInfo.Frames))
// Walk every frame of the trace.
for _, uniqueFrame := range traceInfo.Frames {
Expand Down
64 changes: 62 additions & 2 deletions reporter/internal/pdata/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,15 @@ func TestGenerate_NativeFrame(t *testing.T) {
}
events := map[libpf.Origin]samples.SampleToEvents{
support.TraceOriginSampling: {
{}: &samples.TraceEvents{
{
Hash: libpf.NewTraceHash(0, 1),
Comm: libpf.Intern("abc"),
TID: 42,
CPU: 73,
SpanID: libpf.APMSpanID{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7},
TraceID: libpf.APMTraceID{0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27},
}: &samples.TraceEvents{
Frames: singleFrameNative(mappingFile, 0x1000, 0x1000, 0x2000, 0x100),
Timestamps: []uint64{
uint64(time.Unix(1010, 0).UnixNano()),
Expand Down Expand Up @@ -668,6 +676,50 @@ func TestGenerate_NativeFrame(t *testing.T) {
// since it's resolved by the backend. The function table should be empty.
assert.Equal(t, 1, dic.FunctionTable().Len(),
"Function table should be empty for native frames")

// Verify SpanID and TraceID are set via Link
linkIndex := sample.LinkIndex()
assert.Greater(t, linkIndex, int32(0), "Sample should have a link set (index > 0, since 0 is dummy)")
link := dic.LinkTable().At(int(linkIndex))
expectedSpanID := pcommon.SpanID{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}
expectedTraceID := pcommon.TraceID{0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27}
assert.Equal(t, expectedSpanID, link.SpanID())
assert.Equal(t, expectedTraceID, link.TraceID())

// Verify Comm, TID, and CPU are set in sample attributes
attributeIndices := sample.AttributeIndices().AsRaw()
assert.NotEmpty(t, attributeIndices, "Sample should have attributes")

attributeTable := dic.AttributeTable()
stringTable := dic.StringTable()

foundComm := false
foundTID := false
foundCPU := false

for _, attrIdx := range attributeIndices {
attr := attributeTable.At(int(attrIdx))
keyStrIdx := attr.KeyStrindex()
key := stringTable.At(int(keyStrIdx))

switch key {
case string(semconv.ThreadNameKey):
assert.Equal(t, "abc", attr.Value().Str())
foundComm = true
case string(semconv.ThreadIDKey):
assert.Equal(t, int64(42), attr.Value().Int())
foundTID = true
case string(semconv.CPULogicalNumberKey):
assert.Equal(t, int64(73), attr.Value().Int())
foundCPU = true
}
}

assert.True(t, foundComm, "Sample should have Comm attribute set")
assert.True(t, foundTID, "Sample should have TID attribute set")
assert.True(t, foundCPU, "Sample should have CPU attribute set")

}

func TestStackTableOrder(t *testing.T) {
Expand Down Expand Up @@ -761,7 +813,15 @@ func TestGenerate_Validate(t *testing.T) {
}
events := map[libpf.Origin]samples.SampleToEvents{
support.TraceOriginSampling: {
{}: &samples.TraceEvents{
{
Hash: libpf.NewTraceHash(0, 1),
Comm: libpf.Intern("abc"),
TID: 42,
CPU: 73,
SpanID: libpf.APMSpanID{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7},
TraceID: libpf.APMTraceID{0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27},
}: &samples.TraceEvents{
Frames: singleFrameTrace(libpf.PythonFrame, mapping, 0x30,
funcName, filePath, 123),
Timestamps: []uint64{42},
Expand Down
6 changes: 6 additions & 0 deletions reporter/internal/pdata/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,9 @@ func hashLocationIndices(locationIndices []int32) uint64 {
h.Write(pfunsafe.FromSlice(locationIndices))
return h.Sum64()
}

// linkInfo is a helper used to deduplicate Links.
type linkInfo struct {
spanID libpf.APMSpanID
traceID libpf.APMTraceID
}
5 changes: 5 additions & 0 deletions reporter/samples/samples.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type TraceEventMeta struct {
Origin libpf.Origin
OffTime int64
PID, TID libpf.PID
SpanID libpf.APMSpanID
TraceID libpf.APMTraceID
}

// TraceEvents holds known information about a trace.
Expand Down Expand Up @@ -78,4 +80,7 @@ type SampleKey struct {

TID int64
CPU int64

SpanID libpf.APMSpanID
TraceID libpf.APMTraceID
}
Loading