Skip to content

Commit 232bea8

Browse files
authored
Revert lazy profile-tree changes to mitigate CpuProfile::Delete crash (#329)
* Revert "use stop and collect on time profiler (#305)" This reverts commit 85f2457. * Revert "Switch heap profiling to use lazy allocation profile method by default (#281)" This reverts commit fb3d75d. * Touchup for TS6 changes introduced after the reverted commits. Specifically enforcing TS2883 under `composite: true`: any function whose inferred return type names a non-imported type must be annotated explicitly.
1 parent f8abe98 commit 232bea8

16 files changed

Lines changed: 730 additions & 81 deletions

README.md

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,10 @@ Install [`pprof`][npm-url] with `npm` or add to your `package.json`.
9797
pprof -http=: heap.pb.gz
9898
```
9999

100-
* Collecting a heap profile with V8 allocation profile format:
100+
* Collecting a heap profile with V8 allocation profile format:
101101
```javascript
102-
const profile = pprof.heap.v8Profile(pprof.heap.convertProfile);
102+
const profile = await pprof.heap.v8Profile();
103103
```
104-
`v8Profile` accepts a callback and returns its result. Allocation nodes
105-
are only valid during the callback, so copy/transform what you need
106-
before returning. `heap.convertProfile` performs that conversion during
107-
the callback, and `heap.profile()` uses it under the hood.
108104

109105
[build-image]: https://github.com/Datadog/pprof-nodejs/actions/workflows/build.yml/badge.svg?branch=main
110106
[build-url]: https://github.com/Datadog/pprof-nodejs/actions/workflows/build.yml

bindings/profilers/heap.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,23 @@ NAN_METHOD(HeapProfiler::StopSamplingHeapProfiler) {
488488
}
489489
}
490490

491+
// Signature:
492+
// getAllocationProfile(): AllocationProfileNode
493+
NAN_METHOD(HeapProfiler::GetAllocationProfile) {
494+
auto isolate = info.GetIsolate();
495+
std::unique_ptr<v8::AllocationProfile> profile(
496+
isolate->GetHeapProfiler()->GetAllocationProfile());
497+
if (!profile) {
498+
return Nan::ThrowError("Heap profiler is not enabled.");
499+
}
500+
v8::AllocationProfile::Node* root = profile->GetRootNode();
501+
auto state = PerIsolateData::For(isolate)->GetHeapProfilerState();
502+
if (state) {
503+
state->OnNewProfile();
504+
}
505+
info.GetReturnValue().Set(TranslateAllocationProfile(root));
506+
}
507+
491508
// mapAllocationProfile(callback): callback result
492509
NAN_METHOD(HeapProfiler::MapAllocationProfile) {
493510
if (info.Length() < 1 || !info[0]->IsFunction()) {
@@ -579,6 +596,7 @@ NAN_MODULE_INIT(HeapProfiler::Init) {
579596
heapProfiler, "startSamplingHeapProfiler", StartSamplingHeapProfiler);
580597
Nan::SetMethod(
581598
heapProfiler, "stopSamplingHeapProfiler", StopSamplingHeapProfiler);
599+
Nan::SetMethod(heapProfiler, "getAllocationProfile", GetAllocationProfile);
582600
Nan::SetMethod(heapProfiler, "mapAllocationProfile", MapAllocationProfile);
583601
Nan::SetMethod(heapProfiler, "monitorOutOfMemory", MonitorOutOfMemory);
584602
Nan::Set(target,

bindings/profilers/heap.hh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ class HeapProfiler {
3030
// stopSamplingHeapProfiler()
3131
static NAN_METHOD(StopSamplingHeapProfiler);
3232

33+
// Signature:
34+
// getAllocationProfile(): AllocationProfileNode
35+
static NAN_METHOD(GetAllocationProfile);
36+
3337
// Signature:
3438
// mapAllocationProfile(callback): callback result
3539
static NAN_METHOD(MapAllocationProfile);

bindings/profilers/wall.cc

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,28 @@ v8::ProfilerId WallProfiler::StartInternal() {
929929
return result.id;
930930
}
931931

932+
NAN_METHOD(WallProfiler::Stop) {
933+
if (info.Length() != 1) {
934+
return Nan::ThrowTypeError("Stop must have one argument.");
935+
}
936+
if (!info[0]->IsBoolean()) {
937+
return Nan::ThrowTypeError("Restart must be a boolean.");
938+
}
939+
940+
bool restart = info[0].As<Boolean>()->Value();
941+
942+
WallProfiler* wallProfiler =
943+
Nan::ObjectWrap::Unwrap<WallProfiler>(info.This());
944+
945+
v8::Local<v8::Value> profile;
946+
auto err = wallProfiler->StopImpl(restart, profile);
947+
948+
if (!err.success) {
949+
return Nan::ThrowTypeError(err.msg.c_str());
950+
}
951+
info.GetReturnValue().Set(profile);
952+
}
953+
932954
// stopAndCollect(restart, callback): callback result
933955
NAN_METHOD(WallProfiler::StopAndCollect) {
934956
if (info.Length() != 2) {
@@ -1087,6 +1109,20 @@ Result WallProfiler::StopCore(bool restart, ProfileBuilder&& buildProfile) {
10871109
return {};
10881110
}
10891111

1112+
Result WallProfiler::StopImpl(bool restart, v8::Local<v8::Value>& profile) {
1113+
return StopCore(restart,
1114+
[&](const v8::CpuProfile* v8_profile,
1115+
bool hasCpuTime,
1116+
int64_t nonJSThreadsCpuTime,
1117+
ContextsByNode* contextsByNodePtr) {
1118+
profile = TranslateTimeProfile(v8_profile,
1119+
includeLines_,
1120+
contextsByNodePtr,
1121+
hasCpuTime,
1122+
nonJSThreadsCpuTime);
1123+
});
1124+
}
1125+
10901126
Result WallProfiler::StopAndCollectImpl(bool restart,
10911127
v8::Local<v8::Function> callback,
10921128
v8::Local<v8::Value>& result) {
@@ -1121,6 +1157,7 @@ NAN_MODULE_INIT(WallProfiler::Init) {
11211157
SetContext);
11221158

11231159
Nan::SetPrototypeMethod(tpl, "start", Start);
1160+
Nan::SetPrototypeMethod(tpl, "stop", Stop);
11241161
Nan::SetPrototypeMethod(tpl, "stopAndCollect", StopAndCollect);
11251162
Nan::SetPrototypeMethod(tpl, "dispose", Dispose);
11261163
Nan::SetPrototypeMethod(tpl,

bindings/profilers/wall.hh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ class WallProfiler : public Nan::ObjectWrap {
157157
v8::ProfilerId StartInternal();
158158
template <typename ProfileBuilder>
159159
Result StopCore(bool restart, ProfileBuilder&& buildProfile);
160+
Result StopImpl(bool restart, v8::Local<v8::Value>& profile);
160161
Result StopAndCollectImpl(bool restart,
161162
v8::Local<v8::Function> callback,
162163
v8::Local<v8::Value>& result);
@@ -188,6 +189,7 @@ class WallProfiler : public Nan::ObjectWrap {
188189

189190
static NAN_METHOD(New);
190191
static NAN_METHOD(Start);
192+
static NAN_METHOD(Stop);
191193
static NAN_METHOD(StopAndCollect);
192194
static NAN_METHOD(V8ProfilerStuckEventLoopDetected);
193195
static NAN_METHOD(Dispose);

bindings/translate-heap-profile.cc

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,30 @@ class HeapProfileTranslator : ProfileTranslator {
4141
#undef X
4242

4343
public:
44+
v8::Local<v8::Value> TranslateAllocationProfile(
45+
v8::AllocationProfile::Node* node) {
46+
v8::Local<v8::Array> children = NewArray(node->children.size());
47+
for (size_t i = 0; i < node->children.size(); i++) {
48+
Set(children, i, TranslateAllocationProfile(node->children[i]));
49+
}
50+
51+
v8::Local<v8::Array> allocations = NewArray(node->allocations.size());
52+
for (size_t i = 0; i < node->allocations.size(); i++) {
53+
auto alloc = node->allocations[i];
54+
Set(allocations,
55+
i,
56+
CreateAllocation(NewNumber(alloc.count), NewNumber(alloc.size)));
57+
}
58+
59+
return CreateNode(node->name,
60+
node->script_name,
61+
NewInteger(node->script_id),
62+
NewInteger(node->line_number),
63+
NewInteger(node->column_number),
64+
children,
65+
allocations);
66+
}
67+
4468
v8::Local<v8::Value> TranslateAllocationProfile(Node* node) {
4569
v8::Local<v8::Array> children = NewArray(node->children.size());
4670
for (size_t i = 0; i < node->children.size(); i++) {
@@ -118,6 +142,11 @@ std::shared_ptr<Node> TranslateAllocationProfileToCpp(
118142
return new_node;
119143
}
120144

145+
v8::Local<v8::Value> TranslateAllocationProfile(
146+
v8::AllocationProfile::Node* node) {
147+
return HeapProfileTranslator().TranslateAllocationProfile(node);
148+
}
149+
121150
v8::Local<v8::Value> TranslateAllocationProfile(Node* node) {
122151
return HeapProfileTranslator().TranslateAllocationProfile(node);
123152
}

bindings/translate-heap-profile.hh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,7 @@ std::shared_ptr<Node> TranslateAllocationProfileToCpp(
3939
v8::AllocationProfile::Node* node);
4040

4141
v8::Local<v8::Value> TranslateAllocationProfile(Node* node);
42+
v8::Local<v8::Value> TranslateAllocationProfile(
43+
v8::AllocationProfile::Node* node);
44+
4245
} // namespace dd

ts/src/heap-profiler-bindings.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ export function stopSamplingHeapProfiler() {
3737
profiler.heapProfiler.stopSamplingHeapProfiler();
3838
}
3939

40+
export function getAllocationProfile(): AllocationProfileNode {
41+
return profiler.heapProfiler.getAllocationProfile();
42+
}
43+
4044
export function mapAllocationProfile<T>(
4145
callback: (root: AllocationProfileNode) => T,
4246
): T {

ts/src/heap-profiler.ts

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import {Profile} from 'pprof-format';
1818

1919
import {
20+
getAllocationProfile,
2021
mapAllocationProfile,
2122
startSamplingHeapProfiler,
2223
stopSamplingHeapProfiler,
@@ -33,6 +34,20 @@ import {isMainThread} from 'worker_threads';
3334
let enabled = false;
3435
let heapIntervalBytes = 0;
3536
let heapStackDepth = 0;
37+
38+
/*
39+
* Collects a heap profile when heapProfiler is enabled. Otherwise throws
40+
* an error.
41+
*
42+
* Data is returned in V8 allocation profile format.
43+
*/
44+
export function v8Profile(): AllocationProfileNode {
45+
if (!enabled) {
46+
throw new Error('Heap profiler is not enabled.');
47+
}
48+
return getAllocationProfile();
49+
}
50+
3651
/**
3752
* Collects a heap profile when heapProfiler is enabled. Otherwise throws
3853
* an error.
@@ -44,13 +59,35 @@ let heapStackDepth = 0;
4459
* @param callback - function to convert the heap profiler to a converted profile
4560
* @returns <T> converted profile
4661
*/
47-
export function v8Profile<T>(callback: (root: AllocationProfileNode) => T): T {
62+
export function v8ProfileV2<T>(
63+
callback: (root: AllocationProfileNode) => T,
64+
): T {
4865
if (!enabled) {
4966
throw new Error('Heap profiler is not enabled.');
5067
}
5168
return mapAllocationProfile(callback);
5269
}
5370

71+
/**
72+
* Collects a profile and returns it serialized in pprof format.
73+
* Throws if heap profiler is not enabled.
74+
*
75+
* @param ignoreSamplePath
76+
* @param sourceMapper
77+
*/
78+
export function profile(
79+
ignoreSamplePath?: string,
80+
sourceMapper?: SourceMapper,
81+
generateLabels?: GenerateAllocationLabelsFunction,
82+
): Profile {
83+
return convertProfile(
84+
v8Profile(),
85+
ignoreSamplePath,
86+
sourceMapper,
87+
generateLabels,
88+
);
89+
}
90+
5491
export function convertProfile(
5592
rootNode: AllocationProfileNode,
5693
ignoreSamplePath?: string,
@@ -93,12 +130,12 @@ export function convertProfile(
93130
* @param sourceMapper
94131
* @param generateLabels
95132
*/
96-
export function profile(
133+
export function profileV2(
97134
ignoreSamplePath?: string,
98135
sourceMapper?: SourceMapper,
99136
generateLabels?: GenerateAllocationLabelsFunction,
100137
): Profile {
101-
return v8Profile(root => {
138+
return v8ProfileV2(root => {
102139
return convertProfile(root, ignoreSamplePath, sourceMapper, generateLabels);
103140
});
104141
}

ts/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export const time = {
3434
profile: timeProfiler.profile,
3535
start: timeProfiler.start,
3636
stop: timeProfiler.stop,
37+
profileV2: timeProfiler.profileV2,
3738
getContext: timeProfiler.getContext,
3839
setContext: timeProfiler.setContext,
3940
runWithContext: timeProfiler.runWithContext,
@@ -49,6 +50,7 @@ export const heap = {
4950
start: heapProfiler.start,
5051
stop: heapProfiler.stop,
5152
profile: heapProfiler.profile,
53+
profileV2: heapProfiler.profileV2,
5254
convertProfile: heapProfiler.convertProfile,
5355
v8Profile: heapProfiler.v8Profile,
5456
monitorOutOfMemory: heapProfiler.monitorOutOfMemory,

0 commit comments

Comments
 (0)