-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 Backport-PR-URL: #18550 PR-URL: #14901 Refs: nodejs/post-mortem#46 Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
- Loading branch information
Showing
11 changed files
with
432 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# Postmortem Support | ||
|
||
Postmortem metadata are constants present in the final build which can be used | ||
by debuggers and other tools to navigate through internal structures of software | ||
when analyzing its memory (either on a running process or a core dump). Node | ||
provides this metadata in its builds for V8 and Node internal structures. | ||
|
||
|
||
### V8 Postmortem metadata | ||
|
||
V8 prefixes all postmortem constants with `v8dbg_`, and they allow inspection of | ||
objects on the heap as well as object properties and references. V8 generates | ||
those symbols with a script (`deps/v8/tools/gen-postmortem-metadata.py`), and | ||
Node always includes these constants in the final build. | ||
|
||
### Node Debug Symbols | ||
|
||
Node prefixes all postmortem constants with `nodedbg_`, and they complement V8 | ||
constants by providing ways to inspect Node-specific structures, like | ||
`node::Environment`, `node::BaseObject` and its descendants, classes from | ||
`src/utils.h` and others. Those constants are declared in | ||
`src/node_postmortem_metadata.cc`, and most of them are calculated at compile | ||
time. | ||
|
||
#### Calculating offset of class members | ||
|
||
Node constants referring to the offset of class members in memory are calculated | ||
at compile time. Because of that, those class members must be at a fixed offset | ||
from the start of the class. That's not a problem in most cases, but it also | ||
means that those members should always come after any templated member on the | ||
class definition. | ||
|
||
For example, if we want to add a constant with the offset for | ||
`ReqWrap::req_wrap_queue_`, it should be defined after `ReqWrap::req_`, because | ||
`sizeof(req_)` depends on the type of T, which means the class definition should | ||
be like this: | ||
|
||
```c++ | ||
template <typename T> | ||
class ReqWrap : public AsyncWrap { | ||
private: | ||
// req_wrap_queue_ comes before any templated member, which places it in a | ||
// fixed offset from the start of the class | ||
ListNode<ReqWrap> req_wrap_queue_; | ||
|
||
T req_; | ||
}; | ||
``` | ||
instead of: | ||
```c++ | ||
template <typename T> | ||
class ReqWrap : public AsyncWrap { | ||
private: | ||
T req_; | ||
// req_wrap_queue_ comes after a templated member, which means it won't be in | ||
// a fixed offset from the start of the class | ||
ListNode<ReqWrap> req_wrap_queue_; | ||
}; | ||
``` | ||
|
||
There are also tests on `test/cctest/test_node_postmortem_metadata.cc` to make | ||
sure all Node postmortem metadata are calculated correctly. | ||
|
||
## Tools and References | ||
|
||
* [llnode](https://github.com/nodejs/llnode): LLDB plugin | ||
* [`mdb_v8`](https://github.com/joyent/mdb_v8): mdb plugin | ||
* [nodejs/post-mortem](https://github.com/nodejs/post-mortem): Node.js | ||
post-mortem working group |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
// Need to import standard headers before redefining private, otherwise it | ||
// won't compile. | ||
#include <algorithm> | ||
#include <array> | ||
#include <atomic> | ||
#include <bitset> | ||
#include <cctype> | ||
#include <climits> | ||
#include <cmath> | ||
#include <cstdarg> | ||
#include <cstddef> | ||
#include <cstdint> | ||
#include <cstdio> | ||
#include <cstdlib> | ||
#include <cstring> | ||
#include <ctime> | ||
#include <deque> | ||
#include <exception> | ||
#include <forward_list> | ||
#include <fstream> | ||
#include <functional> | ||
#include <iomanip> | ||
#include <iosfwd> | ||
#include <iostream> | ||
#include <istream> | ||
#include <iterator> | ||
#include <limits> | ||
#include <list> | ||
#include <map> | ||
#include <memory> | ||
#include <new> | ||
#include <ostream> | ||
#include <queue> | ||
#include <set> | ||
#include <sstream> | ||
#include <stack> | ||
#include <streambuf> | ||
#include <string> | ||
#include <tuple> | ||
#include <type_traits> | ||
#include <typeinfo> | ||
#include <unordered_map> | ||
#include <unordered_set> | ||
#include <utility> | ||
#include <vector> | ||
|
||
namespace node { | ||
// Forward declaration needed before redefining private. | ||
int GenDebugSymbols(); | ||
} // namespace node | ||
|
||
|
||
#define private friend int GenDebugSymbols(); private | ||
|
||
#include "env.h" | ||
#include "base_object-inl.h" | ||
#include "handle_wrap.h" | ||
#include "util-inl.h" | ||
#include "req_wrap.h" | ||
#include "v8abbr.h" | ||
|
||
#define NODEDBG_SYMBOL(Name) nodedbg_ ## Name | ||
|
||
// nodedbg_offset_CLASS__MEMBER__TYPE: Describes the offset to a class member. | ||
#define NODEDBG_OFFSET(Class, Member, Type) \ | ||
NODEDBG_SYMBOL(offset_ ## Class ## __ ## Member ## __ ## Type) | ||
|
||
// These are the constants describing Node internal structures. Every constant | ||
// should use the format described above. These constants are declared as | ||
// global integers so that they'll be present in the generated node binary. They | ||
// also need to be declared outside any namespace to avoid C++ name-mangling. | ||
#define NODE_OFFSET_POSTMORTEM_METADATA(V) \ | ||
V(BaseObject, persistent_handle_, v8_Persistent_v8_Object, \ | ||
BaseObject::persistent_handle_) \ | ||
V(Environment, handle_wrap_queue_, Environment_HandleWrapQueue, \ | ||
Environment::handle_wrap_queue_) \ | ||
V(Environment, req_wrap_queue_, Environment_ReqWrapQueue, \ | ||
Environment::req_wrap_queue_) \ | ||
V(HandleWrap, handle_wrap_queue_, ListNode_HandleWrap, \ | ||
HandleWrap::handle_wrap_queue_) \ | ||
V(Environment_HandleWrapQueue, head_, ListNode_HandleWrap, \ | ||
Environment::HandleWrapQueue::head_) \ | ||
V(ListNode_HandleWrap, next_, uintptr_t, ListNode<HandleWrap>::next_) \ | ||
V(ReqWrap, req_wrap_queue_, ListNode_ReqWrapQueue, \ | ||
ReqWrap<uv_req_t>::req_wrap_queue_) \ | ||
V(Environment_ReqWrapQueue, head_, ListNode_ReqWrapQueue, \ | ||
Environment::ReqWrapQueue::head_) \ | ||
V(ListNode_ReqWrap, next_, uintptr_t, ListNode<ReqWrap<uv_req_t>>::next_) | ||
|
||
extern "C" { | ||
int nodedbg_const_Environment__kContextEmbedderDataIndex__int; | ||
uintptr_t nodedbg_offset_ExternalString__data__uintptr_t; | ||
|
||
#define V(Class, Member, Type, Accessor) \ | ||
NODE_EXTERN uintptr_t NODEDBG_OFFSET(Class, Member, Type); | ||
NODE_OFFSET_POSTMORTEM_METADATA(V) | ||
#undef V | ||
} | ||
|
||
namespace node { | ||
|
||
int GenDebugSymbols() { | ||
nodedbg_const_Environment__kContextEmbedderDataIndex__int = | ||
Environment::kContextEmbedderDataIndex; | ||
|
||
nodedbg_offset_ExternalString__data__uintptr_t = NODE_OFF_EXTSTR_DATA; | ||
|
||
#define V(Class, Member, Type, Accessor) \ | ||
NODEDBG_OFFSET(Class, Member, Type) = OffsetOf(&Accessor); | ||
NODE_OFFSET_POSTMORTEM_METADATA(V) | ||
#undef V | ||
|
||
return 1; | ||
} | ||
|
||
int debug_symbols_generated = GenDebugSymbols(); | ||
|
||
} // namespace node |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.