|
@@ -40,10 +40,10 @@ func Merge(srcs []*Profile) (*Profile, error) {
|
40
|
40
|
|
41
|
41
|
pm := &profileMerger{
|
42
|
42
|
p: p,
|
43
|
|
- samples: make(map[string]*Sample, len(srcs[0].Sample)),
|
44
|
|
- locations: make(map[string]*Location, len(srcs[0].Location)),
|
45
|
|
- functions: make(map[string]*Function, len(srcs[0].Function)),
|
46
|
|
- mappings: make(map[string]*Mapping, len(srcs[0].Mapping)),
|
|
43
|
+ samples: make(map[sampleKey]*Sample, len(srcs[0].Sample)),
|
|
44
|
+ locations: make(map[locationKey]*Location, len(srcs[0].Location)),
|
|
45
|
+ functions: make(map[functionKey]*Function, len(srcs[0].Function)),
|
|
46
|
+ mappings: make(map[mappingKey]*Mapping, len(srcs[0].Mapping)),
|
47
|
47
|
}
|
48
|
48
|
|
49
|
49
|
for _, src := range srcs {
|
|
@@ -96,10 +96,10 @@ type profileMerger struct {
|
96
|
96
|
mappingsByID map[uint64]mapInfo
|
97
|
97
|
|
98
|
98
|
// Memoization tables for profile entities.
|
99
|
|
- samples map[string]*Sample
|
100
|
|
- locations map[string]*Location
|
101
|
|
- functions map[string]*Function
|
102
|
|
- mappings map[string]*Mapping
|
|
99
|
+ samples map[sampleKey]*Sample
|
|
100
|
+ locations map[locationKey]*Location
|
|
101
|
+ functions map[functionKey]*Function
|
|
102
|
+ mappings map[mappingKey]*Mapping
|
103
|
103
|
}
|
104
|
104
|
|
105
|
105
|
type mapInfo struct {
|
|
@@ -143,30 +143,36 @@ func (pm *profileMerger) mapSample(src *Sample) *Sample {
|
143
|
143
|
return s
|
144
|
144
|
}
|
145
|
145
|
|
146
|
|
-// key generates encoded string of Sample to be used as a key for maps.
|
147
|
|
-func (sample *Sample) key() (s string) {
|
|
146
|
+// key generates sampleKey to be used as a key for maps.
|
|
147
|
+func (sample *Sample) key() sampleKey {
|
148
|
148
|
ids := make([]string, len(sample.Location))
|
149
|
149
|
for i, l := range sample.Location {
|
150
|
150
|
ids[i] = strconv.FormatUint(l.ID, 16)
|
151
|
151
|
}
|
152
|
|
- s = strings.Join(ids, "|")
|
153
|
|
- if len(sample.Label) != 0 {
|
154
|
|
- labels := make([]string, 0, len(sample.Label))
|
155
|
|
- for k, v := range sample.Label {
|
156
|
|
- labels = append(labels, fmt.Sprintf("%q%q", k, v))
|
157
|
|
- }
|
158
|
|
- sort.Strings(labels)
|
159
|
|
- s += strings.Join(labels, "")
|
|
152
|
+
|
|
153
|
+ labels := make([]string, 0, len(sample.Label))
|
|
154
|
+ for k, v := range sample.Label {
|
|
155
|
+ labels = append(labels, fmt.Sprintf("%q%q", k, v))
|
160
|
156
|
}
|
161
|
|
- if len(sample.NumLabel) > 0 {
|
162
|
|
- labels := make([]string, 0, len(sample.NumLabel))
|
163
|
|
- for k, v := range sample.NumLabel {
|
164
|
|
- labels = append(labels, fmt.Sprintf("%q%v", k, v))
|
165
|
|
- }
|
166
|
|
- sort.Strings(labels)
|
167
|
|
- s += strings.Join(labels, "")
|
|
157
|
+ sort.Strings(labels)
|
|
158
|
+
|
|
159
|
+ numlabels := make([]string, 0, len(sample.NumLabel))
|
|
160
|
+ for k, v := range sample.NumLabel {
|
|
161
|
+ numlabels = append(numlabels, fmt.Sprintf("%q%x", k, v))
|
168
|
162
|
}
|
169
|
|
- return s
|
|
163
|
+ sort.Strings(numlabels)
|
|
164
|
+
|
|
165
|
+ return sampleKey{
|
|
166
|
+ strings.Join(ids, "|"),
|
|
167
|
+ strings.Join(labels, ""),
|
|
168
|
+ strings.Join(numlabels, ""),
|
|
169
|
+ }
|
|
170
|
+}
|
|
171
|
+
|
|
172
|
+type sampleKey struct {
|
|
173
|
+ locations string
|
|
174
|
+ labels string
|
|
175
|
+ numlabels string
|
170
|
176
|
}
|
171
|
177
|
|
172
|
178
|
func (pm *profileMerger) mapLocation(src *Location) *Location {
|
|
@@ -202,21 +208,28 @@ func (pm *profileMerger) mapLocation(src *Location) *Location {
|
202
|
208
|
return l
|
203
|
209
|
}
|
204
|
210
|
|
205
|
|
-// key generates encoded string of Location to be used as a key for maps.
|
206
|
|
-func (l *Location) key() string {
|
207
|
|
- addr := l.Address
|
208
|
|
- var s string
|
|
211
|
+// key generates locationKey to be used as a key for maps.
|
|
212
|
+func (l *Location) key() locationKey {
|
|
213
|
+ key := locationKey{
|
|
214
|
+ addr: l.Address,
|
|
215
|
+ }
|
209
|
216
|
if l.Mapping != nil {
|
210
|
217
|
// Normalizes address to handle address space randomization.
|
211
|
|
- addr -= l.Mapping.Start
|
212
|
|
- s = strconv.FormatUint(l.Mapping.ID, 16) + "|" + strconv.FormatUint(addr, 16)
|
213
|
|
- } else {
|
214
|
|
- s = "0|" + strconv.FormatUint(addr, 16)
|
|
218
|
+ key.addr -= l.Mapping.Start
|
|
219
|
+ key.mappingID = l.Mapping.ID
|
215
|
220
|
}
|
216
|
|
- for _, line := range l.Line {
|
217
|
|
- s += strconv.FormatUint(line.Function.ID, 16) + "|" + strconv.FormatInt(line.Line, 16)
|
|
221
|
+ lines := make([]string, len(l.Line)*2)
|
|
222
|
+ for i, line := range l.Line {
|
|
223
|
+ lines[i*2] = strconv.FormatUint(line.Function.ID, 16)
|
|
224
|
+ lines[i*2+1] = strconv.FormatInt(line.Line, 16)
|
218
|
225
|
}
|
219
|
|
- return s
|
|
226
|
+ key.lines = strings.Join(lines, "|")
|
|
227
|
+ return key
|
|
228
|
+}
|
|
229
|
+
|
|
230
|
+type locationKey struct {
|
|
231
|
+ addr, mappingID uint64
|
|
232
|
+ lines string
|
220
|
233
|
}
|
221
|
234
|
|
222
|
235
|
func (pm *profileMerger) mapMapping(src *Mapping) mapInfo {
|
|
@@ -273,7 +286,7 @@ func (pm *profileMerger) mapMapping(src *Mapping) mapInfo {
|
273
|
286
|
// key generates encoded strings of Mapping to be used as a key for
|
274
|
287
|
// maps. The first key represents only the build id, while the second
|
275
|
288
|
// represents only the file path.
|
276
|
|
-func (m *Mapping) key() (buildIDKey, pathKey string) {
|
|
289
|
+func (m *Mapping) key() (buildIDKey, pathKey mappingKey) {
|
277
|
290
|
// Normalize addresses to handle address space randomization.
|
278
|
291
|
// Round up to next 4K boundary to avoid minor discrepancies.
|
279
|
292
|
const mapsizeRounding = 0x1000
|
|
@@ -281,12 +294,26 @@ func (m *Mapping) key() (buildIDKey, pathKey string) {
|
281
|
294
|
size := m.Limit - m.Start
|
282
|
295
|
size = size + mapsizeRounding - 1
|
283
|
296
|
size = size - (size % mapsizeRounding)
|
284
|
|
- key := strconv.FormatUint(size, 16) + "|" + strconv.FormatUint(m.Offset, 16)
|
285
|
|
- buildIDKey = key + "B" + strconv.Quote(m.BuildID)
|
286
|
|
- pathKey = key + "F" + strconv.Quote(m.File)
|
|
297
|
+
|
|
298
|
+ buildIDKey = mappingKey{
|
|
299
|
+ size,
|
|
300
|
+ m.Offset,
|
|
301
|
+ m.BuildID,
|
|
302
|
+ }
|
|
303
|
+
|
|
304
|
+ pathKey = mappingKey{
|
|
305
|
+ size,
|
|
306
|
+ m.Offset,
|
|
307
|
+ m.File,
|
|
308
|
+ }
|
287
|
309
|
return
|
288
|
310
|
}
|
289
|
311
|
|
|
312
|
+type mappingKey struct {
|
|
313
|
+ size, offset uint64
|
|
314
|
+ buildidIDOrFile string
|
|
315
|
+}
|
|
316
|
+
|
290
|
317
|
func (pm *profileMerger) mapLine(src Line) Line {
|
291
|
318
|
ln := Line{
|
292
|
319
|
Function: pm.mapFunction(src.Function),
|
|
@@ -320,9 +347,19 @@ func (pm *profileMerger) mapFunction(src *Function) *Function {
|
320
|
347
|
return f
|
321
|
348
|
}
|
322
|
349
|
|
323
|
|
-// key generates encoded string of Function to be used as a key for maps.
|
324
|
|
-func (f *Function) key() string {
|
325
|
|
- return fmt.Sprintf("%x%q%q%q", f.StartLine, f.Name, f.SystemName, f.Filename)
|
|
350
|
+// key generates a struct to be used as a key for maps.
|
|
351
|
+func (f *Function) key() functionKey {
|
|
352
|
+ return functionKey{
|
|
353
|
+ f.StartLine,
|
|
354
|
+ f.Name,
|
|
355
|
+ f.SystemName,
|
|
356
|
+ f.Filename,
|
|
357
|
+ }
|
|
358
|
+}
|
|
359
|
+
|
|
360
|
+type functionKey struct {
|
|
361
|
+ startLine int64
|
|
362
|
+ name, systemName, fileName string
|
326
|
363
|
}
|
327
|
364
|
|
328
|
365
|
// combineHeaders checks that all profiles can be merged and returns
|