Skip to content

Commit c623709

Browse files
committed
feat(profiler): adding cpu
1 parent 607f128 commit c623709

File tree

9 files changed

+132
-1
lines changed

9 files changed

+132
-1
lines changed

lib/agent/api/index.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ function CollectorApi (options) {
1818
this.COLLECTOR_API_EXTERNAL_EDGE_METRICS = url.resolve(options.collectorApiUrl, options.collectorApiExternalEdgeMetricsEndpoint)
1919
this.COLLECTOR_API_HEALTHCHECK = url.resolve(options.collectorApiUrl, options.collectorApiHealthcheckEndpoint)
2020
this.COLLECTOR_API_PROFILER_MEMORY_HEAPDUMP = url.resolve(options.collectorApiUrl, options.collectorApiProfilerMemoryHeapdumpEndpoint)
21+
this.COLLECTOR_API_PROFILER_CPU_PROFILE = url.resolve(options.collectorApiUrl, options.collectorApiProfilerCpuProfileEndpoint)
2122
this.COLLECTOR_API_CONTROL = url.resolve(options.collectorApiUrl, options.collectorApiControlEndpoint)
2223

2324
this.collectorLanguage = options.collectorLanguage
@@ -132,6 +133,16 @@ CollectorApi.prototype.sendMemorySnapshot = function (data) {
132133
this._send(url, this._withInstanceInfo(data))
133134
}
134135

136+
CollectorApi.prototype.sendCpuProfile = function (data) {
137+
if (!isNumber(this.serviceKey)) {
138+
debug('Service id not present, cannot send cpu profile')
139+
return
140+
}
141+
142+
var url = util.format(this.COLLECTOR_API_PROFILER_CPU_PROFILE, this.serviceKey)
143+
this._send(url, this._withInstanceInfo(data))
144+
}
145+
135146
CollectorApi.prototype.sendExternalEdgeMetrics = function (data) {
136147
if (!isNumber(this.serviceKey)) {
137148
debug('Service id not present, cannot send metrics')

lib/agent/api/index.spec.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ describe('The Trace CollectorApi module', function () {
2323
collectorApiExternalEdgeMetricsEndpoint: '/service/%s/edge-external',
2424
collectorApiHealthcheckEndpoint: '/service/%s/healthcheck',
2525
collectorApiProfilerMemoryHeapdumpEndpoint: '/service/%s/memory-heapdump',
26+
collectorApiProfilerCpuProfileEndpoint: '/service/%s/cpu-profile',
2627
collectorApiControlEndpoint: '/service/%s/control',
2728
system: {
2829
hostname: 'test.org',

lib/agent/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ function Agent (options) {
6767
controlBus: controlBus
6868
})
6969

70+
this.cpuProfiler = Profiler.cpu.create({
71+
collectorApi: this.collectorApi,
72+
config: this.config,
73+
controlBus: controlBus
74+
})
75+
7076
this.control = Control.create({
7177
collectorApi: this.collectorApi,
7278
config: this.config,

lib/agent/profiler/cpu/index.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
var debug = require('debug')('risingstack/trace')
2+
3+
var v8profiler = require('../../../optionalDependencies/v8-profiler')
4+
5+
6+
function CpuProfiler (options) {
7+
var _this = this
8+
this.collectorApi = options.collectorApi
9+
this.controlBus = options.controlBus
10+
this.profileWindow = 10000
11+
12+
this.controlBus.on('cpu-profile', function (data) {
13+
_this.sendProfile()
14+
})
15+
}
16+
17+
CpuProfiler.prototype.sendProfile = function () {
18+
var _this = this
19+
20+
v8profiler.startProfiling('trace-cpu-profile', true)
21+
22+
setTimeout(function() {
23+
var profile = v8profiler.stopProfiling('trace-cpu-profile')
24+
25+
// if the v8-profiler cannot be compiled, we won't have a profile
26+
profile && profile.export(function(err, result) {
27+
if (err) {
28+
return debug(err)
29+
}
30+
31+
_this.collectorApi.sendCpuProfile({
32+
cpuProfile: result,
33+
time: Date.now()
34+
})
35+
profile.delete()
36+
});
37+
}, this.profileWindow)
38+
}
39+
40+
function create (options) {
41+
return new CpuProfiler(options)
42+
}
43+
44+
module.exports.create = create
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
var expect = require('chai').expect
2+
var EventEmitter = require('events').EventEmitter
3+
4+
var v8profiler = require('../../../optionalDependencies/v8-profiler')
5+
var CpuProfiler = require('./')
6+
7+
describe('The Cpu Profiler module', function () {
8+
it('sends profile', function (done) {
9+
var msgBus = new EventEmitter()
10+
var profileData = 'some-profile-data'
11+
var collectorApi = {
12+
sendCpuProfile: this.sandbox.spy()
13+
}
14+
var profile = {
15+
export: function (cb) {
16+
cb(null, profileData)
17+
},
18+
delete: this.sandbox.spy()
19+
}
20+
21+
var startProfilingStub = this.sandbox.stub(v8profiler, 'startProfiling', function () {})
22+
var stopProfilingStub = this.sandbox.stub(v8profiler, 'stopProfiling', function () {
23+
return profile
24+
})
25+
26+
var now = 1234
27+
28+
var profiler = CpuProfiler.create({
29+
collectorApi: collectorApi,
30+
controlBus: msgBus
31+
})
32+
33+
profiler.profileWindow = 0
34+
35+
this.sandbox.stub(Date, 'now', function () {
36+
return now
37+
})
38+
39+
profiler.sendProfile()
40+
41+
setTimeout(function () {
42+
expect(startProfilingStub).to.be.calledWith('trace-cpu-profile', true)
43+
expect(stopProfilingStub).to.be.calledWith('trace-cpu-profile')
44+
expect(collectorApi.sendCpuProfile).to.be.calledWith({
45+
cpuProfile: profileData,
46+
time: now
47+
})
48+
expect(profile.delete).to.be.called
49+
done()
50+
}, 1)
51+
52+
})
53+
})

lib/agent/profiler/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
module.exports.memory = require('./memory')
2+
module.exports.cpu = require('./cpu')

lib/config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ config.collectorApiIncomingEdgeMetricsEndpoint = '/service/%s/edge-incoming'
1515
config.collectorApiExternalEdgeMetricsEndpoint = '/service/%s/edge-external'
1616
config.collectorApiHealthcheckEndpoint = '/service/%s/healthcheck'
1717
config.collectorApiProfilerMemoryHeapdumpEndpoint = '/service/%s/memory-heapdump'
18+
config.collectorApiProfilerCpuProfileEndpoint = '/service/%s/cpu-profile'
1819
config.collectorApiControlEndpoint = '/service/%s/control'
1920

2021
config.configPath = 'trace.config'
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
var v8profiler
2+
3+
try {
4+
v8profiler = require('v8-profiler')
5+
} catch (ex) {
6+
console.error('error: [trace]', 'v8-profiler couldn\'t be required, possibly a compiler issue - continuing')
7+
v8profiler = {
8+
startProfiling: function () {},
9+
stopProfiling: function () {}
10+
}
11+
}
12+
13+
module.exports = v8profiler

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@
5252
"node-uuid": "1.4.7",
5353
"qs": "6.2.0",
5454
"semver": "5.1.0",
55-
"sync-request": "3.0.1"
55+
"sync-request": "3.0.1",
56+
"v8-profiler": "5.6.5"
5657
},
5758
"optionalDependencies": {
5859
"microtime": "2.1.1",

0 commit comments

Comments
 (0)