Skip to content

Commit 14b2d4f

Browse files
authored
Reduce memory usage of allocation profiles (#188)
1 parent 784f6f6 commit 14b2d4f

File tree

8 files changed

+245
-111
lines changed

8 files changed

+245
-111
lines changed

binding.gyp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"bindings/profilers/wall.cc",
1717
"bindings/per-isolate-data.cc",
1818
"bindings/thread-cpu-clock.cc",
19+
"bindings/translate-heap-profile.cc",
1920
"bindings/translate-time-profile.cc",
2021
"bindings/binding.cc"
2122
],
@@ -38,6 +39,7 @@
3839
"bindings/profilers/wall.cc",
3940
"bindings/per-isolate-data.cc",
4041
"bindings/thread-cpu-clock.cc",
42+
"bindings/translate-heap-profile.cc",
4143
"bindings/translate-time-profile.cc",
4244
"bindings/test/binding.cc",
4345
],

bindings/contexts.hh

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
#pragma once
1818

19-
#include <nan.h>
2019
#include <v8-profiler.h>
2120
#include <unordered_map>
2221

bindings/profile-translator.hh

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright 2024 Datadog, Inc
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 <v8.h>
18+
19+
namespace dd {
20+
class ProfileTranslator {
21+
v8::Isolate* isolate = v8::Isolate::GetCurrent();
22+
v8::Local<v8::Context> context = isolate->GetCurrentContext();
23+
v8::Local<v8::Array> emptyArray = v8::Array::New(isolate, 0);
24+
25+
protected:
26+
v8::Local<v8::Object> NewObject() { return v8::Object::New(isolate); }
27+
28+
v8::Local<v8::Integer> NewInteger(int x) {
29+
return v8::Integer::New(isolate, x);
30+
}
31+
32+
v8::Local<v8::Boolean> NewBoolean(bool x) {
33+
return v8::Boolean::New(isolate, x);
34+
}
35+
36+
template <typename T>
37+
v8::Local<v8::Number> NewNumber(T x) {
38+
return v8::Number::New(isolate, x);
39+
}
40+
41+
v8::Local<v8::Array> NewArray(int length) {
42+
return length == 0 ? emptyArray : v8::Array::New(isolate, length);
43+
}
44+
45+
v8::Local<v8::String> NewString(const char* str) {
46+
return v8::String::NewFromUtf8(isolate, str).ToLocalChecked();
47+
}
48+
49+
v8::MaybeLocal<v8::Value> Get(v8::Local<v8::Array> arr, uint32_t index) {
50+
return arr->Get(context, index);
51+
}
52+
53+
v8::Maybe<bool> Set(v8::Local<v8::Array> arr,
54+
uint32_t index,
55+
v8::Local<v8::Value> value) {
56+
return arr->Set(context, index, value);
57+
}
58+
59+
v8::Maybe<bool> Set(v8::Local<v8::Object> obj,
60+
v8::Local<v8::Value> key,
61+
v8::Local<v8::Value> value) {
62+
return obj->Set(context, key, value);
63+
}
64+
65+
ProfileTranslator() = default;
66+
};
67+
}; // namespace dd

bindings/profilers/heap.cc

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "defer.hh"
2020
#include "per-isolate-data.hh"
21+
#include "translate-heap-profile.hh"
2122

2223
#include <chrono>
2324
#include <memory>
@@ -482,49 +483,6 @@ size_t NearHeapLimit(void* data,
482483
return new_heap_limit;
483484
}
484485

