diff --git a/common.gypi b/common.gypi index 6677274f3abe80..b090bf0db00e97 100644 --- a/common.gypi +++ b/common.gypi @@ -50,6 +50,7 @@ }, { 'os_posix': 1, 'v8_postmortem_support%': 'true', + 'node_postmortem_support%': 'true', }], ['OS== "mac"', { 'OBJ_DIR%': '<(PRODUCT_DIR)/obj.target', diff --git a/node.gyp b/node.gyp index 08b792ef309c2a..c9828b6ffb10f9 100644 --- a/node.gyp +++ b/node.gyp @@ -718,7 +718,28 @@ 'ldflags': [ '-I<(SHARED_INTERMEDIATE_DIR)' ] }], ] - } + }, + { + 'target_name': 'node-postmortem-metadata', + 'type': 'none', + 'variables': {}, + 'actions': [ + { + 'action_name': 'gen-postmortem-metadata', + 'inputs': [ + './tools/gen-postmortem-metadata.py', + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/node-debug-support.cc', + ], + 'action': [ + 'python', + './tools/gen-postmortem-metadata.py', + '<@(_outputs)', + ] + } + ] + }, ], # end targets 'conditions': [ diff --git a/node.gypi b/node.gypi index e9905ab4436dd8..324abb893b9583 100644 --- a/node.gypi +++ b/node.gypi @@ -230,6 +230,13 @@ [ 'node_no_browser_globals=="true"', { 'defines': [ 'NODE_NO_BROWSER_GLOBALS' ], } ], + [ 'node_postmortem_support=="true"', { + 'dependencies': [ 'node-postmortem-metadata' ], + 'defines': [ 'NODE_POSTMORTEM_SUPPORT' ], + 'sources': [ + '<(SHARED_INTERMEDIATE_DIR)/node-debug-support.cc' + ] + }], [ 'node_use_bundled_v8=="true" and v8_postmortem_support=="true"', { 'dependencies': [ 'deps/v8/src/v8.gyp:postmortem-metadata' ], 'conditions': [ diff --git a/src/env.cc b/src/env.cc index 076198cd626a81..69900760903aaa 100644 --- a/src/env.cc +++ b/src/env.cc @@ -1,6 +1,7 @@ #include "env.h" #include "env-inl.h" #include "async-wrap.h" +#include "base-object.h" #include "v8.h" #include "v8-profiler.h" @@ -23,6 +24,8 @@ using v8::Message; using v8::StackFrame; using v8::StackTrace; +Environment *Environment::currentEnvironment; + void Environment::Start(int argc, const char* const* argv, int exec_argc, diff --git a/src/env.h b/src/env.h index b06901085a2c7a..aec9b636ffe9b1 100644 --- a/src/env.h +++ b/src/env.h @@ -361,6 +361,7 @@ class IsolateData { class Environment { public: + static Environment *currentEnvironment; class AsyncHooks { public: // Reason for both UidFields and Fields are that one is stored as a double* diff --git a/src/handle_wrap.h b/src/handle_wrap.h index f8be356e1a730c..5689d6dd185e12 100644 --- a/src/handle_wrap.h +++ b/src/handle_wrap.h @@ -70,6 +70,13 @@ class HandleWrap : public AsyncWrap { inline uv_handle_t* GetHandle() const { return handle_; } + #ifdef NODE_POSTMORTEM_SUPPORT + inline ListNode *handle_wrap_queue() { + return &handle_wrap_queue_; + } + #endif + + protected: HandleWrap(Environment* env, v8::Local object, diff --git a/src/node.cc b/src/node.cc index 1ef5adce3bb7d1..cc3402bdb093ec 100644 --- a/src/node.cc +++ b/src/node.cc @@ -4528,6 +4528,7 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data, Local context = Context::New(isolate); Context::Scope context_scope(context); Environment env(isolate_data, context); + Environment::currentEnvironment = &env; CHECK_EQ(0, uv_key_create(&thread_local_env)); uv_key_set(&thread_local_env, &env); env.Start(argc, argv, exec_argc, exec_argv, v8_is_profiling); diff --git a/src/req-wrap.h b/src/req-wrap.h index 0fddae67460d6f..c240d14fd67b74 100644 --- a/src/req-wrap.h +++ b/src/req-wrap.h @@ -19,6 +19,10 @@ class ReqWrap : public AsyncWrap { inline ~ReqWrap() override; inline void Dispatched(); // Call this after the req has been dispatched. T* req() { return &req_; } + #ifdef NODE_POSTMORTEM_SUPPORT + inline ListNode *req_wrap_queue() { return &req_wrap_queue_; } + #endif + private: friend class Environment; diff --git a/src/util.h b/src/util.h index 08308d837fbf8b..7a1740353597e6 100644 --- a/src/util.h +++ b/src/util.h @@ -156,6 +156,9 @@ class ListNode { inline ~ListNode(); inline void Remove(); inline bool IsEmpty() const; + #ifdef NODE_POSTMORTEM_SUPPORT + inline ListNode** next() { return &next_; } + #endif private: template (U::*M)> friend class ListHead; @@ -189,6 +192,11 @@ class ListHead { inline Iterator begin() const; inline Iterator end() const; + #ifdef NODE_POSTMORTEM_SUPPORT + inline ListNode *head() { return &head_; } + #endif + + private: ListNode head_; DISALLOW_COPY_AND_ASSIGN(ListHead); diff --git a/tools/gen-postmortem-metadata.py b/tools/gen-postmortem-metadata.py new file mode 100644 index 00000000000000..a51ad273706747 --- /dev/null +++ b/tools/gen-postmortem-metadata.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python + +# +# gen-postmortem-metadata.py output_file.cc +# +# Creates debugging symbols to help naviage Node's internals using post-mortem +# debugging tools. +# + +import sys + + +class DebugSymbol(object): + type_ = 'int' + _PREFIX = 'nodedbg_' + + def __init__(self, name, value, headers=[], type_=None): + self.name = name + self.value = value + self.headers = headers + self.type_ = type_ or DebugSymbol.type_ + + @classmethod + def get_headers(cls, debug_symbols): + seen = set() + seen_add = seen.add # faster than seen.add on each iteration + headers = [debug_symbol.headers for debug_symbol in debug_symbols] + headers = sum(headers, []) + + return [h for h in headers if not (h in seen or seen_add(h))] + + def __str__(self): + return "{type} {prefix}{name} = {value};".format( + type=self.type_, + prefix=self._PREFIX, + name=self.name, + value=self.value, + ) + + +debug_symbols = [ + DebugSymbol( + name="currentEnvironment", + value="(uint64_t) &Environment::currentEnvironment", + headers=["env.h"], + type_="uint64_t", + ), + DebugSymbol( + name="class__BaseObject__persistant_handle", + value="(size_t) &(((BaseObject*)0)->persistent())", + headers=["base-object.h", "base-object-inl.h"], + type_="size_t", + ), + DebugSymbol( + name="class__Environment__handleWrapQueue", + value="(size_t) (((Environment*)0)->handle_wrap_queue())", + headers=["env.h"], + type_="size_t", + ), + DebugSymbol( + name="class__HandleWrap__node", + value="(size_t) (((HandleWrap*)0)->handle_wrap_queue())", + headers=["handle_wrap.h"], + type_="size_t", + ), + DebugSymbol( + name="class__HandleWrapQueue__headOffset", + value="(size_t) (((Environment::HandleWrapQueue*)0)->head())", + headers=["env.h"], + type_="size_t", + ), + DebugSymbol( + name="class__HandleWrapQueue__nextOffset", + value="(size_t) (((ListNode*)0)->next())", + headers=["handle_wrap.h", "util.h"], + type_="size_t", + ), + DebugSymbol( + name="class__Environment__reqWrapQueue", + value="(size_t) (((Environment*)0)->req_wrap_queue())", + headers=["env.h"], + type_="size_t", + ), + DebugSymbol( + name="class__ReqWrap__node", + value="(size_t) (((ReqWrap*)0)->req_wrap_queue())", + headers=["req-wrap.h"], + type_="size_t", + ), + DebugSymbol( + name="class__ReqWrapQueue__headOffset", + value="(size_t) (((Environment::ReqWrapQueue*)0)->head())", + headers=["env.h"], + type_="size_t", + ), + DebugSymbol( + name="class__ReqWrapQueue__nextOffset", + value="(size_t) (((ListNode>*)0)->next())", + headers=["req-wrap.h", "util.h"], + type_="size_t", + ), +] + + +template = ''' +/* + * This file is generated by {filename}. Do not edit directly. + */ + +{includes} + +using namespace node; + +extern "C" {{ +{symbols} +}} +''' + + +def create_symbols_file(): + out = file(sys.argv[1], 'w') + headers = DebugSymbol.get_headers(debug_symbols) + includes = ['#include "{}"'.format(header) for header in headers] + includes = "\n".join(includes) + + symbols = "\n".join([str(symbol) for symbol in debug_symbols]) + + out.write(template.format( + filename=sys.argv[0], + includes=includes, + symbols=symbols, + )) + + +if (len(sys.argv) < 2): + print('usage: %s output.cc' % sys.argv[0]) + sys.exit(2) + + +create_symbols_file()