Skip to content

Commit 756a34e

Browse files
Matheus Marchinijoyeecheung
Matheus Marchini
authored andcommittedJan 26, 2018
src, test: node internals' postmortem metadata
Before these changes, only V8 added postmortem metadata to Node's binary, limiting the possibilities for debugger's developers to add some features that rely on investigating Node's internal structures. These changes are first steps towards empowering debug tools to navigate Node's internal structures. One example of what can be achieved with this is shown at nodejs/llnode#122 (a command which prints information about handles and requests on the queue for a core dump file). Node postmortem metadata are prefixed with nodedbg_. This also adds tests to validate if all postmortem metadata are calculated correctly, plus some documentation on what is postmortem metadata and a few care to be taken to avoid breaking it. Ref: nodejs/llnode#122 Ref: nodejs/post-mortem#46 PR-URL: #14901 Refs: nodejs/post-mortem#46 Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Joyee Cheung <[email protected]>
1 parent bea5f26 commit 756a34e

11 files changed

+432
-48
lines changed
 

‎doc/guides/node-postmortem-support.md

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Postmortem Support
2+
3+
Postmortem metadata are constants present in the final build which can be used
4+
by debuggers and other tools to navigate through internal structures of software
5+
when analyzing its memory (either on a running process or a core dump). Node
6+
provides this metadata in its builds for V8 and Node internal structures.
7+
8+
9+
### V8 Postmortem metadata
10+
11+
V8 prefixes all postmortem constants with `v8dbg_`, and they allow inspection of
12+
objects on the heap as well as object properties and references. V8 generates
13+
those symbols with a script (`deps/v8/tools/gen-postmortem-metadata.py`), and
14+
Node always includes these constants in the final build.
15+
16+
### Node Debug Symbols
17+
18+
Node prefixes all postmortem constants with `nodedbg_`, and they complement V8
19+
constants by providing ways to inspect Node-specific structures, like
20+
`node::Environment`, `node::BaseObject` and its descendants, classes from
21+
`src/utils.h` and others. Those constants are declared in
22+
`src/node_postmortem_metadata.cc`, and most of them are calculated at compile
23+
time.
24+
25+
#### Calculating offset of class members
26+
27+
Node constants referring to the offset of class members in memory are calculated
28+
at compile time. Because of that, those class members must be at a fixed offset
29+
from the start of the class. That's not a problem in most cases, but it also
30+
means that those members should always come after any templated member on the
31+
class definition.
32+
33+
For example, if we want to add a constant with the offset for
34+
`ReqWrap::req_wrap_queue_`, it should be defined after `ReqWrap::req_`, because
35+
`sizeof(req_)` depends on the type of T, which means the class definition should
36+
be like this:
37+
38+
```c++
39+
template <typename T>
40+
class ReqWrap : public AsyncWrap {
41+
private:
42+
// req_wrap_queue_ comes before any templated member, which places it in a
43+
// fixed offset from the start of the class
44+
ListNode<ReqWrap> req_wrap_queue_;
45+
46+
T req_;
47+
};
48+
```
49+
50+
instead of:
51+
52+
```c++
53+
template <typename T>
54+
class ReqWrap : public AsyncWrap {
55+
private:
56+
T req_;
57+
58+
// req_wrap_queue_ comes after a templated member, which means it won't be in
59+
// a fixed offset from the start of the class
60+
ListNode<ReqWrap> req_wrap_queue_;
61+
};
62+
```
63+
64+
There are also tests on `test/cctest/test_node_postmortem_metadata.cc` to make
65+
sure all Node postmortem metadata are calculated correctly.
66+
67+
## Tools and References
68+
69+
* [llnode](https://github.com/nodejs/llnode): LLDB plugin
70+
* [`mdb_v8`](https://github.com/joyent/mdb_v8): mdb plugin
71+
* [nodejs/post-mortem](https://github.com/nodejs/post-mortem): Node.js
72+
post-mortem working group

‎node.gyp

+3
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@
305305
'src/node_os.cc',
306306
'src/node_platform.cc',
307307
'src/node_perf.cc',
308+
'src/node_postmortem_metadata.cc',
308309
'src/node_serdes.cc',
309310
'src/node_trace_events.cc',
310311
'src/node_url.cc',
@@ -962,13 +963,15 @@
962963
'test/cctest/node_test_fixture.cc',
963964
'test/cctest/test_aliased_buffer.cc',
964965
'test/cctest/test_base64.cc',
966+
'test/cctest/test_node_postmortem_metadata.cc',
965967
'test/cctest/test_environment.cc',
966968
'test/cctest/test_util.cc',
967969
'test/cctest/test_url.cc'
968970
],
969971

970972
'libraries': [
971973
'<(OBJ_PATH)<(OBJ_SEPARATOR)async_wrap.<(OBJ_SUFFIX)',
974+
'<(OBJ_PATH)<(OBJ_SEPARATOR)handle_wrap.<(OBJ_SUFFIX)',
972975
'<(OBJ_PATH)<(OBJ_SEPARATOR)env.<(OBJ_SUFFIX)',
973976
'<(OBJ_PATH)<(OBJ_SEPARATOR)node.<(OBJ_SUFFIX)',
974977
'<(OBJ_PATH)<(OBJ_SEPARATOR)node_buffer.<(OBJ_SUFFIX)',

‎src/base_object.h

+5
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ class BaseObject {
6565
static inline void WeakCallback(
6666
const v8::WeakCallbackInfo<Type>& data);
6767

68+
// persistent_handle_ needs to be at a fixed offset from the start of the
69+
// class because it is used by src/node_postmortem_metadata.cc to calculate
70+
// offsets and generate debug symbols for BaseObject, which assumes that the
71+
// position of members in memory are predictable. For more information please
72+
// refer to `doc/guides/node-postmortem-support.md`
6873
v8::Persistent<v8::Object> persistent_handle_;
6974
Environment* env_;
7075
};

‎src/env.h

+6
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,12 @@ class Environment {
765765
std::unique_ptr<inspector::Agent> inspector_agent_;
766766
#endif
767767

768+
// handle_wrap_queue_ and req_wrap_queue_ needs to be at a fixed offset from
769+
// the start of the class because it is used by
770+
// src/node_postmortem_metadata.cc to calculate offsets and generate debug
771+
// symbols for Environment, which assumes that the position of members in
772+
// memory are predictable. For more information please refer to
773+
// `doc/guides/node-postmortem-support.md`
768774
HandleWrapQueue handle_wrap_queue_;
769775
ReqWrapQueue req_wrap_queue_;
770776
ListHead<HandleCleanup,

‎src/handle_wrap.h

+5
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ class HandleWrap : public AsyncWrap {
8181
friend class Environment;
8282
friend void GetActiveHandles(const v8::FunctionCallbackInfo<v8::Value>&);
8383
static void OnClose(uv_handle_t* handle);
84+
// handle_wrap_queue_ needs to be at a fixed offset from the start of the
85+
// class because it is used by src/node_postmortem_metadata.cc to calculate
86+
// offsets and generate debug symbols for HandleWrap, which assumes that the
87+
// position of members in memory are predictable. For more information please
88+
// refer to `doc/guides/node-postmortem-support.md`
8489
ListNode<HandleWrap> handle_wrap_queue_;
8590
enum { kInitialized, kClosing, kClosingWithCallback, kClosed } state_;
8691
uv_handle_t* const handle_;

‎src/node_postmortem_metadata.cc

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Need to import standard headers before redefining private, otherwise it
2+
// won't compile.
3+
#include <algorithm>
4+
#include <array>
5+
#include <atomic>
6+
#include <bitset>
7+
#include <cctype>
8+
#include <climits>
9+
#include <cmath>
10+
#include <cstdarg>
11+
#include <cstddef>
12+
#include <cstdint>
13+
#include <cstdio>
14+
#include <cstdlib>
15+
#include <cstring>
16+
#include <ctime>
17+
#include <deque>
18+
#include <exception>
19+
#include <forward_list>
20+
#include <fstream>
21+
#include <functional>
22+
#include <iomanip>
23+
#include <iosfwd>
24+
#include <iostream>
25+
#include <istream>
26+
#include <iterator>
27+
#include <limits>
28+
#include <list>
29+
#include <map>
30+
#include <memory>
31+
#include <new>
32+
#include <ostream>
33+
#include <queue>
34+
#include <set>
35+
#include <sstream>
36+
#include <stack>
37+
#include <streambuf>
38+
#include <string>
39+
#include <tuple>
40+
#include <type_traits>
41+
#include <typeinfo>
42+
#include <unordered_map>
43+
#include <unordered_set>
44+
#include <utility>
45+
#include <vector>
46+
47+
namespace node {
48+
// Forward declaration needed before redefining private.
49+
int GenDebugSymbols();
50+
} // namespace node
51+
52+
53+
#define private friend int GenDebugSymbols(); private
54+
55+
#include "env.h"
56+
#include "base_object-inl.h"
57+
#include "handle_wrap.h"
58+
#include "util-inl.h"
59+
#include "req_wrap.h"
60+
#include "v8abbr.h"
61+
62+
#define NODEDBG_SYMBOL(Name) nodedbg_ ## Name
63+
64+
// nodedbg_offset_CLASS__MEMBER__TYPE: Describes the offset to a class member.
65+
#define NODEDBG_OFFSET(Class, Member, Type) \
66+
NODEDBG_SYMBOL(offset_ ## Class ## __ ## Member ## __ ## Type)
67+
68+
// These are the constants describing Node internal structures. Every constant
69+
// should use the format described above. These constants are declared as
70+
// global integers so that they'll be present in the generated node binary. They
71+
// also need to be declared outside any namespace to avoid C++ name-mangling.
72+
#define NODE_OFFSET_POSTMORTEM_METADATA(V) \
73+
V(BaseObject, persistent_handle_, v8_Persistent_v8_Object, \
74+
BaseObject::persistent_handle_) \
75+
V(Environment, handle_wrap_queue_, Environment_HandleWrapQueue, \
76+
Environment::handle_wrap_queue_) \
77+
V(Environment, req_wrap_queue_, Environment_ReqWrapQueue, \
78+
Environment::req_wrap_queue_) \
79+
V(HandleWrap, handle_wrap_queue_, ListNode_HandleWrap, \
80+
HandleWrap::handle_wrap_queue_) \
81+
V(Environment_HandleWrapQueue, head_, ListNode_HandleWrap, \
82+
Environment::HandleWrapQueue::head_) \
83+
V(ListNode_HandleWrap, next_, uintptr_t, ListNode<HandleWrap>::next_) \
84+
V(ReqWrap, req_wrap_queue_, ListNode_ReqWrapQueue, \
85+
ReqWrap<uv_req_t>::req_wrap_queue_) \
86+
V(Environment_ReqWrapQueue, head_, ListNode_ReqWrapQueue, \
87+
Environment::ReqWrapQueue::head_) \
88+
V(ListNode_ReqWrap, next_, uintptr_t, ListNode<ReqWrap<uv_req_t>>::next_)
89+
90+
extern "C" {
91+
int nodedbg_const_Environment__kContextEmbedderDataIndex__int;
92+
uintptr_t nodedbg_offset_ExternalString__data__uintptr_t;
93+
94+
#define V(Class, Member, Type, Accessor) \
95+
NODE_EXTERN uintptr_t NODEDBG_OFFSET(Class, Member, Type);
96+
NODE_OFFSET_POSTMORTEM_METADATA(V)
97+
#undef V
98+
}
99+
100+
namespace node {
101+
102+
int GenDebugSymbols() {
103+
nodedbg_const_Environment__kContextEmbedderDataIndex__int =
104+
Environment::kContextEmbedderDataIndex;
105+
106+
nodedbg_offset_ExternalString__data__uintptr_t = NODE_OFF_EXTSTR_DATA;
107+
108+
#define V(Class, Member, Type, Accessor) \
109+
NODEDBG_OFFSET(Class, Member, Type) = OffsetOf(&Accessor);
110+
NODE_OFFSET_POSTMORTEM_METADATA(V)
111+
#undef V
112+
113+
return 1;
114+
}
115+
116+
int debug_symbols_generated = GenDebugSymbols();
Has conversations. Original line has conversations.
117+
118+
} // namespace node

‎src/req_wrap.h

+7-3
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,13 @@ class ReqWrap : public AsyncWrap {
2727
protected:
2828
// req_wrap_queue_ needs to be at a fixed offset from the start of the class
2929
// because it is used by ContainerOf to calculate the address of the embedding
30-
// ReqWrap. ContainerOf compiles down to simple, fixed pointer arithmetic.
31-
// sizeof(req_) depends on the type of T, so req_wrap_queue_ would
32-
// no longer be at a fixed offset if it came after req_.
30+
// ReqWrap. ContainerOf compiles down to simple, fixed pointer arithmetic. It
31+
// is also used by src/node_postmortem_metadata.cc to calculate offsets and
32+
// generate debug symbols for ReqWrap, which assumes that the position of
33+
// members in memory are predictable. sizeof(req_) depends on the type of T,
34+
// so req_wrap_queue_ would no longer be at a fixed offset if it came after
35+
// req_. For more information please refer to
36+
// `doc/guides/node-postmortem-support.md`
3337
T req_;
3438
};
3539

‎src/util-inl.h

+8-4
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,17 @@ typename ListHead<T, M>::Iterator ListHead<T, M>::end() const {
141141
return Iterator(const_cast<ListNode<T>*>(&head_));
142142
}
143143

144+
template <typename Inner, typename Outer>
145+
constexpr uintptr_t OffsetOf(Inner Outer::*field) {
146+
return reinterpret_cast<uintptr_t>(&(static_cast<Outer*>(0)->*field));
147+
}
148+
144149
template <typename Inner, typename Outer>
145150
ContainerOfHelper<Inner, Outer>::ContainerOfHelper(Inner Outer::*field,
146151
Inner* pointer)
147-
: pointer_(reinterpret_cast<Outer*>(
148-
reinterpret_cast<uintptr_t>(pointer) -
149-
reinterpret_cast<uintptr_t>(&(static_cast<Outer*>(0)->*field)))) {
150-
}
152+
: pointer_(
153+
reinterpret_cast<Outer*>(
154+
reinterpret_cast<uintptr_t>(pointer) - OffsetOf(field))) {}
151155

152156
template <typename Inner, typename Outer>
153157
template <typename TypeName>

‎test/cctest/node_test_fixture.h

+55
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "gtest/gtest.h"
66
#include "node.h"
77
#include "node_platform.h"
8+
#include "node_internals.h"
89
#include "env.h"
910
#include "v8.h"
1011
#include "libplatform/libplatform.h"
@@ -75,6 +76,13 @@ class NodeTestFixture : public ::testing::Test {
7576
v8::Isolate::CreateParams params_;
7677
params_.array_buffer_allocator = allocator_.get();
7778
isolate_ = v8::Isolate::New(params_);
79+
80+
// As the TracingController is stored globally, we only need to create it
81+
// one time for all tests.
82+
if (node::tracing::TraceEventHelper::GetTracingController() == nullptr) {
83+
node::tracing::TraceEventHelper::SetTracingController(
84+
new v8::TracingController());
85+
}
7886
}
7987

8088
virtual void TearDown() {
@@ -95,4 +103,51 @@ class NodeTestFixture : public ::testing::Test {
95103
v8::ArrayBuffer::Allocator::NewDefaultAllocator()};
96104
};
97105

106+
107+
class EnvironmentTestFixture : public NodeTestFixture {
108+
public:
109+
class Env {
110+
public:
111+
Env(const v8::HandleScope& handle_scope,
112+
const Argv& argv,
113+
NodeTestFixture* test_fixture) {
114+
auto isolate = handle_scope.GetIsolate();
115+
context_ = node::NewContext(isolate);
116+
CHECK(!context_.IsEmpty());
117+
context_->Enter();
118+
119+
isolate_data_ = node::CreateIsolateData(isolate,
120+
NodeTestFixture::CurrentLoop(),
121+
test_fixture->Platform());
122+
CHECK_NE(nullptr, isolate_data_);
123+
environment_ = node::CreateEnvironment(isolate_data_,
124+
context_,
125+
1, *argv,
126+
argv.nr_args(), *argv);
127+
CHECK_NE(nullptr, environment_);
128+
}
129+
130+
~Env() {
131+
environment_->CleanupHandles();
132+
node::FreeEnvironment(environment_);
133+
node::FreeIsolateData(isolate_data_);
134+
context_->Exit();
135+
}
136+
137+
node::Environment* operator*() const {
138+
return environment_;
139+
}
140+
141+
v8::Local<v8::Context> context() const {
142+
return context_;
143+
}
144+
145+
private:
146+
v8::Local<v8::Context> context_;
147+
node::IsolateData* isolate_data_;
148+
node::Environment* environment_;
149+
DISALLOW_COPY_AND_ASSIGN(Env);
150+
};
151+
};
152+
98153
#endif // TEST_CCTEST_NODE_TEST_FIXTURE_H_

‎test/cctest/test_environment.cc

+5-41
Original file line numberDiff line numberDiff line change
@@ -20,43 +20,7 @@ static void at_exit_callback1(void* arg);
2020
static void at_exit_callback2(void* arg);
2121
static std::string cb_1_arg; // NOLINT(runtime/string)
2222

23-
class EnvironmentTest : public NodeTestFixture {
24-
public:
25-
class Env {
26-
public:
27-
Env(const v8::HandleScope& handle_scope,
28-
v8::Isolate* isolate,
29-
const Argv& argv,
30-
NodeTestFixture* test_fixture) {
31-
context_ = v8::Context::New(isolate);
32-
CHECK(!context_.IsEmpty());
33-
isolate_data_ = CreateIsolateData(isolate,
34-
NodeTestFixture::CurrentLoop(),
35-
test_fixture->Platform());
36-
CHECK_NE(nullptr, isolate_data_);
37-
environment_ = CreateEnvironment(isolate_data_,
38-
context_,
39-
1, *argv,
40-
argv.nr_args(), *argv);
41-
CHECK_NE(nullptr, environment_);
42-
}
43-
44-
~Env() {
45-
environment_->CleanupHandles();
46-
FreeEnvironment(environment_);
47-
FreeIsolateData(isolate_data_);
48-
}
49-
50-
Environment* operator*() const {
51-
return environment_;
52-
}
53-
54-
private:
55-
v8::Local<v8::Context> context_;
56-
IsolateData* isolate_data_;
57-
Environment* environment_;
58-
};
59-
23+
class EnvironmentTest : public EnvironmentTestFixture {
6024
private:
6125
virtual void TearDown() {
6226
NodeTestFixture::TearDown();
@@ -68,7 +32,7 @@ class EnvironmentTest : public NodeTestFixture {
6832
TEST_F(EnvironmentTest, AtExitWithEnvironment) {
6933
const v8::HandleScope handle_scope(isolate_);
7034
const Argv argv;
71-
Env env {handle_scope, isolate_, argv, this};
35+
Env env {handle_scope, argv, this};
7236

7337
AtExit(*env, at_exit_callback1);
7438
RunAtExit(*env);
@@ -78,7 +42,7 @@ TEST_F(EnvironmentTest, AtExitWithEnvironment) {
7842
TEST_F(EnvironmentTest, AtExitWithArgument) {
7943
const v8::HandleScope handle_scope(isolate_);
8044
const Argv argv;
81-
Env env {handle_scope, isolate_, argv, this};
45+
Env env {handle_scope, argv, this};
8246

8347
std::string arg{"some args"};
8448
AtExit(*env, at_exit_callback1, static_cast<void*>(&arg));
@@ -89,8 +53,8 @@ TEST_F(EnvironmentTest, AtExitWithArgument) {
8953
TEST_F(EnvironmentTest, MultipleEnvironmentsPerIsolate) {
9054
const v8::HandleScope handle_scope(isolate_);
9155
const Argv argv;
92-
Env env1 {handle_scope, isolate_, argv, this};
93-
Env env2 {handle_scope, isolate_, argv, this};
56+
Env env1 {handle_scope, argv, this};
57+
Env env2 {handle_scope, argv, this};
9458

9559
AtExit(*env1, at_exit_callback1);
9660
AtExit(*env2, at_exit_callback2);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#include "node_postmortem_metadata.cc"
2+
3+
#include "gtest/gtest.h"
4+
#include "node.h"
5+
#include "node_internals.h"
6+
#include "node_test_fixture.h"
7+
#include "req_wrap-inl.h"
8+
#include "tracing/agent.h"
9+
#include "v8.h"
10+
11+
12+
class DebugSymbolsTest : public EnvironmentTestFixture {};
13+
14+
15+
class TestHandleWrap : public node::HandleWrap {
16+
public:
17+
size_t self_size() const override { return sizeof(*this); }
18+
19+
TestHandleWrap(node::Environment* env,
20+
v8::Local<v8::Object> object,
21+
uv_tcp_t* handle)
22+
: node::HandleWrap(env,
23+
object,
24+
reinterpret_cast<uv_handle_t*>(handle),
25+
node::AsyncWrap::PROVIDER_TIMERWRAP) {}
26+
};
27+
28+
29+
class TestReqWrap : public node::ReqWrap<uv_req_t> {
30+
public:
31+
size_t self_size() const override { return sizeof(*this); }
32+
33+
TestReqWrap(node::Environment* env, v8::Local<v8::Object> object)
34+
: node::ReqWrap<uv_req_t>(env,
35+
object,
36+
node::AsyncWrap::PROVIDER_TIMERWRAP) {}
37+
};
38+
39+
TEST_F(DebugSymbolsTest, ContextEmbedderDataIndex) {
40+
int kContextEmbedderDataIndex = node::Environment::kContextEmbedderDataIndex;
41+
EXPECT_EQ(nodedbg_const_Environment__kContextEmbedderDataIndex__int,
42+
kContextEmbedderDataIndex);
43+
}
44+
45+
TEST_F(DebugSymbolsTest, ExternalStringDataOffset) {
46+
EXPECT_EQ(nodedbg_offset_ExternalString__data__uintptr_t,
47+
NODE_OFF_EXTSTR_DATA);
48+
}
49+
50+
TEST_F(DebugSymbolsTest, BaseObjectPersistentHandle) {
51+
const v8::HandleScope handle_scope(isolate_);
52+
const Argv argv;
53+
Env env{handle_scope, argv, this};
54+
55+
v8::Local<v8::Object> object = v8::Object::New(isolate_);
56+
node::BaseObject obj(*env, object);
57+
58+
auto expected = reinterpret_cast<uintptr_t>(&obj.persistent());
59+
auto calculated = reinterpret_cast<uintptr_t>(&obj) +
60+
nodedbg_offset_BaseObject__persistent_handle___v8_Persistent_v8_Object;
61+
EXPECT_EQ(expected, calculated);
62+
63+
obj.persistent().Reset(); // ~BaseObject() expects an empty handle.
64+
}
65+
66+
67+
TEST_F(DebugSymbolsTest, EnvironmentHandleWrapQueue) {
68+
const v8::HandleScope handle_scope(isolate_);
69+
const Argv argv;
70+
Env env{handle_scope, argv, this};
71+
72+
auto expected = reinterpret_cast<uintptr_t>((*env)->handle_wrap_queue());
73+
auto calculated = reinterpret_cast<uintptr_t>(*env) +
74+
nodedbg_offset_Environment__handle_wrap_queue___Environment_HandleWrapQueue; // NOLINT(whitespace/line_length)
75+
EXPECT_EQ(expected, calculated);
76+
}
77+
78+
TEST_F(DebugSymbolsTest, EnvironmentReqWrapQueue) {
79+
const v8::HandleScope handle_scope(isolate_);
80+
const Argv argv;
81+
Env env{handle_scope, argv, this};
82+
83+
auto expected = reinterpret_cast<uintptr_t>((*env)->req_wrap_queue());
84+
auto calculated = reinterpret_cast<uintptr_t>(*env) +
85+
nodedbg_offset_Environment__req_wrap_queue___Environment_ReqWrapQueue;
86+
EXPECT_EQ(expected, calculated);
87+
}
88+
89+
TEST_F(DebugSymbolsTest, HandleWrapList) {
90+
const v8::HandleScope handle_scope(isolate_);
91+
const Argv argv;
92+
Env env{handle_scope, argv, this};
93+
94+
uv_tcp_t handle;
95+
96+
auto obj_template = v8::FunctionTemplate::New(isolate_);
97+
obj_template->InstanceTemplate()->SetInternalFieldCount(1);
98+
99+
v8::Local<v8::Object> object =
100+
obj_template->GetFunction()->NewInstance(env.context()).ToLocalChecked();
101+
TestHandleWrap obj(*env, object, &handle);
102+
103+
auto queue = reinterpret_cast<uintptr_t>((*env)->handle_wrap_queue());
104+
auto head = queue +
105+
nodedbg_offset_Environment_HandleWrapQueue__head___ListNode_HandleWrap;
106+
auto next =
107+
head + nodedbg_offset_ListNode_HandleWrap__next___uintptr_t;
108+
next = *reinterpret_cast<uintptr_t*>(next);
109+
110+
auto expected = reinterpret_cast<uintptr_t>(&obj);
111+
auto calculated = next -
112+
nodedbg_offset_HandleWrap__handle_wrap_queue___ListNode_HandleWrap;
113+
EXPECT_EQ(expected, calculated);
114+
115+
obj.persistent().Reset(); // ~HandleWrap() expects an empty handle.
116+
}
117+
118+
TEST_F(DebugSymbolsTest, ReqWrapList) {
119+
const v8::HandleScope handle_scope(isolate_);
120+
const Argv argv;
121+
Env env{handle_scope, argv, this};
122+
123+
auto obj_template = v8::FunctionTemplate::New(isolate_);
124+
obj_template->InstanceTemplate()->SetInternalFieldCount(1);
125+
126+
v8::Local<v8::Object> object =
127+
obj_template->GetFunction()->NewInstance(env.context()).ToLocalChecked();
128+
TestReqWrap obj(*env, object);
129+
130+
// NOTE (mmarchini): Workaround to fix failing tests on ARM64 machines with
131+
// older GCC. Should be removed once we upgrade the GCC version used on our
132+
// ARM64 CI machinies.
133+
for (auto it : *(*env)->req_wrap_queue()) {}
134+
135+
auto queue = reinterpret_cast<uintptr_t>((*env)->req_wrap_queue());
136+
auto head = queue +
137+
nodedbg_offset_Environment_ReqWrapQueue__head___ListNode_ReqWrapQueue;
138+
auto next =
139+
head + nodedbg_offset_ListNode_ReqWrap__next___uintptr_t;
140+
next = *reinterpret_cast<uintptr_t*>(next);
141+
142+
auto expected = reinterpret_cast<uintptr_t>(&obj);
143+
auto calculated =
144+
next - nodedbg_offset_ReqWrap__req_wrap_queue___ListNode_ReqWrapQueue;
145+
EXPECT_EQ(expected, calculated);
146+
147+
obj.Dispatched();
148+
}

0 commit comments

Comments
 (0)
Please sign in to comment.