@datadog/pprof 0.2.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/binding.gyp +130 -31
- package/bindings/binding.cc +13 -0
- package/bindings/code-event-record.cc +115 -0
- package/bindings/code-event-record.hh +67 -0
- package/bindings/code-map.cc +112 -0
- package/bindings/code-map.hh +47 -0
- package/bindings/cpu-time.cc +89 -0
- package/bindings/cpu-time.hh +35 -0
- package/bindings/location.cc +117 -0
- package/bindings/location.hh +43 -0
- package/bindings/per-isolate-data.cc +42 -0
- package/bindings/per-isolate-data.hh +26 -0
- package/bindings/profilers/cpu.cc +285 -0
- package/bindings/profilers/cpu.hh +78 -0
- package/bindings/profilers/heap.cc +112 -0
- package/bindings/profilers/heap.hh +40 -0
- package/bindings/profilers/wall.cc +306 -0
- package/bindings/profilers/wall.hh +28 -0
- package/bindings/sample.cc +160 -0
- package/bindings/sample.hh +48 -0
- package/bindings/test/binding.cc +59 -0
- package/bindings/test/code-event-record.test.cc +81 -0
- package/bindings/test/code-event-record.test.hh +5 -0
- package/bindings/test/code-map.test.cc +77 -0
- package/bindings/test/code-map.test.hh +5 -0
- package/bindings/test/cpu-time.test.cc +30 -0
- package/bindings/test/cpu-time.test.hh +5 -0
- package/bindings/test/location.test.cc +39 -0
- package/bindings/test/location.test.hh +5 -0
- package/bindings/test/profilers/cpu.test.cc +88 -0
- package/bindings/test/profilers/cpu.test.hh +5 -0
- package/bindings/test/sample.test.cc +128 -0
- package/bindings/test/sample.test.hh +5 -0
- package/bindings/test/tap.h +830 -0
- package/bindings/wrap.hh +20 -0
- package/out/src/cpu-profiler-bindings.d.ts +1 -0
- package/out/src/cpu-profiler-bindings.js +23 -0
- package/out/src/cpu-profiler-bindings.js.map +1 -0
- package/out/src/cpu-profiler.d.ts +19 -0
- package/out/src/cpu-profiler.js +94 -0
- package/out/src/cpu-profiler.js.map +1 -0
- package/out/src/index.d.ts +2 -0
- package/out/src/index.js +3 -1
- package/out/src/index.js.map +1 -1
- package/out/src/profile-encoder.js +3 -14
- package/out/src/profile-encoder.js.map +1 -1
- package/out/src/profile-serializer.d.ts +9 -1
- package/out/src/profile-serializer.js +73 -1
- package/out/src/profile-serializer.js.map +1 -1
- package/out/src/sourcemapper/sourcemapper.js +81 -88
- package/out/src/sourcemapper/sourcemapper.js.map +1 -1
- package/out/src/time-profiler.d.ts +1 -1
- package/out/src/time-profiler.js +40 -34
- package/out/src/time-profiler.js.map +1 -1
- package/out/src/v8-types.d.ts +38 -0
- package/out/third_party/cloud-debug-nodejs/src/agent/io/scanner.js +6 -17
- package/out/third_party/cloud-debug-nodejs/src/agent/io/scanner.js.map +1 -1
- package/package-lock.json +5627 -0
- package/package.json +12 -14
- package/prebuilds/darwin-arm64/node-102.node +0 -0
- package/prebuilds/darwin-arm64/node-108.node +0 -0
- package/prebuilds/darwin-arm64/node-72.node +0 -0
- package/prebuilds/darwin-arm64/node-79.node +0 -0
- package/prebuilds/darwin-arm64/node-83.node +0 -0
- package/prebuilds/darwin-arm64/node-88.node +0 -0
- package/prebuilds/darwin-arm64/node-93.node +0 -0
- package/prebuilds/darwin-ia32/node-102.node +0 -0
- package/prebuilds/darwin-ia32/node-108.node +0 -0
- package/prebuilds/darwin-ia32/node-72.node +0 -0
- package/prebuilds/darwin-ia32/node-79.node +0 -0
- package/prebuilds/darwin-ia32/node-83.node +0 -0
- package/prebuilds/darwin-ia32/node-88.node +0 -0
- package/prebuilds/darwin-ia32/node-93.node +0 -0
- package/prebuilds/darwin-x64/node-102.node +0 -0
- package/prebuilds/darwin-x64/node-108.node +0 -0
- package/prebuilds/darwin-x64/node-72.node +0 -0
- package/prebuilds/darwin-x64/node-79.node +0 -0
- package/prebuilds/darwin-x64/node-83.node +0 -0
- package/prebuilds/darwin-x64/node-88.node +0 -0
- package/prebuilds/darwin-x64/node-93.node +0 -0
- package/prebuilds/linux-ia32/node-72.node +0 -0
- package/prebuilds/linux-ia32/node-79.node +0 -0
- package/prebuilds/linux-x64/node-102.node +0 -0
- package/prebuilds/linux-x64/node-108.node +0 -0
- package/prebuilds/linux-x64/node-72.node +0 -0
- package/prebuilds/linux-x64/node-79.node +0 -0
- package/prebuilds/linux-x64/node-83.node +0 -0
- package/prebuilds/linux-x64/node-88.node +0 -0
- package/prebuilds/linux-x64/node-93.node +0 -0
- package/prebuilds/win32-ia32/node-102.node +0 -0
- package/prebuilds/win32-ia32/node-72.node +0 -0
- package/prebuilds/win32-ia32/node-79.node +0 -0
- package/prebuilds/win32-ia32/node-83.node +0 -0
- package/prebuilds/win32-ia32/node-88.node +0 -0
- package/prebuilds/win32-ia32/node-93.node +0 -0
- package/prebuilds/win32-x64/node-102.node +0 -0
- package/prebuilds/win32-x64/node-108.node +0 -0
- package/prebuilds/win32-x64/node-72.node +0 -0
- package/prebuilds/win32-x64/node-79.node +0 -0
- package/prebuilds/win32-x64/node-83.node +0 -0
- package/prebuilds/win32-x64/node-88.node +0 -0
- package/prebuilds/win32-x64/node-93.node +0 -0
- package/scripts/require-package-json.js +11 -11
- package/scripts/should_rebuild.js +3 -3
- package/bindings/profiler.cc +0 -384
- package/scripts/preinstall.js +0 -36
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#include <node.h>
|
|
2
|
+
|
|
3
|
+
#include "per-isolate-data.hh"
|
|
4
|
+
#include "location.hh"
|
|
5
|
+
|
|
6
|
+
namespace dd {
|
|
7
|
+
|
|
8
|
+
Location::Location(v8::Isolate* isolate,
|
|
9
|
+
std::shared_ptr<CodeEventRecord> code_event_record)
|
|
10
|
+
: code_event_record(std::move(code_event_record)) {}
|
|
11
|
+
|
|
12
|
+
Location* Location::New(v8::Isolate* isolate,
|
|
13
|
+
std::shared_ptr<CodeEventRecord> code_event_record) {
|
|
14
|
+
auto per_isolate = PerIsolateData::For(isolate);
|
|
15
|
+
v8::Local<v8::Function> cons = Nan::New(
|
|
16
|
+
per_isolate->LocationConstructor());
|
|
17
|
+
auto inst = Nan::NewInstance(cons, 0, {}).ToLocalChecked();
|
|
18
|
+
|
|
19
|
+
auto location = new Location(isolate, std::move(code_event_record));
|
|
20
|
+
location->Wrap(inst);
|
|
21
|
+
return location;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
std::shared_ptr<CodeEventRecord> Location::GetCodeEventRecord() {
|
|
25
|
+
return code_event_record;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
NAN_GETTER(Location::GetScriptId) {
|
|
29
|
+
Location* wrap = Nan::ObjectWrap::Unwrap<Location>(info.Holder());
|
|
30
|
+
info.GetReturnValue().Set(wrap->GetCodeEventRecord()->GetScriptId(info.GetIsolate()));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
NAN_GETTER(Location::GetAddress) {
|
|
34
|
+
Location* wrap = Nan::ObjectWrap::Unwrap<Location>(info.Holder());
|
|
35
|
+
info.GetReturnValue().Set(wrap->GetCodeEventRecord()->GetAddress(info.GetIsolate()));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
NAN_GETTER(Location::GetPreviousAddress) {
|
|
39
|
+
Location* wrap = Nan::ObjectWrap::Unwrap<Location>(info.Holder());
|
|
40
|
+
info.GetReturnValue().Set(wrap->GetCodeEventRecord()->GetPreviousAddress(info.GetIsolate()));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
NAN_GETTER(Location::GetSize) {
|
|
44
|
+
Location* wrap = Nan::ObjectWrap::Unwrap<Location>(info.Holder());
|
|
45
|
+
info.GetReturnValue().Set(wrap->GetCodeEventRecord()->GetSize(info.GetIsolate()));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
NAN_GETTER(Location::GetFunctionName) {
|
|
49
|
+
Location* wrap = Nan::ObjectWrap::Unwrap<Location>(info.Holder());
|
|
50
|
+
info.GetReturnValue().Set(wrap->GetCodeEventRecord()->GetFunctionName(info.GetIsolate()));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
NAN_GETTER(Location::GetScriptName) {
|
|
54
|
+
Location* wrap = Nan::ObjectWrap::Unwrap<Location>(info.Holder());
|
|
55
|
+
info.GetReturnValue().Set(wrap->GetCodeEventRecord()->GetScriptName(info.GetIsolate()));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
NAN_GETTER(Location::GetLine) {
|
|
59
|
+
Location* wrap = Nan::ObjectWrap::Unwrap<Location>(info.Holder());
|
|
60
|
+
info.GetReturnValue().Set(wrap->GetCodeEventRecord()->GetLine(info.GetIsolate()));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
NAN_GETTER(Location::GetColumn) {
|
|
64
|
+
Location* wrap = Nan::ObjectWrap::Unwrap<Location>(info.Holder());
|
|
65
|
+
info.GetReturnValue().Set(wrap->GetCodeEventRecord()->GetColumn(info.GetIsolate()));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
NAN_GETTER(Location::GetComment) {
|
|
69
|
+
Location* wrap = Nan::ObjectWrap::Unwrap<Location>(info.Holder());
|
|
70
|
+
info.GetReturnValue().Set(wrap->GetCodeEventRecord()->GetComment(info.GetIsolate()));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
NAN_MODULE_INIT(Location::Init) {
|
|
74
|
+
auto class_name = Nan::New<v8::String>("CodeEvent")
|
|
75
|
+
.ToLocalChecked();
|
|
76
|
+
|
|
77
|
+
auto tpl = Nan::New<v8::FunctionTemplate>(nullptr);
|
|
78
|
+
tpl->SetClassName(class_name);
|
|
79
|
+
tpl->InstanceTemplate()
|
|
80
|
+
->SetInternalFieldCount(1);
|
|
81
|
+
|
|
82
|
+
auto proto = tpl->InstanceTemplate();
|
|
83
|
+
|
|
84
|
+
Nan::SetAccessor(proto,
|
|
85
|
+
Nan::New("scriptId").ToLocalChecked(),
|
|
86
|
+
GetScriptId);
|
|
87
|
+
Nan::SetAccessor(proto,
|
|
88
|
+
Nan::New("address").ToLocalChecked(),
|
|
89
|
+
GetAddress);
|
|
90
|
+
Nan::SetAccessor(proto,
|
|
91
|
+
Nan::New("previousAddress").ToLocalChecked(),
|
|
92
|
+
GetPreviousAddress);
|
|
93
|
+
Nan::SetAccessor(proto,
|
|
94
|
+
Nan::New("size").ToLocalChecked(),
|
|
95
|
+
GetSize);
|
|
96
|
+
Nan::SetAccessor(proto,
|
|
97
|
+
Nan::New("line").ToLocalChecked(),
|
|
98
|
+
GetLine);
|
|
99
|
+
Nan::SetAccessor(proto,
|
|
100
|
+
Nan::New("column").ToLocalChecked(),
|
|
101
|
+
GetColumn);
|
|
102
|
+
Nan::SetAccessor(proto,
|
|
103
|
+
Nan::New("comment").ToLocalChecked(),
|
|
104
|
+
GetComment);
|
|
105
|
+
Nan::SetAccessor(proto,
|
|
106
|
+
Nan::New("functionName").ToLocalChecked(),
|
|
107
|
+
GetFunctionName);
|
|
108
|
+
Nan::SetAccessor(proto,
|
|
109
|
+
Nan::New("scriptName").ToLocalChecked(),
|
|
110
|
+
GetScriptName);
|
|
111
|
+
|
|
112
|
+
auto fn = Nan::GetFunction(tpl).ToLocalChecked();
|
|
113
|
+
auto per_isolate = PerIsolateData::For(target->GetIsolate());
|
|
114
|
+
per_isolate->LocationConstructor().Reset(fn);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
}; // namespace dd
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <string>
|
|
4
|
+
|
|
5
|
+
#include <nan.h>
|
|
6
|
+
#include <node_object_wrap.h> // cppcheck-suppress missingIncludeSystem
|
|
7
|
+
|
|
8
|
+
#include "code-event-record.hh"
|
|
9
|
+
|
|
10
|
+
namespace dd {
|
|
11
|
+
|
|
12
|
+
class Location : public Nan::ObjectWrap {
|
|
13
|
+
private:
|
|
14
|
+
std::shared_ptr<CodeEventRecord> code_event_record;
|
|
15
|
+
|
|
16
|
+
public:
|
|
17
|
+
explicit Location(v8::Isolate* isolate,
|
|
18
|
+
std::shared_ptr<CodeEventRecord> code_event_record);
|
|
19
|
+
|
|
20
|
+
static Location* New(v8::Isolate* isolate,
|
|
21
|
+
std::shared_ptr<CodeEventRecord> code_event_record);
|
|
22
|
+
|
|
23
|
+
std::shared_ptr<CodeEventRecord> GetCodeEventRecord();
|
|
24
|
+
|
|
25
|
+
static NAN_GETTER(GetScriptId);
|
|
26
|
+
static NAN_GETTER(GetAddress);
|
|
27
|
+
static NAN_GETTER(GetPreviousAddress);
|
|
28
|
+
static NAN_GETTER(GetSize);
|
|
29
|
+
static NAN_GETTER(GetLine);
|
|
30
|
+
static NAN_GETTER(GetColumn);
|
|
31
|
+
static NAN_GETTER(GetFunctionName);
|
|
32
|
+
static NAN_GETTER(GetScriptName);
|
|
33
|
+
static NAN_GETTER(GetComment);
|
|
34
|
+
|
|
35
|
+
static NAN_MODULE_INIT(Init);
|
|
36
|
+
|
|
37
|
+
friend class CodeMap;
|
|
38
|
+
|
|
39
|
+
using Nan::ObjectWrap::Ref;
|
|
40
|
+
using Nan::ObjectWrap::Unref;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
}; // namespace dd
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#include <unordered_map>
|
|
2
|
+
|
|
3
|
+
#include "per-isolate-data.hh"
|
|
4
|
+
|
|
5
|
+
namespace dd {
|
|
6
|
+
|
|
7
|
+
static std::unordered_map<v8::Isolate*, PerIsolateData> per_isolate_data_;
|
|
8
|
+
|
|
9
|
+
PerIsolateData::PerIsolateData(v8::Isolate* isolate)
|
|
10
|
+
: isolate_(isolate) {}
|
|
11
|
+
|
|
12
|
+
PerIsolateData* PerIsolateData::For(v8::Isolate* isolate) {
|
|
13
|
+
auto maybe = per_isolate_data_.find(isolate);
|
|
14
|
+
if (maybe != per_isolate_data_.end()) {
|
|
15
|
+
return &maybe->second;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
per_isolate_data_.emplace(std::make_pair(isolate, PerIsolateData(isolate)));
|
|
19
|
+
|
|
20
|
+
auto pair = per_isolate_data_.find(isolate);
|
|
21
|
+
auto perIsolateData = &pair->second;
|
|
22
|
+
|
|
23
|
+
node::AddEnvironmentCleanupHook(isolate, [](void* data) {
|
|
24
|
+
per_isolate_data_.erase(static_cast<v8::Isolate*>(data));
|
|
25
|
+
}, isolate);
|
|
26
|
+
|
|
27
|
+
return perIsolateData;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
Nan::Global<v8::Function>& PerIsolateData::CpuProfilerConstructor() {
|
|
31
|
+
return cpu_profiler_constructor;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
Nan::Global<v8::Function>& PerIsolateData::LocationConstructor() {
|
|
35
|
+
return location_constructor;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
Nan::Global<v8::Function>& PerIsolateData::SampleConstructor() {
|
|
39
|
+
return sample_constructor;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
} // namespace dd
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <nan.h>
|
|
4
|
+
#include <node.h>
|
|
5
|
+
#include <v8.h>
|
|
6
|
+
|
|
7
|
+
namespace dd {
|
|
8
|
+
|
|
9
|
+
class PerIsolateData {
|
|
10
|
+
private:
|
|
11
|
+
v8::Isolate* isolate_;
|
|
12
|
+
Nan::Global<v8::Function> cpu_profiler_constructor;
|
|
13
|
+
Nan::Global<v8::Function> location_constructor;
|
|
14
|
+
Nan::Global<v8::Function> sample_constructor;
|
|
15
|
+
|
|
16
|
+
PerIsolateData(v8::Isolate* isolate);
|
|
17
|
+
|
|
18
|
+
public:
|
|
19
|
+
static PerIsolateData* For(v8::Isolate* isolate);
|
|
20
|
+
|
|
21
|
+
Nan::Global<v8::Function>& CpuProfilerConstructor();
|
|
22
|
+
Nan::Global<v8::Function>& LocationConstructor();
|
|
23
|
+
Nan::Global<v8::Function>& SampleConstructor();
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
} // namespace dd
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
#include <queue>
|
|
2
|
+
|
|
3
|
+
#include <v8-profiler.h>
|
|
4
|
+
#include <v8.h>
|
|
5
|
+
#include <node.h>
|
|
6
|
+
#include <uv.h>
|
|
7
|
+
|
|
8
|
+
#include "../per-isolate-data.hh"
|
|
9
|
+
#include "../location.hh"
|
|
10
|
+
#include "cpu.hh"
|
|
11
|
+
|
|
12
|
+
namespace dd {
|
|
13
|
+
|
|
14
|
+
CpuProfiler::CpuProfiler()
|
|
15
|
+
: isolate_(v8::Isolate::GetCurrent()),
|
|
16
|
+
async(new uv_async_t()),
|
|
17
|
+
code_map(CodeMap::For(isolate_)),
|
|
18
|
+
samples(Nan::New<v8::Array>()),
|
|
19
|
+
sampler_running(false) {
|
|
20
|
+
// TODO: Move symbolizer worker to a separate class?
|
|
21
|
+
// Initialize libuv async worker to process samples when JS thread is idle
|
|
22
|
+
uv_async_init(Nan::GetCurrentEventLoop(), async, Run);
|
|
23
|
+
// Unref the async worker so it won't hold the loop open when there are no
|
|
24
|
+
// other tasks. This allows it to clean itself up automatically.
|
|
25
|
+
uv_unref(reinterpret_cast<uv_handle_t*>(async));
|
|
26
|
+
// The async worker needs a reference to the profiler instance so it can get
|
|
27
|
+
// the pending sample and the vector to push symbolized samples to.
|
|
28
|
+
async->data = static_cast<void*>(this);
|
|
29
|
+
uv_sem_init(&sampler_thread_done, 1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
CpuProfiler::~CpuProfiler() {
|
|
33
|
+
uv_close(reinterpret_cast<uv_handle_t*>(async),
|
|
34
|
+
[](uv_handle_t* handle){
|
|
35
|
+
delete reinterpret_cast<uv_handle_t*>(handle);
|
|
36
|
+
});
|
|
37
|
+
Stop();
|
|
38
|
+
|
|
39
|
+
uv_sem_wait(&sampler_thread_done);
|
|
40
|
+
uv_sem_destroy(&sampler_thread_done);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
CpuProfiler* CpuProfiler::New() {
|
|
44
|
+
auto isolate = v8::Isolate::GetCurrent();
|
|
45
|
+
auto per_isolate = PerIsolateData::For(isolate);
|
|
46
|
+
v8::Local<v8::Function> cons = Nan::New(
|
|
47
|
+
per_isolate->CpuProfilerConstructor());
|
|
48
|
+
auto inst = Nan::NewInstance(cons, 0, {}).ToLocalChecked();
|
|
49
|
+
return Nan::ObjectWrap::Unwrap<CpuProfiler>(inst);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
v8::Local<v8::Number> CpuProfiler::GetFrequency() {
|
|
53
|
+
return Nan::New<v8::Number>(frequency);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// NOTE: last sample must be a unique_ptr to ensure it is cleaned up
|
|
57
|
+
// when no longer referenced. However when `ProcessSample()` is called
|
|
58
|
+
// it will be released from the unique_ptr as it will become owned by
|
|
59
|
+
// the JavaScript thread which creates a corresponding handle object,
|
|
60
|
+
// making it garbage-collectable.
|
|
61
|
+
void CpuProfiler::SetLastSample(std::unique_ptr<Sample> sample) {
|
|
62
|
+
lastSample = std::move(sample);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// NOTE: For test/debug purposes only
|
|
66
|
+
Sample* CpuProfiler::GetLastSample() {
|
|
67
|
+
return lastSample.get();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
void CpuProfiler::CaptureSample(v8::Isolate* isolate) {
|
|
71
|
+
const std::lock_guard<std::mutex> lock(mutex);
|
|
72
|
+
auto diff = cpu_time.Diff();
|
|
73
|
+
SetLastSample(
|
|
74
|
+
std::make_unique<Sample>(isolate, labels_, diff));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// TODO: Make sampler thread a separate class?
|
|
78
|
+
void CpuProfiler::SamplerThread(double hz) {
|
|
79
|
+
using hrc = std::chrono::high_resolution_clock;
|
|
80
|
+
std::chrono::duration<double> interval(1.0 / hz);
|
|
81
|
+
while (sampler_running) {
|
|
82
|
+
isolate_->RequestInterrupt([](v8::Isolate* isolate, void* data) {
|
|
83
|
+
auto profiler = static_cast<CpuProfiler*>(data);
|
|
84
|
+
profiler->CaptureSample(isolate);
|
|
85
|
+
|
|
86
|
+
// Notify symbolizer worker that we have a new sample
|
|
87
|
+
uv_async_send(profiler->async);
|
|
88
|
+
}, this);
|
|
89
|
+
|
|
90
|
+
std::this_thread::sleep_for(interval);
|
|
91
|
+
}
|
|
92
|
+
uv_sem_post(&sampler_thread_done);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
void CpuProfiler::ProcessSample() {
|
|
96
|
+
v8::HandleScope scope(isolate_);
|
|
97
|
+
|
|
98
|
+
Sample* sample;
|
|
99
|
+
{
|
|
100
|
+
const std::lock_guard<std::mutex> lock(mutex);
|
|
101
|
+
sample = lastSample.release();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!sample) return;
|
|
105
|
+
if (!sample->Symbolize(code_map)->Length()) {
|
|
106
|
+
delete sample;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Append the newly processed sample to the samples array
|
|
111
|
+
auto arr = samples.Get(isolate_);
|
|
112
|
+
arr->Set(isolate_->GetCurrentContext(), arr->Length(),
|
|
113
|
+
sample->ToObject(isolate_)).Check();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
void CpuProfiler::Run(uv_async_t* handle) {
|
|
117
|
+
auto profiler = static_cast<CpuProfiler*>(handle->data);
|
|
118
|
+
profiler->ProcessSample();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
void CpuProfiler::Start(double hz) {
|
|
122
|
+
if (sampler_running) return;
|
|
123
|
+
|
|
124
|
+
frequency = hz;
|
|
125
|
+
sampler_running = true;
|
|
126
|
+
uv_sem_init(&sampler_thread_done, 0);
|
|
127
|
+
uv_thread_create(&sampler_thread, [](void* arg) {
|
|
128
|
+
auto profiler = static_cast<CpuProfiler*>(arg);
|
|
129
|
+
profiler->SamplerThread(profiler->frequency);
|
|
130
|
+
}, this);
|
|
131
|
+
start_time = uv_hrtime();
|
|
132
|
+
code_map->Enable();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
void CpuProfiler::Stop() {
|
|
136
|
+
if (!sampler_running) return;
|
|
137
|
+
|
|
138
|
+
frequency = 0;
|
|
139
|
+
sampler_running = false;
|
|
140
|
+
code_map->Disable();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
v8::Local<v8::Value> CpuProfiler::GetLabels() {
|
|
144
|
+
const std::lock_guard<std::mutex> lock(mutex);
|
|
145
|
+
if (!labels_) return Nan::Undefined();
|
|
146
|
+
return labels_->handle();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
void CpuProfiler::SetLabels(v8::Local<v8::Value> value) {
|
|
150
|
+
const std::lock_guard<std::mutex> lock(mutex);
|
|
151
|
+
labels_ = std::make_shared<LabelWrap>(value);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
uint32_t CpuProfiler::GetSampleCount() {
|
|
155
|
+
return samples.Get(isolate_)->Length();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// TODO: Probably should make an explicit clear method rather than
|
|
159
|
+
// implicitly clearing whenever getting the samples array
|
|
160
|
+
v8::Local<v8::Array> CpuProfiler::GetSamples() {
|
|
161
|
+
auto array = samples.Get(isolate_);
|
|
162
|
+
samples.Reset(Nan::New<v8::Array>());
|
|
163
|
+
return array;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
v8::Local<v8::Value> CpuProfiler::GetProfile() {
|
|
167
|
+
auto profile = Nan::New<v8::Object>();
|
|
168
|
+
auto end_time = uv_hrtime();
|
|
169
|
+
|
|
170
|
+
Nan::Set(profile,
|
|
171
|
+
Nan::New("name").ToLocalChecked(),
|
|
172
|
+
Nan::New("(root)").ToLocalChecked()).Check();
|
|
173
|
+
Nan::Set(profile,
|
|
174
|
+
Nan::New("startTime").ToLocalChecked(),
|
|
175
|
+
v8::BigInt::New(isolate_, start_time)).Check();
|
|
176
|
+
Nan::Set(profile,
|
|
177
|
+
Nan::New("endTime").ToLocalChecked(),
|
|
178
|
+
v8::BigInt::New(isolate_, end_time)).Check();
|
|
179
|
+
Nan::Set(profile,
|
|
180
|
+
Nan::New("samples").ToLocalChecked(),
|
|
181
|
+
GetSamples()).Check();
|
|
182
|
+
|
|
183
|
+
start_time = end_time;
|
|
184
|
+
|
|
185
|
+
return profile;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
NAN_METHOD(CpuProfiler::New) {
|
|
189
|
+
if (info.IsConstructCall()) {
|
|
190
|
+
CpuProfiler* profiler = new CpuProfiler();
|
|
191
|
+
profiler->Wrap(info.This());
|
|
192
|
+
info.GetReturnValue().Set(info.This());
|
|
193
|
+
} else {
|
|
194
|
+
auto per_isolate = PerIsolateData::For(info.GetIsolate());
|
|
195
|
+
v8::Local<v8::Function> cons = Nan::New(
|
|
196
|
+
per_isolate->CpuProfilerConstructor());
|
|
197
|
+
info.GetReturnValue().Set(Nan::NewInstance(cons, 0, {}).ToLocalChecked());
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
NAN_GETTER(CpuProfiler::GetFrequency) {
|
|
202
|
+
auto profiler = Nan::ObjectWrap::Unwrap<CpuProfiler>(info.Holder());
|
|
203
|
+
info.GetReturnValue().Set(profiler->GetFrequency());
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
NAN_METHOD(CpuProfiler::Start) {
|
|
207
|
+
if (!info[0]->IsNumber()) {
|
|
208
|
+
Nan::ThrowTypeError("hz is not a number");
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
auto profiler = Nan::ObjectWrap::Unwrap<CpuProfiler>(info.Holder());
|
|
213
|
+
profiler->Start(Nan::To<double>(info[0]).FromJust());
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
NAN_METHOD(CpuProfiler::Stop) {
|
|
217
|
+
auto profiler = Nan::ObjectWrap::Unwrap<CpuProfiler>(info.Holder());
|
|
218
|
+
profiler->Stop();
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
NAN_METHOD(CpuProfiler::CaptureSample) {
|
|
222
|
+
auto profiler = Nan::ObjectWrap::Unwrap<CpuProfiler>(info.Holder());
|
|
223
|
+
profiler->CaptureSample(info.GetIsolate());
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
NAN_METHOD(CpuProfiler::ProcessSample) {
|
|
227
|
+
auto profiler = Nan::ObjectWrap::Unwrap<CpuProfiler>(info.Holder());
|
|
228
|
+
profiler->ProcessSample();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
NAN_GETTER(CpuProfiler::GetLabels) {
|
|
232
|
+
auto profiler = Nan::ObjectWrap::Unwrap<CpuProfiler>(info.Holder());
|
|
233
|
+
info.GetReturnValue().Set(profiler->GetLabels());
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
NAN_SETTER(CpuProfiler::SetLabels) {
|
|
237
|
+
auto profiler = Nan::ObjectWrap::Unwrap<CpuProfiler>(info.Holder());
|
|
238
|
+
profiler->SetLabels(value);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
NAN_METHOD(CpuProfiler::GetSamples) {
|
|
242
|
+
auto profiler = Nan::ObjectWrap::Unwrap<CpuProfiler>(info.Holder());
|
|
243
|
+
info.GetReturnValue().Set(profiler->GetSamples());
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
NAN_METHOD(CpuProfiler::GetProfile) {
|
|
247
|
+
auto profiler = Nan::ObjectWrap::Unwrap<CpuProfiler>(info.Holder());
|
|
248
|
+
info.GetReturnValue().Set(profiler->GetProfile());
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
NAN_MODULE_INIT(CpuProfiler::Init) {
|
|
252
|
+
Location::Init(target);
|
|
253
|
+
Sample::Init(target);
|
|
254
|
+
|
|
255
|
+
auto class_name = Nan::New<v8::String>("CpuProfiler")
|
|
256
|
+
.ToLocalChecked();
|
|
257
|
+
|
|
258
|
+
auto tpl = Nan::New<v8::FunctionTemplate>(New);
|
|
259
|
+
tpl->SetClassName(class_name);
|
|
260
|
+
tpl->InstanceTemplate()
|
|
261
|
+
->SetInternalFieldCount(1);
|
|
262
|
+
|
|
263
|
+
auto inst = tpl->InstanceTemplate();
|
|
264
|
+
Nan::SetAccessor(inst,
|
|
265
|
+
Nan::New("labels").ToLocalChecked(),
|
|
266
|
+
GetLabels, SetLabels);
|
|
267
|
+
|
|
268
|
+
Nan::SetAccessor(inst,
|
|
269
|
+
Nan::New("frequency").ToLocalChecked(),
|
|
270
|
+
GetFrequency);
|
|
271
|
+
|
|
272
|
+
Nan::SetPrototypeMethod(tpl, "start", Start);
|
|
273
|
+
Nan::SetPrototypeMethod(tpl, "stop", Stop);
|
|
274
|
+
Nan::SetPrototypeMethod(tpl, "captureSample", CaptureSample);
|
|
275
|
+
Nan::SetPrototypeMethod(tpl, "processSample", ProcessSample);
|
|
276
|
+
Nan::SetPrototypeMethod(tpl, "samples", GetSamples);
|
|
277
|
+
Nan::SetPrototypeMethod(tpl, "profile", GetProfile);
|
|
278
|
+
|
|
279
|
+
auto fn = Nan::GetFunction(tpl).ToLocalChecked();
|
|
280
|
+
Nan::Set(target, class_name, fn);
|
|
281
|
+
auto per_isolate = PerIsolateData::For(target->GetIsolate());
|
|
282
|
+
per_isolate->CpuProfilerConstructor().Reset(fn);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
} // namespace dd
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <atomic>
|
|
4
|
+
#include <mutex>
|
|
5
|
+
#include <thread>
|
|
6
|
+
#include <chrono>
|
|
7
|
+
|
|
8
|
+
#include <nan.h>
|
|
9
|
+
|
|
10
|
+
#include "../code-map.hh"
|
|
11
|
+
#include "../cpu-time.hh"
|
|
12
|
+
#include "../sample.hh"
|
|
13
|
+
#include "../wrap.hh"
|
|
14
|
+
|
|
15
|
+
namespace dd {
|
|
16
|
+
|
|
17
|
+
class CpuProfiler : public Nan::ObjectWrap {
|
|
18
|
+
friend class CodeMap;
|
|
19
|
+
|
|
20
|
+
private:
|
|
21
|
+
v8::Isolate* isolate_;
|
|
22
|
+
uv_async_t* async;
|
|
23
|
+
std::shared_ptr<CodeMap> code_map;
|
|
24
|
+
CpuTime cpu_time;
|
|
25
|
+
std::unique_ptr<Sample> lastSample;
|
|
26
|
+
std::shared_ptr<LabelWrap> labels_;
|
|
27
|
+
double frequency = 0;
|
|
28
|
+
Nan::Global<v8::Array> samples;
|
|
29
|
+
std::mutex mutex;
|
|
30
|
+
uint64_t start_time;
|
|
31
|
+
uv_sem_t sampler_thread_done;
|
|
32
|
+
uv_thread_t sampler_thread;
|
|
33
|
+
std::atomic_bool sampler_running;
|
|
34
|
+
|
|
35
|
+
public:
|
|
36
|
+
CpuProfiler();
|
|
37
|
+
~CpuProfiler();
|
|
38
|
+
static CpuProfiler* New();
|
|
39
|
+
|
|
40
|
+
// Disable copies and moves
|
|
41
|
+
CpuProfiler(const CpuProfiler& other) = delete;
|
|
42
|
+
CpuProfiler(CpuProfiler&& other) = delete;
|
|
43
|
+
CpuProfiler& operator=(const CpuProfiler& other) = delete;
|
|
44
|
+
CpuProfiler& operator=(CpuProfiler&& other) = delete;
|
|
45
|
+
|
|
46
|
+
v8::Local<v8::Number> GetFrequency();
|
|
47
|
+
|
|
48
|
+
void SetLastSample(std::unique_ptr<Sample> sample);
|
|
49
|
+
Sample* GetLastSample();
|
|
50
|
+
void CaptureSample(v8::Isolate* isolate);
|
|
51
|
+
void SamplerThread(double hz);
|
|
52
|
+
|
|
53
|
+
void ProcessSample();
|
|
54
|
+
static void Run(uv_async_t* handle);
|
|
55
|
+
|
|
56
|
+
v8::Local<v8::Value> GetLabels();
|
|
57
|
+
void SetLabels(v8::Local<v8::Value>);
|
|
58
|
+
void Start(double hz);
|
|
59
|
+
void Stop();
|
|
60
|
+
uint32_t GetSampleCount();
|
|
61
|
+
v8::Local<v8::Array> GetSamples();
|
|
62
|
+
v8::Local<v8::Value> GetProfile();
|
|
63
|
+
|
|
64
|
+
static NAN_METHOD(New);
|
|
65
|
+
static NAN_GETTER(GetFrequency);
|
|
66
|
+
static NAN_GETTER(GetLabels);
|
|
67
|
+
static NAN_SETTER(SetLabels);
|
|
68
|
+
static NAN_METHOD(Start);
|
|
69
|
+
static NAN_METHOD(Stop);
|
|
70
|
+
static NAN_METHOD(CaptureSample);
|
|
71
|
+
static NAN_METHOD(ProcessSample);
|
|
72
|
+
static NAN_METHOD(GetSamples);
|
|
73
|
+
static NAN_METHOD(GetProfile);
|
|
74
|
+
|
|
75
|
+
static NAN_MODULE_INIT(Init);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
} // namespace dd
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2018 Google Inc. All Rights Reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
#include "heap.hh"
|
|
18
|
+
|
|
19
|
+
#include <memory>
|
|
20
|
+
|
|
21
|
+
#include <node.h>
|
|
22
|
+
#include <v8-profiler.h>
|
|
23
|
+
|
|
24
|
+
using namespace v8;
|
|
25
|
+
|
|
26
|
+
namespace dd {
|
|
27
|
+
|
|
28
|
+
Local<Value> TranslateAllocationProfile(AllocationProfile::Node* node) {
|
|
29
|
+
Local<Object> js_node = Nan::New<Object>();
|
|
30
|
+
|
|
31
|
+
Nan::Set(js_node, Nan::New<String>("name").ToLocalChecked(), node->name);
|
|
32
|
+
Nan::Set(js_node, Nan::New<String>("scriptName").ToLocalChecked(),
|
|
33
|
+
node->script_name);
|
|
34
|
+
Nan::Set(js_node, Nan::New<String>("scriptId").ToLocalChecked(),
|
|
35
|
+
Nan::New<Integer>(node->script_id));
|
|
36
|
+
Nan::Set(js_node, Nan::New<String>("lineNumber").ToLocalChecked(),
|
|
37
|
+
Nan::New<Integer>(node->line_number));
|
|
38
|
+
Nan::Set(js_node, Nan::New<String>("columnNumber").ToLocalChecked(),
|
|
39
|
+
Nan::New<Integer>(node->column_number));
|
|
40
|
+
|
|
41
|
+
Local<Array> children = Nan::New<Array>(node->children.size());
|
|
42
|
+
for (size_t i = 0; i < node->children.size(); i++) {
|
|
43
|
+
Nan::Set(children, i, TranslateAllocationProfile(node->children[i]));
|
|
44
|
+
}
|
|
45
|
+
Nan::Set(js_node, Nan::New<String>("children").ToLocalChecked(), children);
|
|
46
|
+
Local<Array> allocations = Nan::New<Array>(node->allocations.size());
|
|
47
|
+
for (size_t i = 0; i < node->allocations.size(); i++) {
|
|
48
|
+
AllocationProfile::Allocation alloc = node->allocations[i];
|
|
49
|
+
Local<Object> js_alloc = Nan::New<Object>();
|
|
50
|
+
Nan::Set(js_alloc, Nan::New<String>("sizeBytes").ToLocalChecked(),
|
|
51
|
+
Nan::New<Number>(alloc.size));
|
|
52
|
+
Nan::Set(js_alloc, Nan::New<String>("count").ToLocalChecked(),
|
|
53
|
+
Nan::New<Number>(alloc.count));
|
|
54
|
+
Nan::Set(allocations, i, js_alloc);
|
|
55
|
+
}
|
|
56
|
+
Nan::Set(js_node, Nan::New<String>("allocations").ToLocalChecked(),
|
|
57
|
+
allocations);
|
|
58
|
+
return js_node;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
NAN_METHOD(HeapProfiler::StartSamplingHeapProfiler) {
|
|
62
|
+
if (info.Length() == 2) {
|
|
63
|
+
if (!info[0]->IsUint32()) {
|
|
64
|
+
return Nan::ThrowTypeError("First argument type must be uint32.");
|
|
65
|
+
}
|
|
66
|
+
if (!info[1]->IsNumber()) {
|
|
67
|
+
return Nan::ThrowTypeError("First argument type must be Integer.");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#if NODE_MODULE_VERSION > NODE_8_0_MODULE_VERSION
|
|
71
|
+
uint64_t sample_interval = info[0].As<Integer>()->Value();
|
|
72
|
+
int stack_depth = info[1].As<Integer>()->Value();
|
|
73
|
+
#else
|
|
74
|
+
uint64_t sample_interval = info[0].As<Integer>()->Uint32Value();
|
|
75
|
+
int stack_depth = info[1].As<Integer>()->IntegerValue();
|
|
76
|
+
#endif
|
|
77
|
+
|
|
78
|
+
info.GetIsolate()->GetHeapProfiler()->StartSamplingHeapProfiler(
|
|
79
|
+
sample_interval, stack_depth);
|
|
80
|
+
} else {
|
|
81
|
+
info.GetIsolate()->GetHeapProfiler()->StartSamplingHeapProfiler();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Signature:
|
|
86
|
+
// stopSamplingHeapProfiler()
|
|
87
|
+
NAN_METHOD(HeapProfiler::StopSamplingHeapProfiler) {
|
|
88
|
+
info.GetIsolate()->GetHeapProfiler()->StopSamplingHeapProfiler();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Signature:
|
|
92
|
+
// getAllocationProfile(): AllocationProfileNode
|
|
93
|
+
NAN_METHOD(HeapProfiler::GetAllocationProfile) {
|
|
94
|
+
std::unique_ptr<v8::AllocationProfile> profile(
|
|
95
|
+
info.GetIsolate()->GetHeapProfiler()->GetAllocationProfile());
|
|
96
|
+
AllocationProfile::Node* root = profile->GetRootNode();
|
|
97
|
+
info.GetReturnValue().Set(TranslateAllocationProfile(root));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
NAN_MODULE_INIT(HeapProfiler::Init) {
|
|
101
|
+
v8::Local<v8::Object> heapProfiler = Nan::New<v8::Object>();
|
|
102
|
+
Nan::SetMethod(heapProfiler, "startSamplingHeapProfiler",
|
|
103
|
+
StartSamplingHeapProfiler);
|
|
104
|
+
Nan::SetMethod(heapProfiler, "stopSamplingHeapProfiler",
|
|
105
|
+
StopSamplingHeapProfiler);
|
|
106
|
+
Nan::SetMethod(heapProfiler, "getAllocationProfile",
|
|
107
|
+
GetAllocationProfile);
|
|
108
|
+
Nan::Set(target, Nan::New<v8::String>("heapProfiler").ToLocalChecked(),
|
|
109
|
+
heapProfiler);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
} // namespace dd
|