@datadog/pprof 0.3.0 → 0.5.1
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.js +4 -15
- 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.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-arm/node-102.node +0 -0
- package/prebuilds/linux-arm/node-108.node +0 -0
- package/prebuilds/linux-arm/node-72.node +0 -0
- package/prebuilds/linux-arm/node-79.node +0 -0
- package/prebuilds/linux-arm/node-83.node +0 -0
- package/prebuilds/linux-arm/node-88.node +0 -0
- package/prebuilds/linux-arm/node-93.node +0 -0
- package/prebuilds/linux-arm64/node-102.node +0 -0
- package/prebuilds/linux-arm64/node-108.node +0 -0
- package/prebuilds/linux-arm64/node-72.node +0 -0
- package/prebuilds/linux-arm64/node-79.node +0 -0
- package/prebuilds/linux-arm64/node-83.node +0 -0
- package/prebuilds/linux-arm64/node-88.node +0 -0
- package/prebuilds/linux-arm64/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 -406
- package/scripts/preinstall.js +0 -36
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#include <unordered_map>
|
|
2
|
+
#include <sstream>
|
|
3
|
+
|
|
4
|
+
#include "code-event-record.test.hh"
|
|
5
|
+
#include "../code-event-record.hh"
|
|
6
|
+
|
|
7
|
+
void test_code_event_record(Tap& t) {
|
|
8
|
+
t.plan(19);
|
|
9
|
+
|
|
10
|
+
auto isolate = v8::Isolate::GetCurrent();
|
|
11
|
+
|
|
12
|
+
auto record = new dd::CodeEventRecord(
|
|
13
|
+
isolate, 1234, 0, 5678, 1, 2, "a", "b", "c");
|
|
14
|
+
record->SetScriptId(123);
|
|
15
|
+
|
|
16
|
+
// Type helpers
|
|
17
|
+
auto Str = [isolate](v8::Local<v8::Value> val) -> std::string {
|
|
18
|
+
return *v8::String::Utf8Value(isolate, val);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
auto Int = [](v8::Local<v8::Value> val) -> int64_t {
|
|
22
|
+
return val.As<v8::Integer>()->Value();
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
t.equal(123, Int(record->GetScriptId(isolate)), "script id");
|
|
26
|
+
t.equal(1234, Int(record->GetAddress(isolate)), "address");
|
|
27
|
+
t.equal(0, Int(record->GetPreviousAddress(isolate)), "previous address");
|
|
28
|
+
t.equal(5678, Int(record->GetSize(isolate)), "size");
|
|
29
|
+
t.equal(1, Int(record->GetLine(isolate)), "line");
|
|
30
|
+
t.equal(2, Int(record->GetColumn(isolate)), "column");
|
|
31
|
+
t.equal("a", Str(record->GetComment(isolate)), "comment");
|
|
32
|
+
t.equal("b", Str(record->GetFunctionName(isolate)), "function name");
|
|
33
|
+
t.equal("c", Str(record->GetScriptName(isolate)), "script name");
|
|
34
|
+
|
|
35
|
+
auto same = new dd::CodeEventRecord(
|
|
36
|
+
isolate, 1234, 0, 5678, 1, 2, "a", "b", "c");
|
|
37
|
+
same->SetScriptId(123);
|
|
38
|
+
t.ok(record->Equal(same), "should be equal to itself");
|
|
39
|
+
|
|
40
|
+
using TestPair = std::pair<dd::CodeEventRecord*, dd::CodeEventRecord*>;
|
|
41
|
+
std::unordered_map<std::string, TestPair> non_matching = {
|
|
42
|
+
{"id",
|
|
43
|
+
{new dd::CodeEventRecord(isolate, 1, 1, 1, 1, 1, "a", "a", "a"),
|
|
44
|
+
new dd::CodeEventRecord(isolate, 1, 1, 1, 1, 1, "a", "a", "a")}},
|
|
45
|
+
{"address",
|
|
46
|
+
{new dd::CodeEventRecord(isolate, 1, 1, 1, 1, 1, "a", "a", "a"),
|
|
47
|
+
new dd::CodeEventRecord(isolate, 2, 1, 1, 1, 1, "a", "a", "a")}},
|
|
48
|
+
{"previousAddress",
|
|
49
|
+
{new dd::CodeEventRecord(isolate, 1, 1, 1, 1, 1, "a", "a", "a"),
|
|
50
|
+
new dd::CodeEventRecord(isolate, 1, 2, 1, 1, 1, "a", "a", "a")}},
|
|
51
|
+
{"size",
|
|
52
|
+
{new dd::CodeEventRecord(isolate, 1, 1, 1, 1, 1, "a", "a", "a"),
|
|
53
|
+
new dd::CodeEventRecord(isolate, 1, 1, 2, 1, 1, "a", "a", "a")}},
|
|
54
|
+
{"line",
|
|
55
|
+
{new dd::CodeEventRecord(isolate, 1, 1, 1, 1, 1, "a", "a", "a"),
|
|
56
|
+
new dd::CodeEventRecord(isolate, 1, 1, 1, 2, 1, "a", "a", "a")}},
|
|
57
|
+
{"column",
|
|
58
|
+
{new dd::CodeEventRecord(isolate, 1, 1, 1, 1, 1, "a", "a", "a"),
|
|
59
|
+
new dd::CodeEventRecord(isolate, 1, 1, 1, 1, 2, "a", "a", "a")}},
|
|
60
|
+
{"comment",
|
|
61
|
+
{new dd::CodeEventRecord(isolate, 1, 1, 1, 1, 1, "a", "a", "a"),
|
|
62
|
+
new dd::CodeEventRecord(isolate, 1, 1, 1, 1, 1, "b", "a", "a")}},
|
|
63
|
+
{"functionName",
|
|
64
|
+
{new dd::CodeEventRecord(isolate, 1, 1, 1, 1, 1, "a", "a", "a"),
|
|
65
|
+
new dd::CodeEventRecord(isolate, 1, 1, 1, 1, 1, "a", "b", "a")}},
|
|
66
|
+
{"scriptName",
|
|
67
|
+
{new dd::CodeEventRecord(isolate, 1, 1, 1, 1, 1, "a", "a", "a"),
|
|
68
|
+
new dd::CodeEventRecord(isolate, 1, 1, 1, 1, 1, "a", "a", "b")}}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Script Id is not a constructor argument
|
|
72
|
+
non_matching["id"].second->SetScriptId(123);
|
|
73
|
+
|
|
74
|
+
for (const auto& pair : non_matching) {
|
|
75
|
+
auto name = pair.first;
|
|
76
|
+
auto test = pair.second;
|
|
77
|
+
std::ostringstream s;
|
|
78
|
+
s << "should not have equal " << name;
|
|
79
|
+
t.not_ok(test.first->Equal(test.second), s.str());
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#include "code-map.test.hh"
|
|
2
|
+
#include "../code-map.hh"
|
|
3
|
+
|
|
4
|
+
void test_code_map(Tap& t) {
|
|
5
|
+
t.plan(11);
|
|
6
|
+
|
|
7
|
+
auto isolate = v8::Isolate::GetCurrent();
|
|
8
|
+
|
|
9
|
+
// Lookup in an empty map should return nullptr
|
|
10
|
+
{
|
|
11
|
+
dd::CodeMap map(isolate);
|
|
12
|
+
t.equal(map.Lookup(1234), nullptr, "should not find record in empty map");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
auto record = std::make_shared<dd::CodeEventRecord>(
|
|
16
|
+
isolate, 1234, 0, 5678, 1, 2, "fn");
|
|
17
|
+
|
|
18
|
+
// Lookup with record at matching address should return record
|
|
19
|
+
{
|
|
20
|
+
dd::CodeMap map(isolate, {
|
|
21
|
+
{ 1234, record }
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
t.ok(record->Equal(map.Lookup(1234).get()), "should find record by exact address");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Lookup with address in size range of matching record should return record
|
|
28
|
+
{
|
|
29
|
+
dd::CodeMap map(isolate, {
|
|
30
|
+
{ 1234, record }
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
t.ok(record->Equal(map.Lookup(2000).get()), "should find record in size range");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Lookup with address outside size range should return nullptr
|
|
37
|
+
{
|
|
38
|
+
dd::CodeMap map(isolate, {
|
|
39
|
+
{ 1234, record }
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
t.equal(map.Lookup(1000), nullptr, "should not find record below size range");
|
|
43
|
+
t.equal(map.Lookup(9001), nullptr, "should not find record above size range");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Add a new record
|
|
47
|
+
{
|
|
48
|
+
dd::CodeMap map(isolate);
|
|
49
|
+
map.Add(1234, record);
|
|
50
|
+
|
|
51
|
+
t.ok(record->Equal(map.Lookup(1234).get()), "should find record after added");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Remove an existing record
|
|
55
|
+
{
|
|
56
|
+
dd::CodeMap map(isolate, {
|
|
57
|
+
{ 1234, record }
|
|
58
|
+
});
|
|
59
|
+
map.Remove(1234);
|
|
60
|
+
|
|
61
|
+
t.ok(!map.Lookup(1234), "should not find record after removal");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
{
|
|
65
|
+
dd::CodeMap map(isolate);
|
|
66
|
+
t.equal((int)map.Entries().size(), 0, "should be empty before enabling");
|
|
67
|
+
|
|
68
|
+
map.Enable();
|
|
69
|
+
t.ok((int)map.Entries().size() > 0, "should not be empty after enabled");
|
|
70
|
+
|
|
71
|
+
map.Disable();
|
|
72
|
+
t.equal((int)map.Entries().size(), 0, "should be empty after disabling");
|
|
73
|
+
|
|
74
|
+
map.Enable();
|
|
75
|
+
t.ok((int)map.Entries().size() > 0, "should refill if enabled again");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#include "cpu-time.test.hh"
|
|
2
|
+
#include "../cpu-time.hh"
|
|
3
|
+
|
|
4
|
+
void test_cpu_time(Tap& t) {
|
|
5
|
+
t.plan(3);
|
|
6
|
+
|
|
7
|
+
dd::CpuTime cpu_time({
|
|
8
|
+
2, // tv_sec
|
|
9
|
+
1, // tv_nsec
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
int64_t diff = cpu_time.Diff({
|
|
13
|
+
4, // tv_sec
|
|
14
|
+
3, // tv_nsec
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
t.equal(diff, 2000000002, "should compute time diff correctly");
|
|
18
|
+
|
|
19
|
+
struct timespec now = cpu_time.Now();
|
|
20
|
+
t.ok(
|
|
21
|
+
now.tv_nsec > 0 || now.tv_sec > 0,
|
|
22
|
+
"should get the current cpu time"
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
struct timespec now2 = cpu_time.Now();
|
|
26
|
+
t.ok(
|
|
27
|
+
now2.tv_nsec >= now.tv_nsec && now2.tv_sec >= now.tv_sec,
|
|
28
|
+
"should have current time after previous check"
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#include "location.test.hh"
|
|
2
|
+
#include "../location.hh"
|
|
3
|
+
|
|
4
|
+
void test_location(Tap& t) {
|
|
5
|
+
t.plan(9);
|
|
6
|
+
|
|
7
|
+
auto isolate = v8::Isolate::GetCurrent();
|
|
8
|
+
|
|
9
|
+
auto record = std::make_shared<dd::CodeEventRecord>(
|
|
10
|
+
isolate, 1234, 0, 5678, 1, 2, "a", "b", "c");
|
|
11
|
+
record->SetScriptId(123);
|
|
12
|
+
|
|
13
|
+
auto obj = dd::Location::New(isolate, record)->handle();
|
|
14
|
+
|
|
15
|
+
// Type helpers
|
|
16
|
+
auto Get = [isolate](v8::Local<v8::Object> obj, std::string key)
|
|
17
|
+
-> v8::Local<v8::Value> {
|
|
18
|
+
auto context = isolate->GetCurrentContext();
|
|
19
|
+
return obj->Get(context, Nan::New(key).ToLocalChecked()).ToLocalChecked();
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
auto Str = [isolate](v8::Local<v8::Value> val) -> std::string {
|
|
23
|
+
return *v8::String::Utf8Value(isolate, val);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
auto Int = [](v8::Local<v8::Value> val) -> int64_t {
|
|
27
|
+
return val.As<v8::Integer>()->Value();
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
t.equal(123, Int(Get(obj, "scriptId")), "script id");
|
|
31
|
+
t.equal(1234, Int(Get(obj, "address")), "address");
|
|
32
|
+
t.equal(0, Int(Get(obj, "previousAddress")), "previous address");
|
|
33
|
+
t.equal(5678, Int(Get(obj, "size")), "size");
|
|
34
|
+
t.equal(1, Int(Get(obj, "line")), "line");
|
|
35
|
+
t.equal(2, Int(Get(obj, "column")), "column");
|
|
36
|
+
t.equal("a", Str(Get(obj, "comment")), "comment");
|
|
37
|
+
t.equal("b", Str(Get(obj, "functionName")), "function name");
|
|
38
|
+
t.equal("c", Str(Get(obj, "scriptName")), "script name");
|
|
39
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#include "cpu.test.hh"
|
|
2
|
+
#include "../../profilers/cpu.hh"
|
|
3
|
+
#include "../../location.hh"
|
|
4
|
+
|
|
5
|
+
void test_labels(Tap& t) {
|
|
6
|
+
t.plan(2);
|
|
7
|
+
|
|
8
|
+
auto context = Nan::GetCurrentContext();
|
|
9
|
+
dd::CpuProfiler cpu;
|
|
10
|
+
|
|
11
|
+
t.ok(cpu.GetLabels()->IsUndefined(),
|
|
12
|
+
"should be undefined before setting");
|
|
13
|
+
|
|
14
|
+
auto labels = Nan::New<v8::Number>(1);
|
|
15
|
+
cpu.SetLabels(labels);
|
|
16
|
+
|
|
17
|
+
t.ok(cpu.GetLabels()->Equals(context, labels).ToChecked(),
|
|
18
|
+
"should match given labels value after setting");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
void test_samples(Tap& t) {
|
|
22
|
+
t.plan(9);
|
|
23
|
+
|
|
24
|
+
auto isolate = v8::Isolate::GetCurrent();
|
|
25
|
+
|
|
26
|
+
dd::CpuProfiler cpu;
|
|
27
|
+
|
|
28
|
+
// Empty state
|
|
29
|
+
t.equal(0U, cpu.GetSampleCount(),
|
|
30
|
+
"no processed samples before capture");
|
|
31
|
+
t.ok(!cpu.GetLastSample(),
|
|
32
|
+
"no unprocessed sample after capture");
|
|
33
|
+
|
|
34
|
+
// Set labels to verify they get attached to captured samples
|
|
35
|
+
auto labels = Nan::New<v8::Number>(1);
|
|
36
|
+
cpu.SetLabels(labels);
|
|
37
|
+
cpu.CaptureSample(isolate);
|
|
38
|
+
|
|
39
|
+
t.equal(0U, cpu.GetSampleCount(),
|
|
40
|
+
"no processed samples after capture");
|
|
41
|
+
t.ok(cpu.GetLastSample(),
|
|
42
|
+
"has unprocessed sample after capture");
|
|
43
|
+
t.equal(labels, cpu.GetLastSample()->GetLabels(isolate),
|
|
44
|
+
"should have given labels on unprocessed sample after capture");
|
|
45
|
+
|
|
46
|
+
// Make a synthetic sample to set as the "last sample"
|
|
47
|
+
auto label_wrap = std::make_shared<dd::LabelWrap>(labels);
|
|
48
|
+
std::vector<uintptr_t> frames = {1234};
|
|
49
|
+
uint64_t cpu_time = 12345;
|
|
50
|
+
|
|
51
|
+
std::unique_ptr<dd::Sample> sample(
|
|
52
|
+
new dd::Sample(isolate, label_wrap, frames, cpu_time));
|
|
53
|
+
|
|
54
|
+
auto record = std::make_shared<dd::CodeEventRecord>(
|
|
55
|
+
isolate, 1234, 0, 5678, 1, 2, "fnA");
|
|
56
|
+
|
|
57
|
+
auto map = dd::CodeMap::For(isolate);
|
|
58
|
+
map->Clear();
|
|
59
|
+
map->Add(1234, record);
|
|
60
|
+
|
|
61
|
+
cpu.SetLastSample(std::move(sample));
|
|
62
|
+
cpu.ProcessSample();
|
|
63
|
+
|
|
64
|
+
t.equal(1U, cpu.GetSampleCount(),
|
|
65
|
+
"has processed sample after capture/process");
|
|
66
|
+
|
|
67
|
+
auto samples = cpu.GetSamples();
|
|
68
|
+
t.equal(1U, samples->Length(),
|
|
69
|
+
"should have one processed sample in samples array");
|
|
70
|
+
|
|
71
|
+
auto firstSample = Nan::Get(samples, 0).ToLocalChecked().As<v8::Object>();
|
|
72
|
+
auto locations = Nan::Get(firstSample, Nan::New("locations").ToLocalChecked())
|
|
73
|
+
.ToLocalChecked().As<v8::Array>();
|
|
74
|
+
t.equal(1U, locations->Length(),
|
|
75
|
+
"should have one symbolized stack frame");
|
|
76
|
+
|
|
77
|
+
auto location = Nan::ObjectWrap::Unwrap<dd::Location>(
|
|
78
|
+
Nan::Get(locations, 0).ToLocalChecked().As<v8::Object>());
|
|
79
|
+
t.equal(location->GetCodeEventRecord(), record,
|
|
80
|
+
"symbolization of processed sample should match expected code record");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
void test_profilers_cpu_profiler(Tap& t) {
|
|
84
|
+
t.plan(2);
|
|
85
|
+
|
|
86
|
+
t.test("labels", test_labels);
|
|
87
|
+
t.test("samples", test_samples);
|
|
88
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#include <sstream>
|
|
2
|
+
|
|
3
|
+
#include "sample.test.hh"
|
|
4
|
+
#include "../sample.hh"
|
|
5
|
+
|
|
6
|
+
#include "../location.hh"
|
|
7
|
+
|
|
8
|
+
void test_locations(Tap& t, v8::MaybeLocal<v8::Value> maybe_locations,
|
|
9
|
+
std::vector<uintptr_t> frames,
|
|
10
|
+
std::shared_ptr<dd::CodeMap> map) {
|
|
11
|
+
size_t n = frames.size();
|
|
12
|
+
t.plan(n + 3);
|
|
13
|
+
|
|
14
|
+
v8::Local<v8::Value> locations_value;
|
|
15
|
+
t.ok(
|
|
16
|
+
maybe_locations.ToLocal(&locations_value),
|
|
17
|
+
"location set should not be empty"
|
|
18
|
+
);
|
|
19
|
+
t.ok(
|
|
20
|
+
locations_value->IsArray(),
|
|
21
|
+
"location set should be an array"
|
|
22
|
+
);
|
|
23
|
+
v8::Local<v8::Array> locations = locations_value.As<v8::Array>();
|
|
24
|
+
t.equal(
|
|
25
|
+
(uint32_t) n,
|
|
26
|
+
locations->Length(),
|
|
27
|
+
"length should match the number of frames"
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
for (size_t i = 0; i < n; i++) {
|
|
31
|
+
std::ostringstream name("location #", std::ios_base::ate);
|
|
32
|
+
name << i;
|
|
33
|
+
|
|
34
|
+
auto record = map->Lookup(frames[n - i - 1]);
|
|
35
|
+
auto location = Nan::Get(locations, i)
|
|
36
|
+
.ToLocalChecked()
|
|
37
|
+
.As<v8::Object>();
|
|
38
|
+
|
|
39
|
+
auto wrap = Nan::ObjectWrap::Unwrap<dd::Location>(location);
|
|
40
|
+
|
|
41
|
+
t.equal(record, wrap->GetCodeEventRecord(), name.str());
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
void test_sample_to_object(Tap& t, v8::MaybeLocal<v8::Value> maybe_sample,
|
|
46
|
+
std::vector<uintptr_t> frames,
|
|
47
|
+
std::shared_ptr<dd::CodeMap> map,
|
|
48
|
+
v8::Local<v8::Value> labels,
|
|
49
|
+
uint64_t cpu_time) {
|
|
50
|
+
t.plan(4);
|
|
51
|
+
auto isolate = v8::Isolate::GetCurrent();
|
|
52
|
+
auto context = isolate->GetCurrentContext();
|
|
53
|
+
|
|
54
|
+
auto propIs = [=](v8::Local<v8::Object> object, std::string name,
|
|
55
|
+
v8::Local<v8::Value> value) -> bool {
|
|
56
|
+
auto key = Nan::New(name).ToLocalChecked();
|
|
57
|
+
auto prop = object->Get(context, key).ToLocalChecked();
|
|
58
|
+
return prop->Equals(context, value).ToChecked();
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
v8::Local<v8::Value> sample_value;
|
|
62
|
+
t.ok(
|
|
63
|
+
maybe_sample.ToLocal(&sample_value),
|
|
64
|
+
"should unwrap sample object"
|
|
65
|
+
);
|
|
66
|
+
v8::Local<v8::Object> sample_object = sample_value.As<v8::Object>();
|
|
67
|
+
t.ok(
|
|
68
|
+
propIs(sample_object, "labels", labels),
|
|
69
|
+
"should have expected labels"
|
|
70
|
+
);
|
|
71
|
+
t.ok(
|
|
72
|
+
propIs(sample_object, "cpuTime", Nan::New<v8::Number>(cpu_time)),
|
|
73
|
+
"should have expected cpuTime"
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
auto locations = sample_object->Get(context,
|
|
77
|
+
Nan::New("locations").ToLocalChecked());
|
|
78
|
+
|
|
79
|
+
t.test("sample.locations", [=](Tap& t) {
|
|
80
|
+
test_locations(t, locations, frames, map);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
void test_sample(Tap& t) {
|
|
85
|
+
t.plan(5);
|
|
86
|
+
|
|
87
|
+
auto isolate = v8::Isolate::GetCurrent();
|
|
88
|
+
auto context = isolate->GetCurrentContext();
|
|
89
|
+
|
|
90
|
+
auto labels = v8::Number::New(isolate, 9876);
|
|
91
|
+
auto label_wrap = std::make_shared<dd::LabelWrap>(labels);
|
|
92
|
+
std::vector<uintptr_t> frames = {1234, 2345};
|
|
93
|
+
uint64_t cpu_time = 12345;
|
|
94
|
+
|
|
95
|
+
dd::Sample* sample = new dd::Sample(isolate, label_wrap, frames, cpu_time);
|
|
96
|
+
|
|
97
|
+
t.ok(sample->GetLabels(isolate)->Equals(context, labels).ToChecked(),
|
|
98
|
+
"sample->Labels() should return supplied labels");
|
|
99
|
+
t.equal(sample->GetFrames(), frames,
|
|
100
|
+
"sample->GetFrames() should return supplied frames");
|
|
101
|
+
|
|
102
|
+
// Before symbolization, Locations() and ToObject() should be empty
|
|
103
|
+
t.equal(0u, sample->GetLocations(isolate)->Length(),
|
|
104
|
+
"location set should be empty before symbolizing");
|
|
105
|
+
|
|
106
|
+
// Do symbolization
|
|
107
|
+
auto recordA = std::make_shared<dd::CodeEventRecord>(
|
|
108
|
+
isolate, 1234, 0, 5678, 1, 2, "fnA");
|
|
109
|
+
auto recordB = std::make_shared<dd::CodeEventRecord>(
|
|
110
|
+
isolate, 2345, 0, 5678, 3, 4, "fnB");
|
|
111
|
+
|
|
112
|
+
auto map = dd::CodeMap::For(isolate);
|
|
113
|
+
map->Clear();
|
|
114
|
+
map->Add(1234, recordA);
|
|
115
|
+
map->Add(2345, recordB);
|
|
116
|
+
|
|
117
|
+
sample->Symbolize(map);
|
|
118
|
+
|
|
119
|
+
// After symbolization, Locations() should return a location array
|
|
120
|
+
t.test("sample->GetLocations()", [=](Tap& t) {
|
|
121
|
+
test_locations(t, sample->GetLocations(isolate), frames, map);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// After symbolization, ToObject() should return a valid sample object
|
|
125
|
+
t.test("sample->ToObject()", [=](Tap& t) {
|
|
126
|
+
test_sample_to_object(t, sample->ToObject(isolate), frames, map, labels, cpu_time);
|
|
127
|
+
});
|
|
128
|
+
}
|