Skip to content
This repository was archived by the owner on Oct 15, 2020. It is now read-only.

Commit 4569b28

Browse files
committed
meta: merge node/master into node-chakracore/master
Merge fa33f02 as of 2018-01-26 This commit was automatically generated. For any problems, please contact jackhorton Reviewed-By: Jimmy Thomson <[email protected]>
2 parents 33a43bc + fa33f02 commit 4569b28

12 files changed

+436
-49
lines changed

Diff for: 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

Diff for: node.gyp

+3
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@
317317
'src/node_os.cc',
318318
'src/node_platform.cc',
319319
'src/node_perf.cc',
320+
'src/node_postmortem_metadata.cc',
320321
'src/node_serdes.cc',
321322
'src/node_trace_events.cc',
322323
'src/node_url.cc',
@@ -1006,12 +1007,14 @@
10061007
'test/cctest/node_test_fixture.cc',
10071008
'test/cctest/test_aliased_buffer.cc',
10081009
'test/cctest/test_base64.cc',
1010+
'test/cctest/test_node_postmortem_metadata.cc',
10091011
'test/cctest/test_environment.cc',
10101012
'test/cctest/test_util.cc',
10111013
'test/cctest/test_url.cc'
10121014
],
10131015
'libraries': [
10141016
'<(OBJ_PATH)<(OBJ_SEPARATOR)async_wrap.<(OBJ_SUFFIX)',
1017+
'<(OBJ_PATH)<(OBJ_SEPARATOR)handle_wrap.<(OBJ_SUFFIX)',
10151018
'<(OBJ_PATH)<(OBJ_SEPARATOR)env.<(OBJ_SUFFIX)',
10161019
'<(OBJ_PATH)<(OBJ_SEPARATOR)node.<(OBJ_SUFFIX)',
10171020
'<(OBJ_PATH)<(OBJ_SEPARATOR)node_buffer.<(OBJ_SUFFIX)',

Diff for: 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
};

Diff for: 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,

Diff for: 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_;

Diff for: 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();
117+
118+
} // namespace node

Diff for: 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

Diff for: 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>

Diff for: 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_

0 commit comments

Comments
 (0)