485-
v8::Local<v8::Value> TranslateAllocationProfile(
486-
v8::AllocationProfile::Node* node) {
487-
v8::Local<v8::Object> js_node = Nan::New<v8::Object>();
488-
489-
Nan::Set(js_node, Nan::New<v8::String>("name").ToLocalChecked(), node->name);
490-
Nan::Set(js_node,
491-
Nan::New<v8::String>("scriptName").ToLocalChecked(),
492-
node->script_name);
493-
Nan::Set(js_node,
494-
Nan::New<v8::String>("scriptId").ToLocalChecked(),
495-
Nan::New<v8::Integer>(node->script_id));
496-
Nan::Set(js_node,
497-
Nan::New<v8::String>("lineNumber").ToLocalChecked(),
498-
Nan::New<v8::Integer>(node->line_number));
499-
Nan::Set(js_node,
500-
Nan::New<v8::String>("columnNumber").ToLocalChecked(),
501-
Nan::New<v8::Integer>(node->column_number));
502-
503-
v8::Local<v8::Array> children = Nan::New<v8::Array>(node->children.size());
504-
for (size_t i = 0; i < node->children.size(); i++) {
505-
Nan::Set(children, i, TranslateAllocationProfile(node->children[i]));
506-
}
507-
Nan::Set(
508-
js_node, Nan::New<v8::String>("children").ToLocalChecked(), children);
509-
v8::Local<v8::Array> allocations =
510-
Nan::New<v8::Array>(node->allocations.size());
511-
for (size_t i = 0; i < node->allocations.size(); i++) {
512-
v8::AllocationProfile::Allocation alloc = node->allocations[i];
513-
v8::Local<v8::Object> js_alloc = Nan::New<v8::Object>();
514-
Nan::Set(js_alloc,
515-
Nan::New<v8::String>("sizeBytes").ToLocalChecked(),
516-
Nan::New<v8::Number>(alloc.size));
517-
Nan::Set(js_alloc,
518-
Nan::New<v8::String>("count").ToLocalChecked(),
519-
Nan::New<v8::Number>(alloc.count));
520-
Nan::Set(allocations, i, js_alloc);
521-
}
522-
Nan::Set(js_node,
523-
Nan::New<v8::String>("allocations").ToLocalChecked(),
524-
allocations);
525-
return js_node;
526-
}
527-
528486
NAN_METHOD(HeapProfiler::StartSamplingHeapProfiler) {
529487
if (info.Length() == 2) {
530488
if (!info[0]->IsUint32()) {

bindings/translate-heap-profile.cc

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* Copyright 2024 Datadog, Inc
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 "translate-heap-profile.hh"
18+
#include "profile-translator.hh"
19+
20+
namespace dd {
21+
22+
namespace {
23+
class HeapProfileTranslator : ProfileTranslator {
24+
#define NODE_FIELDS \
25+
X(name) \
26+
X(scriptName) \
27+
X(scriptId) \
28+
X(lineNumber) \
29+
X(columnNumber) \
30+
X(children) \
31+
X(allocations)
32+
33+
#define ALLOCATION_FIELDS \
34+
X(sizeBytes) \
35+
X(count)
36+
37+
#define X(name) v8::Local<v8::String> str_##name = NewString(#name);
38+
NODE_FIELDS
39+
ALLOCATION_FIELDS
40+
#undef X
41+
42+
public:
43+
v8::Local<v8::Value> TranslateAllocationProfile(
44+
v8::AllocationProfile::Node* node) {
45+
v8::Local<v8::Array> children = NewArray(node->children.size());
46+
for (size_t i = 0; i < node->children.size(); i++) {
47+
Set(children, i, TranslateAllocationProfile(node->children[i]));
48+
}
49+
50+
v8::Local<v8::Array> allocations = NewArray(node->allocations.size());
51+
for (size_t i = 0; i < node->allocations.size(); i++) {
52+
auto alloc = node->allocations[i];
53+
Set(allocations,
54+
i,
55+
CreateAllocation(NewNumber(alloc.size), NewNumber(alloc.count)));
56+
}
57+
58+
return CreateNode(node->name,
59+
node->script_name,
60+
NewInteger(node->script_id),
61+
NewInteger(node->line_number),
62+
NewInteger(node->column_number),
63+
children,
64+
allocations);
65+
}
66+
67+
private:
68+
v8::Local<v8::Object> CreateNode(v8::Local<v8::String> name,
69+
v8::Local<v8::String> scriptName,
70+
v8::Local<v8::Integer> scriptId,
71+
v8::Local<v8::Integer> lineNumber,
72+
v8::Local<v8::Integer> columnNumber,
73+
v8::Local<v8::Array> children,
74+
v8::Local<v8::Array> allocations) {
75+
v8::Local<v8::Object> js_node = NewObject();
76+
#define X(name) Set(js_node, str_##name, name);
77+
NODE_FIELDS
78+
#undef X
79+
#undef NODE_FIELDS
80+
return js_node;
81+
}
82+
83+
v8::Local<v8::Object> CreateAllocation(v8::Local<v8::Number> count,
84+
v8::Local<v8::Number> sizeBytes) {
85+
v8::Local<v8::Object> js_alloc = NewObject();
86+
#define X(name) Set(js_alloc, str_##name, name);
87+
ALLOCATION_FIELDS
88+
#undef X
89+
#undef ALLOCATION_FIELDS
90+
return js_alloc;
91+
}
92+
93+
public:
94+
explicit HeapProfileTranslator() {}
95+
};
96+
} // namespace
97+
98+
v8::Local<v8::Value> TranslateAllocationProfile(
99+
v8::AllocationProfile::Node* node) {
100+
return HeapProfileTranslator().TranslateAllocationProfile(node);
101+
}
102+
103+
} // namespace dd

bindings/translate-heap-profile.hh

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2024 Datadog, Inc
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+
#pragma once
18+
19+
#include <v8-profiler.h>
20+
21+
namespace dd {
22+
23+
v8::Local<v8::Value> TranslateAllocationProfile(
24+
v8::AllocationProfile::Node* node);
25+
26+
} // namespace dd

0 commit comments

Comments
 (0)