Introduce SetMapping for profiles (#13197)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description This is part of https://github.com/open-telemetry/opentelemetry-collector/issues/13106 It introduces a new `SetMapping` method, which allows setting a location's mapping based on a specific dictionary. I named the method `SetMapping` and not `PutMapping`, as it overrides the `MappingIndex` value, it doesn't append to a slice. Contrarily to Locations or Attributes, as location has a single mapping. So we don't need a `FromMappingIndices` method, we can use `MappingSlice.At()` directly. --------- Co-authored-by: Tim Rühsen <tim.ruehsen@gmx.de>
This commit is contained in:
parent
3ef58fda95
commit
0387e8323b
|
|
@ -0,0 +1,25 @@
|
|||
# Use this changelog template to create an entry for release notes.
|
||||
|
||||
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
|
||||
change_type: enhancement
|
||||
|
||||
# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
|
||||
component: pdata/pprofile
|
||||
|
||||
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
|
||||
note: Introduce `Equal` method on the `Mapping` type
|
||||
|
||||
# One or more tracking issues or pull requests related to the change
|
||||
issues: [13197]
|
||||
|
||||
# (Optional) One or more lines of additional information to render under the primary note.
|
||||
# These lines will be padded with 2 spaces and then inserted directly into the document.
|
||||
# Use pipe (|) for multiline entries.
|
||||
subtext:
|
||||
|
||||
# Optional: The change log or logs in which this entry should be included.
|
||||
# e.g. '[user]' or '[user, api]'
|
||||
# Include 'user' if the change is relevant to end users.
|
||||
# Include 'api' if there is a change to a library API.
|
||||
# Default: '[user]'
|
||||
change_logs: [api]
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# Use this changelog template to create an entry for release notes.
|
||||
|
||||
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
|
||||
change_type: enhancement
|
||||
|
||||
# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
|
||||
component: pdata/pprofile
|
||||
|
||||
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
|
||||
note: Add new helper method `SetMapping` to set a new mapping on a location.
|
||||
|
||||
# One or more tracking issues or pull requests related to the change
|
||||
issues: [13197]
|
||||
|
||||
# (Optional) One or more lines of additional information to render under the primary note.
|
||||
# These lines will be padded with 2 spaces and then inserted directly into the document.
|
||||
# Use pipe (|) for multiline entries.
|
||||
subtext:
|
||||
|
||||
# Optional: The change log or logs in which this entry should be included.
|
||||
# e.g. '[user]' or '[user, api]'
|
||||
# Include 'user' if the change is relevant to end users.
|
||||
# Include 'api' if there is a change to a library API.
|
||||
# Default: '[user]'
|
||||
change_logs: [api]
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pprofile // import "go.opentelemetry.io/collector/pdata/pprofile"
|
||||
|
||||
// Equal checks equality with another Mapping
|
||||
func (m Mapping) Equal(val Mapping) bool {
|
||||
return m.MemoryStart() == val.MemoryStart() &&
|
||||
m.MemoryLimit() == val.MemoryLimit() &&
|
||||
m.FileOffset() == val.FileOffset() &&
|
||||
m.FilenameStrindex() == val.FilenameStrindex() &&
|
||||
m.AttributeIndices().Equal(val.AttributeIndices()) &&
|
||||
m.HasFunctions() == val.HasFunctions() &&
|
||||
m.HasFilenames() == val.HasFilenames() &&
|
||||
m.HasLineNumbers() == val.HasLineNumbers() &&
|
||||
m.HasInlineFrames() == val.HasInlineFrames()
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pprofile
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMappingEqual(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
orig Mapping
|
||||
dest Mapping
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "empty mappings",
|
||||
orig: NewMapping(),
|
||||
dest: NewMapping(),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "non-empty identical mappings",
|
||||
orig: buildMapping(1, 2, 3, 4, []int32{1, 2}, true, true, true, true),
|
||||
dest: buildMapping(1, 2, 3, 4, []int32{1, 2}, true, true, true, true),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "with different MemoryStart",
|
||||
orig: buildMapping(1, 2, 3, 4, []int32{1, 2}, true, true, true, true),
|
||||
dest: buildMapping(2, 2, 3, 4, []int32{1, 2}, true, true, true, true),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "with different MemoryLimit",
|
||||
orig: buildMapping(1, 2, 3, 4, []int32{1, 2}, true, true, true, true),
|
||||
dest: buildMapping(1, 3, 3, 4, []int32{1, 2}, true, true, true, true),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "with different FileOffset",
|
||||
orig: buildMapping(1, 2, 3, 4, []int32{1, 2}, true, true, true, true),
|
||||
dest: buildMapping(1, 2, 4, 4, []int32{1, 2}, true, true, true, true),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "with different FilenameStrindex",
|
||||
orig: buildMapping(1, 2, 3, 4, []int32{1, 2}, true, true, true, true),
|
||||
dest: buildMapping(1, 2, 3, 5, []int32{1, 2}, true, true, true, true),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "with different AttributeIndices",
|
||||
orig: buildMapping(1, 2, 3, 4, []int32{1, 2}, true, true, true, true),
|
||||
dest: buildMapping(1, 2, 3, 4, []int32{1, 3}, true, true, true, true),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "with different HasFunctions",
|
||||
orig: buildMapping(1, 2, 3, 4, []int32{1, 2}, true, true, true, true),
|
||||
dest: buildMapping(1, 2, 3, 4, []int32{1, 2}, false, true, true, true),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "with different HasFilenames",
|
||||
orig: buildMapping(1, 2, 3, 4, []int32{1, 2}, true, true, true, true),
|
||||
dest: buildMapping(1, 2, 3, 4, []int32{1, 2}, true, false, true, true),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "with different HasLineNumbers",
|
||||
orig: buildMapping(1, 2, 3, 4, []int32{1, 2}, true, true, true, true),
|
||||
dest: buildMapping(1, 2, 3, 4, []int32{1, 2}, true, true, false, true),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "with different HasInlineFrames",
|
||||
orig: buildMapping(1, 2, 3, 4, []int32{1, 2}, true, true, true, true),
|
||||
dest: buildMapping(1, 2, 3, 4, []int32{1, 2}, true, true, true, false),
|
||||
want: false,
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.want {
|
||||
assert.True(t, tt.orig.Equal(tt.dest))
|
||||
} else {
|
||||
assert.False(t, tt.orig.Equal(tt.dest))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func buildMapping(memStart, memLimit, fileOffset uint64, filenameIdx int32, attrIdxs []int32, hasFn, hasFilenames, hasLnNumber, hasInlFrames bool) Mapping {
|
||||
m := NewMapping()
|
||||
m.SetMemoryStart(memStart)
|
||||
m.SetMemoryLimit(memLimit)
|
||||
m.SetFileOffset(fileOffset)
|
||||
m.SetFilenameStrindex(filenameIdx)
|
||||
m.AttributeIndices().FromRaw(attrIdxs)
|
||||
m.SetHasFunctions(hasFn)
|
||||
m.SetHasFilenames(hasFilenames)
|
||||
m.SetHasLineNumbers(hasLnNumber)
|
||||
m.SetHasInlineFrames(hasInlFrames)
|
||||
return m
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pprofile // import "go.opentelemetry.io/collector/pdata/pprofile"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
var errTooManyMappingTableEntries = errors.New("too many entries in MappingTable")
|
||||
|
||||
// SetMapping updates a MappingTable and a Location's MappingIndex to
|
||||
// add or update a mapping.
|
||||
func SetMapping(table MappingSlice, record Location, ma Mapping) error {
|
||||
idx := int(record.MappingIndex())
|
||||
if idx > 0 {
|
||||
if idx >= table.Len() {
|
||||
return fmt.Errorf("index value %d out of range for MappingIndex", idx)
|
||||
}
|
||||
mapAt := table.At(idx)
|
||||
if mapAt.Equal(ma) {
|
||||
// Mapping already exists, nothing to do.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
for j, m := range table.All() {
|
||||
if m.Equal(ma) {
|
||||
if j > math.MaxInt32 {
|
||||
return errTooManyMappingTableEntries
|
||||
}
|
||||
// Add the index of the existing mapping to the indices.
|
||||
record.SetMappingIndex(int32(j)) //nolint:gosec // G115 overflow checked
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if table.Len() >= math.MaxInt32 {
|
||||
return errTooManyMappingTableEntries
|
||||
}
|
||||
|
||||
ma.CopyTo(table.AppendEmpty())
|
||||
record.SetMappingIndex(int32(table.Len() - 1)) //nolint:gosec // G115 overflow checked
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pprofile
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSetMapping(t *testing.T) {
|
||||
table := NewMappingSlice()
|
||||
m := NewMapping()
|
||||
m.SetMemoryLimit(1)
|
||||
m2 := NewMapping()
|
||||
m2.SetMemoryLimit(2)
|
||||
loc := NewLocation()
|
||||
|
||||
// Put a first mapping
|
||||
require.NoError(t, SetMapping(table, loc, m))
|
||||
assert.Equal(t, 1, table.Len())
|
||||
assert.Equal(t, int32(0), loc.MappingIndex())
|
||||
|
||||
// Put the same mapping
|
||||
// This should be a no-op.
|
||||
require.NoError(t, SetMapping(table, loc, m))
|
||||
assert.Equal(t, 1, table.Len())
|
||||
assert.Equal(t, int32(0), loc.MappingIndex())
|
||||
|
||||
// Set a new mapping
|
||||
// This sets the index and adds to the table.
|
||||
require.NoError(t, SetMapping(table, loc, m2))
|
||||
assert.Equal(t, 2, table.Len())
|
||||
assert.Equal(t, int32(table.Len()-1), loc.MappingIndex()) //nolint:gosec // G115
|
||||
|
||||
// Set an existing mapping
|
||||
require.NoError(t, SetMapping(table, loc, m))
|
||||
assert.Equal(t, 2, table.Len())
|
||||
assert.Equal(t, int32(0), loc.MappingIndex())
|
||||
// Set another existing mapping
|
||||
require.NoError(t, SetMapping(table, loc, m2))
|
||||
assert.Equal(t, 2, table.Len())
|
||||
assert.Equal(t, int32(table.Len()-1), loc.MappingIndex()) //nolint:gosec // G115
|
||||
}
|
||||
|
||||
func TestSetMappingCurrentTooHigh(t *testing.T) {
|
||||
table := NewMappingSlice()
|
||||
loc := NewLocation()
|
||||
loc.SetMappingIndex(42)
|
||||
|
||||
err := SetMapping(table, loc, NewMapping())
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, 0, table.Len())
|
||||
assert.Equal(t, int32(42), loc.MappingIndex())
|
||||
}
|
||||
|
||||
func BenchmarkSetMapping(b *testing.B) {
|
||||
for _, bb := range []struct {
|
||||
name string
|
||||
mapping Mapping
|
||||
|
||||
runBefore func(*testing.B, MappingSlice, Location)
|
||||
}{
|
||||
{
|
||||
name: "with a new mapping",
|
||||
mapping: NewMapping(),
|
||||
},
|
||||
{
|
||||
name: "with an existing mapping",
|
||||
mapping: func() Mapping {
|
||||
m := NewMapping()
|
||||
m.SetMemoryLimit(1)
|
||||
return m
|
||||
}(),
|
||||
|
||||
runBefore: func(_ *testing.B, table MappingSlice, _ Location) {
|
||||
m := table.AppendEmpty()
|
||||
m.SetMemoryLimit(1)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with a duplicate mapping",
|
||||
mapping: NewMapping(),
|
||||
|
||||
runBefore: func(_ *testing.B, table MappingSlice, obj Location) {
|
||||
require.NoError(b, SetMapping(table, obj, NewMapping()))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with a hundred mappings to loop through",
|
||||
mapping: func() Mapping {
|
||||
m := NewMapping()
|
||||
m.SetMemoryLimit(1)
|
||||
return m
|
||||
}(),
|
||||
|
||||
runBefore: func(_ *testing.B, table MappingSlice, _ Location) {
|
||||
for i := range 100 {
|
||||
m := table.AppendEmpty()
|
||||
m.SetMemoryLimit(uint64(i)) //nolint:gosec // overflow checked
|
||||
}
|
||||
},
|
||||
},
|
||||
} {
|
||||
b.Run(bb.name, func(b *testing.B) {
|
||||
table := NewMappingSlice()
|
||||
obj := NewLocation()
|
||||
|
||||
if bb.runBefore != nil {
|
||||
bb.runBefore(b, table, obj)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for range b.N {
|
||||
_ = SetMapping(table, obj, bb.mapping)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue