Skip to content

Commit 2f8c981

Browse files
add ByteProfileMerger
1 parent 463f6b4 commit 2f8c981

7 files changed

Lines changed: 1515 additions & 208 deletions

File tree

merge.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,46 @@ type locationKey struct {
2828
isFolded bool
2929
}
3030

31+
// ByteProfileUnPacker is the unpacker for MergedByteProfile
32+
type ByteProfileUnPacker struct {
33+
mergedProfile *MergedByteProfile
34+
}
35+
36+
// NewByteProfileUnPacker returns new ByteProfileUnPacker instance
37+
func NewByteProfileUnPacker(mergedProfile *MergedByteProfile) *ByteProfileUnPacker {
38+
return &ByteProfileUnPacker{
39+
mergedProfile: mergedProfile,
40+
}
41+
}
42+
43+
func (pu *ByteProfileUnPacker) UnpackRaw(compressedRawProfile []byte, idx uint64) ([]byte, error) {
44+
bb := bytes.NewBuffer(compressedRawProfile)
45+
46+
gzReader, err := gzip.NewReader(bb)
47+
if err != nil {
48+
return nil, err
49+
}
50+
51+
rawProfile, err := io.ReadAll(gzReader)
52+
if err != nil {
53+
return nil, err
54+
}
55+
56+
if pu.mergedProfile == nil {
57+
pu.mergedProfile = new(MergedByteProfile)
58+
}
59+
60+
if err = proto.Unmarshal(rawProfile, pu.mergedProfile); err != nil {
61+
return nil, err
62+
}
63+
64+
return pu.Unpack(idx)
65+
}
66+
67+
func (pu *ByteProfileUnPacker) Unpack(idx uint64) ([]byte, error) {
68+
return pu.mergedProfile.Profiles[idx], nil
69+
}
70+
3171
// ProfileUnPacker recovers any of the profiles stored inside mergedProfile
3272
type ProfileUnPacker struct {
3373
mergedProfile *MergedProfile
@@ -287,6 +327,47 @@ func (pu *ProfileUnPacker) unpackMapping(p *profile.Profile, id uint64) *profile
287327
return profileMapping
288328
}
289329

330+
type ByteProfileMerger struct {
331+
mergedProfile *MergedByteProfile
332+
}
333+
334+
func NewByteProfileMerger() *ByteProfileMerger {
335+
return &ByteProfileMerger{
336+
mergedProfile: new(MergedByteProfile),
337+
}
338+
}
339+
340+
func (bm *ByteProfileMerger) Merge(profiles ...[]byte) *MergedByteProfile {
341+
bm.mergedProfile.Profiles = make([][]byte, len(profiles))
342+
for i, p := range profiles {
343+
bm.mergedProfile.Profiles[i] = p
344+
}
345+
346+
return bm.mergedProfile
347+
}
348+
349+
func (bm *ByteProfileMerger) WriteCompressed(w io.Writer) error {
350+
// Write writes the profile as a gzip-compressed marshaled protobuf.
351+
zw := gzip.NewWriter(w)
352+
defer zw.Close()
353+
serialized, err := proto.Marshal(bm.mergedProfile)
354+
if err != nil {
355+
return err
356+
}
357+
358+
_, err = zw.Write(serialized)
359+
return err
360+
}
361+
362+
func (bm *ByteProfileMerger) WriteUncompressed(w io.Writer) error {
363+
serialized, err := proto.Marshal(bm.mergedProfile)
364+
if err != nil {
365+
return err
366+
}
367+
_, err = w.Write(serialized)
368+
return err
369+
}
370+
290371
// ProfileMerger merges several profiles into a single one
291372
type ProfileMerger struct {
292373
mergedProfile *MergedProfile

merge_test.go

Lines changed: 79 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package ppmerge
22

33
import (
44
"bytes"
5+
"io"
56
"os"
67
"testing"
78

@@ -119,20 +120,73 @@ func TestMergeWrite(t *testing.T) {
119120
}
120121

121122
func TestMergeUnpack(t *testing.T) {
122-
profiles := getProfiles(t, "hprof1", "hprof2", "hprof3", "hprof4")
123+
t.Run("general merge unpack", func(t *testing.T) {
124+
profiles := getProfiles(t, "hprof1", "hprof2", "hprof3", "hprof4")
123125

124-
profileMerger := NewProfileMerger()
125-
mergedProfile := profileMerger.Merge(profiles...)
126-
require.NotNil(t, mergedProfile)
126+
profileMerger := NewProfileMerger()
127+
mergedProfile := profileMerger.Merge(profiles...)
128+
require.NotNil(t, mergedProfile)
127129

128-
compressedBB := bytes.NewBuffer(nil)
129-
require.NoError(t, profileMerger.WriteCompressed(compressedBB))
130-
require.Greater(t, compressedBB.Len(), 0)
130+
compressedBB := bytes.NewBuffer(nil)
131+
require.NoError(t, profileMerger.WriteCompressed(compressedBB))
132+
require.Greater(t, compressedBB.Len(), 0)
133+
134+
unpacker := NewProfileUnPacker(nil)
135+
p, err := unpacker.UnpackRaw(compressedBB.Bytes(), 0)
136+
require.NoError(t, err)
137+
require.NotNil(t, p)
138+
})
139+
140+
t.Run("merge unpack debug goroutine profiles", func(t *testing.T) {
141+
profiles := getDebugProfiles(t, "parca_goroutine_debug_1_1", "parca_goroutine_debug_1_2", "parca_goroutine_debug_1_2")
142+
143+
profileMerger := NewByteProfileMerger()
144+
mergedProfile := profileMerger.Merge(profiles...)
145+
require.NotNil(t, mergedProfile)
146+
147+
unpacker := NewByteProfileUnPacker(mergedProfile)
148+
p, err := unpacker.Unpack(0)
149+
require.NoError(t, err)
150+
require.NotNil(t, p)
151+
require.Equal(t, profiles[0], p)
152+
153+
p, err = unpacker.Unpack(1)
154+
require.NoError(t, err)
155+
require.NotNil(t, p)
156+
require.Equal(t, profiles[1], p)
157+
158+
p, err = unpacker.Unpack(2)
159+
require.NoError(t, err)
160+
require.NotNil(t, p)
161+
require.Equal(t, profiles[2], p)
162+
})
131163

132-
unpacker := NewProfileUnPacker(nil)
133-
p, err := unpacker.UnpackRaw(compressedBB.Bytes(), 0)
134-
require.NoError(t, err)
135-
require.NotNil(t, p)
164+
t.Run("merge unpack raw debug goroutine profiles", func(t *testing.T) {
165+
profiles := getDebugProfiles(t, "parca_goroutine_debug_1_1", "parca_goroutine_debug_1_2", "parca_goroutine_debug_1_2")
166+
167+
profileMerger := NewByteProfileMerger()
168+
mergedProfile := profileMerger.Merge(profiles...)
169+
require.NotNil(t, mergedProfile)
170+
171+
bb := bytes.NewBuffer(nil)
172+
require.NoError(t, profileMerger.WriteCompressed(bb))
173+
174+
unpacker := NewByteProfileUnPacker(mergedProfile)
175+
p, err := unpacker.UnpackRaw(bb.Bytes(), 0)
176+
require.NoError(t, err)
177+
require.NotNil(t, p)
178+
require.Equal(t, profiles[0], p)
179+
180+
p, err = unpacker.UnpackRaw(bb.Bytes(), 1)
181+
require.NoError(t, err)
182+
require.NotNil(t, p)
183+
require.Equal(t, profiles[1], p)
184+
185+
p, err = unpacker.UnpackRaw(bb.Bytes(), 2)
186+
require.NoError(t, err)
187+
require.NotNil(t, p)
188+
require.Equal(t, profiles[2], p)
189+
})
136190
}
137191

138192
func BenchmarkProfileMerger(b *testing.B) {
@@ -173,3 +227,17 @@ func getProfiles(t require.TestingT, paths ...string) []*profile.Profile {
173227

174228
return profiles
175229
}
230+
231+
func getDebugProfiles(t require.TestingT, paths ...string) [][]byte {
232+
dir := "./testdata/"
233+
profiles := make([][]byte, len(paths))
234+
for i, profileName := range paths {
235+
file, err := os.OpenFile(dir+profileName, os.O_RDONLY, 0666)
236+
require.NoError(t, err)
237+
p, err := io.ReadAll(file)
238+
require.NoError(t, err)
239+
profiles[i] = p
240+
}
241+
242+
return profiles
243+
}

0 commit comments

Comments
 (0)