Skip to content

Commit 5a1318b

Browse files
authored
Support for Node.js profiles (#4728)
* Support for Node.js profiles Implements support for Node.js profiles, by normalizing sample.count to samples.count and adding wall.microseconds as a supported value type.
1 parent 5042806 commit 5a1318b

8 files changed

Lines changed: 53 additions & 4 deletions

File tree

apmpackage/apm/0.1.0/data_stream/profile_metrics/fields/fields.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@
102102
type: long
103103
description: |
104104
Source code line number for the top stack frame.
105+
- name: profile.wall.us
106+
type: long
107+
description: |
108+
Amount of wall time profiled, in microseconds.
105109
- name: service.environment
106110
type: keyword
107111
description: |

changelogs/head.asciidoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ https://github.com/elastic/apm-server/compare/7.11\...master[View commits]
3030
* When tail-sampling is enabled, a default policy must be defined {pull}4729[4729]
3131
* Support additional config options when running under Fleet {pull}4690[4690]
3232
* Upgrade Go to 1.15.8 {pull}4733[4733]
33+
* Add support for Node.js wall time profiles {pull}4728[4728]
3334

3435
[float]
3536
==== Deprecated

cmd/pprofessor/fetcher.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,11 @@ func (f *fetcher) fetchProfile(
166166
"field": "profile.cpu.ns",
167167
},
168168
},
169+
"wall_us": map[string]interface{}{
170+
"sum": map[string]interface{}{
171+
"field": "profile.wall.us",
172+
},
173+
},
169174
"alloc_objects": map[string]interface{}{
170175
"sum": map[string]interface{}{
171176
"field": "profile.alloc_objects.count",
@@ -257,6 +262,7 @@ func (f *fetcher) fetchProfile(
257262
if i == 0 {
258263
node.samplesCount += int64(stack.SamplesCount.Value)
259264
node.cpuNanos += int64(stack.CPUNanos.Value)
265+
node.wallMicros += int64(stack.WallMicros.Value)
260266
node.allocObjects += int64(stack.AllocObjects.Value)
261267
node.allocSpaceBytes += int64(stack.AllocSpace.Value)
262268
node.inuseObjects += int64(stack.InuseObjects.Value)
@@ -285,6 +291,7 @@ func (f *fetcher) fetchProfile(
285291
SampleType: []*profile.ValueType{
286292
{Type: "samples", Unit: "count"},
287293
{Type: "cpu", Unit: "nanoseconds"},
294+
{Type: "wall", Unit: "microseconds"},
288295
{Type: "alloc_objects", Unit: "count"},
289296
{Type: "alloc_space", Unit: "bytes"},
290297
{Type: "inuse_objects", Unit: "count"},
@@ -340,6 +347,7 @@ func (f *fetcher) fetchProfile(
340347
Value: []int64{
341348
node.samplesCount,
342349
node.cpuNanos,
350+
node.wallMicros,
343351
node.allocObjects,
344352
node.allocSpaceBytes,
345353
node.inuseObjects,
@@ -377,6 +385,7 @@ type aggregationsResult struct {
377385
}
378386
DocCount int `json:"doc_count"`
379387
CPUNanos numericAggValue `json:"cpu_ns"`
388+
WallMicros numericAggValue `json:"wall_us"`
380389
AllocObjects numericAggValue `json:"alloc_objects"`
381390
AllocSpace numericAggValue `json:"alloc_space"`
382391
InuseObjects numericAggValue `json:"inuse_objects"`
@@ -416,6 +425,7 @@ type profileNode struct {
416425

417426
samplesCount int64
418427
cpuNanos int64
428+
wallMicros int64
419429
allocObjects int64
420430
allocSpaceBytes int64
421431
inuseObjects int64

docs/fields.asciidoc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2206,6 +2206,17 @@ type: long
22062206
--
22072207
22082208
2209+
*`profile.wall.us`*::
2210+
+
2211+
--
2212+
Amount of wall time profiled, in microseconds.
2213+
2214+
2215+
type: long
2216+
2217+
--
2218+
2219+
22092220
*`profile.samples.count`*::
22102221
+
22112222
--

include/fields.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

model/profile.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,14 @@ func (pp PprofProfile) Transform(ctx context.Context, cfg *transform.Config) []b
5858
valueFieldNames := make([]string, len(pp.Profile.SampleType))
5959
for i, sampleType := range pp.Profile.SampleType {
6060
sampleUnit := normalizeUnit(sampleType.Unit)
61-
valueFieldNames[i] = sampleType.Type + "." + sampleUnit
61+
// Go profiles report samples.count, Node.js profiles report sample.count.
62+
// We use samples.count for both so we can aggregate on one field.
63+
if sampleType.Type == "sample" || sampleType.Type == "samples" {
64+
valueFieldNames[i] = "samples.count"
65+
} else {
66+
valueFieldNames[i] = sampleType.Type + "." + sampleUnit
67+
}
68+
6269
}
6370

6471
// Generate a unique profile ID shared by all samples in the profile.
@@ -148,6 +155,9 @@ func normalizeUnit(unit string) string {
148155
switch unit {
149156
case "nanoseconds":
150157
unit = "ns"
158+
159+
case "microseconds":
160+
unit = "us"
151161
}
152162
return unit
153163
}

model/profile/_meta/fields.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,15 @@
619619
count: 1
620620
description: >
621621
Amount of CPU time profiled, in nanoseconds.
622+
623+
- name: wall
624+
type: group
625+
fields:
626+
- name: us
627+
type: long
628+
count: 1
629+
description: >
630+
Amount of wall time profiled, in microseconds.
622631
623632
- name: samples
624633
type: group

model/profile_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,13 @@ func TestPprofProfileTransform(t *testing.T) {
4646
TimeNanos: timestamp.UnixNano(),
4747
DurationNanos: int64(10 * time.Second),
4848
SampleType: []*pprof_profile.ValueType{
49+
{Type: "sample", Unit: "count"},
4950
{Type: "cpu", Unit: "nanoseconds"},
51+
{Type: "wall", Unit: "microseconds"},
5052
{Type: "inuse_space", Unit: "bytes"},
5153
},
5254
Sample: []*pprof_profile.Sample{{
53-
Value: []int64{123, 456},
55+
Value: []int64{1, 123, 789, 456},
5456
Label: map[string][]string{
5557
"key1": []string{"abc", "def"},
5658
"key2": []string{"ghi"},
@@ -66,7 +68,7 @@ func TestPprofProfileTransform(t *testing.T) {
6668
}},
6769
}},
6870
}, {
69-
Value: []int64{123, 456},
71+
Value: []int64{1, 123, 789, 456},
7072
Label: map[string][]string{
7173
"key1": []string{"abc", "def"},
7274
"key2": []string{"ghi"},
@@ -112,7 +114,9 @@ func TestPprofProfileTransform(t *testing.T) {
112114
"id": "random",
113115
"duration": int64(10 * time.Second),
114116
"cpu.ns": int64(123),
117+
"wall.us": int64(789),
115118
"inuse_space.bytes": int64(456),
119+
"samples.count": int64(1),
116120
"top": common.MapStr{
117121
"function": "foo",
118122
"filename": "foo.go",

0 commit comments

Comments
 (0)