@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.
Files changed (106) hide show
  1. package/binding.gyp +130 -31
  2. package/bindings/binding.cc +13 -0
  3. package/bindings/code-event-record.cc +115 -0
  4. package/bindings/code-event-record.hh +67 -0
  5. package/bindings/code-map.cc +112 -0
  6. package/bindings/code-map.hh +47 -0
  7. package/bindings/cpu-time.cc +89 -0
  8. package/bindings/cpu-time.hh +35 -0
  9. package/bindings/location.cc +117 -0
  10. package/bindings/location.hh +43 -0
  11. package/bindings/per-isolate-data.cc +42 -0
  12. package/bindings/per-isolate-data.hh +26 -0
  13. package/bindings/profilers/cpu.cc +285 -0
  14. package/bindings/profilers/cpu.hh +78 -0
  15. package/bindings/profilers/heap.cc +112 -0
  16. package/bindings/profilers/heap.hh +40 -0
  17. package/bindings/profilers/wall.cc +306 -0
  18. package/bindings/profilers/wall.hh +28 -0
  19. package/bindings/sample.cc +160 -0
  20. package/bindings/sample.hh +48 -0
  21. package/bindings/test/binding.cc +59 -0
  22. package/bindings/test/code-event-record.test.cc +81 -0
  23. package/bindings/test/code-event-record.test.hh +5 -0
  24. package/bindings/test/code-map.test.cc +77 -0
  25. package/bindings/test/code-map.test.hh +5 -0
  26. package/bindings/test/cpu-time.test.cc +30 -0
  27. package/bindings/test/cpu-time.test.hh +5 -0
  28. package/bindings/test/location.test.cc +39 -0
  29. package/bindings/test/location.test.hh +5 -0
  30. package/bindings/test/profilers/cpu.test.cc +88 -0
  31. package/bindings/test/profilers/cpu.test.hh +5 -0
  32. package/bindings/test/sample.test.cc +128 -0
  33. package/bindings/test/sample.test.hh +5 -0
  34. package/bindings/test/tap.h +830 -0
  35. package/bindings/wrap.hh +20 -0
  36. package/out/src/cpu-profiler-bindings.d.ts +1 -0
  37. package/out/src/cpu-profiler-bindings.js +23 -0
  38. package/out/src/cpu-profiler-bindings.js.map +1 -0
  39. package/out/src/cpu-profiler.d.ts +19 -0
  40. package/out/src/cpu-profiler.js +94 -0
  41. package/out/src/cpu-profiler.js.map +1 -0
  42. package/out/src/index.d.ts +2 -0
  43. package/out/src/index.js +3 -1
  44. package/out/src/index.js.map +1 -1
  45. package/out/src/profile-encoder.js +3 -14
  46. package/out/src/profile-encoder.js.map +1 -1
  47. package/out/src/profile-serializer.d.ts +9 -1
  48. package/out/src/profile-serializer.js +73 -1
  49. package/out/src/profile-serializer.js.map +1 -1
  50. package/out/src/sourcemapper/sourcemapper.js +81 -88
  51. package/out/src/sourcemapper/sourcemapper.js.map +1 -1
  52. package/out/src/time-profiler.d.ts +1 -1
  53. package/out/src/time-profiler.js +40 -34
  54. package/out/src/time-profiler.js.map +1 -1
  55. package/out/src/v8-types.d.ts +38 -0
  56. package/out/third_party/cloud-debug-nodejs/src/agent/io/scanner.js +6 -17
  57. package/out/third_party/cloud-debug-nodejs/src/agent/io/scanner.js.map +1 -1
  58. package/package-lock.json +5627 -0
  59. package/package.json +12 -14
  60. package/prebuilds/darwin-arm64/node-102.node +0 -0
  61. package/prebuilds/darwin-arm64/node-108.node +0 -0
  62. package/prebuilds/darwin-arm64/node-72.node +0 -0
  63. package/prebuilds/darwin-arm64/node-79.node +0 -0
  64. package/prebuilds/darwin-arm64/node-83.node +0 -0
  65. package/prebuilds/darwin-arm64/node-88.node +0 -0
  66. package/prebuilds/darwin-arm64/node-93.node +0 -0
  67. package/prebuilds/darwin-ia32/node-102.node +0 -0
  68. package/prebuilds/darwin-ia32/node-108.node +0 -0
  69. package/prebuilds/darwin-ia32/node-72.node +0 -0
  70. package/prebuilds/darwin-ia32/node-79.node +0 -0
  71. package/prebuilds/darwin-ia32/node-83.node +0 -0
  72. package/prebuilds/darwin-ia32/node-88.node +0 -0
  73. package/prebuilds/darwin-ia32/node-93.node +0 -0
  74. package/prebuilds/darwin-x64/node-102.node +0 -0
  75. package/prebuilds/darwin-x64/node-108.node +0 -0
  76. package/prebuilds/darwin-x64/node-72.node +0 -0
  77. package/prebuilds/darwin-x64/node-79.node +0 -0
  78. package/prebuilds/darwin-x64/node-83.node +0 -0
  79. package/prebuilds/darwin-x64/node-88.node +0 -0
  80. package/prebuilds/darwin-x64/node-93.node +0 -0
  81. package/prebuilds/linux-ia32/node-72.node +0 -0
  82. package/prebuilds/linux-ia32/node-79.node +0 -0
  83. package/prebuilds/linux-x64/node-102.node +0 -0
  84. package/prebuilds/linux-x64/node-108.node +0 -0
  85. package/prebuilds/linux-x64/node-72.node +0 -0
  86. package/prebuilds/linux-x64/node-79.node +0 -0
  87. package/prebuilds/linux-x64/node-83.node +0 -0
  88. package/prebuilds/linux-x64/node-88.node +0 -0
  89. package/prebuilds/linux-x64/node-93.node +0 -0
  90. package/prebuilds/win32-ia32/node-102.node +0 -0
  91. package/prebuilds/win32-ia32/node-72.node +0 -0
  92. package/prebuilds/win32-ia32/node-79.node +0 -0
  93. package/prebuilds/win32-ia32/node-83.node +0 -0
  94. package/prebuilds/win32-ia32/node-88.node +0 -0
  95. package/prebuilds/win32-ia32/node-93.node +0 -0
  96. package/prebuilds/win32-x64/node-102.node +0 -0
  97. package/prebuilds/win32-x64/node-108.node +0 -0
  98. package/prebuilds/win32-x64/node-72.node +0 -0
  99. package/prebuilds/win32-x64/node-79.node +0 -0
  100. package/prebuilds/win32-x64/node-83.node +0 -0
  101. package/prebuilds/win32-x64/node-88.node +0 -0
  102. package/prebuilds/win32-x64/node-93.node +0 -0
  103. package/scripts/require-package-json.js +11 -11
  104. package/scripts/should_rebuild.js +3 -3
  105. package/bindings/profiler.cc +0 -384
  106. 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,5 @@
1
+ #pragma once
2
+
3
+ #include "tap.h"
4
+
5
+ void test_code_event_record(Tap& t);
@@ -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,5 @@
1
+ #pragma once
2
+
3
+ #include "tap.h"
4
+
5
+ void test_code_map(Tap& t);
@@ -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,5 @@
1
+ #pragma once
2
+
3
+ #include "tap.h"
4
+
5
+ void test_cpu_time(Tap& t);
@@ -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,5 @@
1
+ #pragma once
2
+
3
+ #include "tap.h"
4
+
5
+ void test_location(Tap& t);
@@ -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,5 @@
1
+ #pragma once
2
+
3
+ #include "../tap.h"
4
+
5
+ void test_profilers_cpu_profiler(Tap& t);
@@ -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
+ }
@@ -0,0 +1,5 @@
1
+ #pragma once
2
+
3
+ #include "tap.h"
4
+
5
+ void test_sample(Tap& t);