diff --git a/node.gyp b/node.gyp index e362b67830537f..cbfa6853a644ca 100644 --- a/node.gyp +++ b/node.gyp @@ -299,6 +299,7 @@ # node.gyp is added to the project by default. 'common.gypi', '<(SHARED_INTERMEDIATE_DIR)/node_javascript.cc', + '<(SHARED_INTERMEDIATE_DIR)/node-debug-support.cc', ], 'variables': { @@ -974,7 +975,29 @@ }]] }], ] - } + }, + { + 'target_name': 'node_postmortem_metadata', + 'type': 'none', + 'toolsets': ['host'], + 'actions': [ + { + 'action_name': 'gen-postmortem-metadata', + 'process_outputs_as_sources': 1, + '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/tools/gen-postmortem-metadata.py b/tools/gen-postmortem-metadata.py new file mode 100644 index 00000000000000..3ec95ba079db99 --- /dev/null +++ b/tools/gen-postmortem-metadata.py @@ -0,0 +1,200 @@ +#!/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 os +import fnmatch +import re +from glob import glob +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): + ''' + Return a list of headers without duplicates, preserving the order they were + declared + ''' + seen = set() + headers = [debug_symbol.headers for debug_symbol in debug_symbols] + headers = sum(headers, []) + + result = [] + for h in headers: + if not h in seen: + seen.add(h) + result.append(h) + + return result + + @property + def declare(self): + return '{type} {prefix}{name};'.format( + type=self.type_, + prefix=self._prefix, + name=self.name, + ) + + @property + def fill(self): + return '{prefix}{name} = {value};'.format( + prefix=self._prefix, + name=self.name, + value=self.value, + ) + + +debug_symbols = [ + DebugSymbol( + name='environment_context_idx_embedder_data', + value='Environment::kContextEmbedderDataIndex', + headers=['env.h'], + type_='int', + ), + DebugSymbol( + name='class__BaseObject__persistent_handle', + value='nonstandard_offsetof(BaseObject, persistent_handle_)', + headers=['base_object-inl.h'], + type_='size_t', + ), + DebugSymbol( + name='class__Environment__handleWrapQueue', + value='nonstandard_offsetof(Environment, handle_wrap_queue_)', + headers=['env.h'], + type_='size_t', + ), + DebugSymbol( + name='class__HandleWrap__node', + value='nonstandard_offsetof(HandleWrap, handle_wrap_queue_)', + headers=['handle_wrap.h'], + type_='size_t', + ), + DebugSymbol( + name='class__HandleWrapQueue__headOffset', + value='offsetof(Environment::HandleWrapQueue, head_)', + headers=['env.h'], + type_='size_t', + ), + DebugSymbol( + name='class__HandleWrapQueue__nextOffset', + value='offsetof(ListNode, next_)', + headers=['handle_wrap.h', 'util.h'], + type_='size_t', + ), + DebugSymbol( + name='class__Environment__reqWrapQueue', + value='nonstandard_offsetof(Environment, req_wrap_queue_)', + headers=['env.h'], + type_='size_t', + ), + DebugSymbol( + name='class__ReqWrap__node', + value='nonstandard_offsetof(ReqWrap, req_wrap_queue_)', + headers=['req_wrap.h'], + type_='size_t', + ), + DebugSymbol( + name='class__ReqWrapQueue__headOffset', + value='offsetof(Environment::ReqWrapQueue, head_)', + headers=['env.h'], + type_='size_t', + ), + DebugSymbol( + name='class__ReqWrapQueue__nextOffset', + value='offsetof(ListNode>, next_)', + headers=['req_wrap.h', 'util.h'], + type_='size_t', + ), +] + + +template = ''' +/* + * This file is generated by {filename}. Do not edit directly. + */ + +// Need to import standard headers before redefining private, otherwise it +// won't compile +{standard_includes} + +int GenDebugSymbol(); + +#define private friend int GenDebugSymbol(); private + +#define nonstandard_offsetof(CLASS, FIELD) reinterpret_cast\ + (&(static_cast(0)->FIELD)) + +{includes} + +{declare_symbols} + +namespace node {{ + +int GenDebugSymbol() {{ +{fill_symbols} +return 1; +}} + +int debug_symbols_generated = GenDebugSymbol(); + +}} +''' + + +def get_standard_includes(): + ''' + Try to find all standard C++ headers needed by node and its dependencies + ''' + includes = ["algorithm", "array", "atomic", "bitset", "cctype", "climits", + "cmath", "cstdarg", "cstddef", "cstdint", "cstdio", "cstdlib", "cstring", + "ctime", "deque", "exception", "forward_list", "fstream", "functional", + "iomanip", "iosfwd", "iostream", "istream", "iterator", "limits", "list", + "map", "memory", "new", "ostream", "queue", "set", "sstream", "stack", + "streambuf", "string", "tuple", "type_traits", "typeinfo", "unordered_map", + "unordered_set", "utility", "vector"] + return includes + + +def create_symbols_file(): + out = file(sys.argv[1], 'w') + headers = DebugSymbol.get_headers(debug_symbols) + includes = ['#include "{0}"'.format(header) for header in headers] + includes = '\n'.join(includes) + + standard_includes = get_standard_includes() + standard_includes = ['#include <{0}>'.format(include) for include in standard_includes] + standard_includes = '\n'.join(standard_includes) + + declare_symbols = '\n'.join([symbol.declare for symbol in debug_symbols]) + fill_symbols = '\n'.join([symbol.fill for symbol in debug_symbols]) + + out.write(template.format( + filename=sys.argv[0], + includes=includes, + standard_includes=standard_includes, + declare_symbols=declare_symbols, + fill_symbols=fill_symbols, + )) + + +if len(sys.argv) < 2: + print('usage: {0} output.cc'.format(sys.argv[0])) + sys.exit(2) + + +create_symbols_file()