Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 45eeea4

Browse filesBrowse files
committed
src: implement debug output utilities
Implement utilities for easier debugging of Node.js core code, inspired by the HTTP/2 debugging code. Debugging is, however, implemented at runtime rather than at compile time, controlled through a new `NODE_DEBUG_NATIVE=categories` environment variable. The runtime overhead in the debugging-disabled case amounts to 1 well-cachable one-byte read per debug call. PR-URL: #20987 Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Minwoo Jung <minwoo@nodesource.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 1b8e8e9 commit 45eeea4
Copy full SHA for 45eeea4

File tree

Expand file treeCollapse file tree

10 files changed

+174
-0
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

10 files changed

+174
-0
lines changed
Open diff view settings
Collapse file

‎node.gyp‎

Copy file name to clipboardExpand all lines: node.gyp
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@
367367
'src/base_object-inl.h',
368368
'src/connection_wrap.h',
369369
'src/connect_wrap.h',
370+
'src/debug_utils.h',
370371
'src/env.h',
371372
'src/env-inl.h',
372373
'src/handle_wrap.h',
Collapse file

‎src/async_wrap.cc‎

Copy file name to clipboardExpand all lines: src/async_wrap.cc
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,11 @@ void EmitAsyncDestroy(Isolate* isolate, async_context asyncContext) {
769769
Environment::GetCurrent(isolate), asyncContext.async_id);
770770
}
771771

772+
std::string AsyncWrap::diagnostic_name() const {
773+
return std::string(provider_names[provider_type()]) +
774+
" (" + std::to_string(static_cast<int64_t>(async_id_)) + ")";
775+
}
776+
772777
} // namespace node
773778

774779
NODE_BUILTIN_MODULE_CONTEXT_AWARE(async_wrap, node::AsyncWrap::Initialize)
Collapse file

‎src/async_wrap.h‎

Copy file name to clipboardExpand all lines: src/async_wrap.h
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ class AsyncWrap : public BaseObject {
167167
v8::Local<v8::Value>* argv);
168168

169169
virtual size_t self_size() const = 0;
170+
virtual std::string diagnostic_name() const;
170171

171172
static void WeakCallback(const v8::WeakCallbackInfo<DestroyParam> &info);
172173

Collapse file

‎src/debug_utils.h‎

Copy file name to clipboard
+92Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#ifndef SRC_DEBUG_UTILS_H_
2+
#define SRC_DEBUG_UTILS_H_
3+
4+
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5+
6+
#include "async_wrap.h"
7+
#include "env.h"
8+
#include <string>
9+
10+
// Use FORCE_INLINE on functions that have a debug-category-enabled check first
11+
// and then ideally only a single function call following it, to maintain
12+
// performance for the common case (no debugging used).
13+
#ifdef __GNUC__
14+
#define FORCE_INLINE __attribute__((always_inline))
15+
#define COLD_NOINLINE __attribute__((cold, noinline))
16+
#else
17+
#define FORCE_INLINE
18+
#define COLD_NOINLINE
19+
#endif
20+
21+
namespace node {
22+
23+
template <typename... Args>
24+
inline void FORCE_INLINE Debug(Environment* env,
25+
DebugCategory cat,
26+
const char* format,
27+
Args&&... args) {
28+
if (!UNLIKELY(env->debug_enabled(cat)))
29+
return;
30+
fprintf(stderr, format, std::forward<Args>(args)...);
31+
}
32+
33+
inline void FORCE_INLINE Debug(Environment* env,
34+
DebugCategory cat,
35+
const char* message) {
36+
if (!UNLIKELY(env->debug_enabled(cat)))
37+
return;
38+
fprintf(stderr, "%s", message);
39+
}
40+
41+
template <typename... Args>
42+
inline void Debug(Environment* env,
43+
DebugCategory cat,
44+
const std::string& format,
45+
Args&&... args) {
46+
Debug(env, cat, format.c_str(), std::forward<Args>(args)...);
47+
}
48+
49+
// Used internally by the 'real' Debug(AsyncWrap*, ...) functions below, so that
50+
// the FORCE_INLINE flag on them doesn't apply to the contents of this function
51+
// as well.
52+
// We apply COLD_NOINLINE to tell the compiler that it's not worth optimizing
53+
// this function for speed and it should rather focus on keeping it out of
54+
// hot code paths. In particular, we want to keep the string concatenating code
55+
// out of the function containing the original `Debug()` call.
56+
template <typename... Args>
57+
void COLD_NOINLINE UnconditionalAsyncWrapDebug(AsyncWrap* async_wrap,
58+
const char* format,
59+
Args&&... args) {
60+
Debug(async_wrap->env(),
61+
static_cast<DebugCategory>(async_wrap->provider_type()),
62+
async_wrap->diagnostic_name() + " " + format + "\n",
63+
std::forward<Args>(args)...);
64+
}
65+
66+
template <typename... Args>
67+
inline void FORCE_INLINE Debug(AsyncWrap* async_wrap,
68+
const char* format,
69+
Args&&... args) {
70+
#ifdef DEBUG
71+
CHECK_NOT_NULL(async_wrap);
72+
#endif
73+
DebugCategory cat =
74+
static_cast<DebugCategory>(async_wrap->provider_type());
75+
if (!UNLIKELY(async_wrap->env()->debug_enabled(cat)))
76+
return;
77+
UnconditionalAsyncWrapDebug(async_wrap, format, std::forward<Args>(args)...);
78+
}
79+
80+
template <typename... Args>
81+
inline void FORCE_INLINE Debug(AsyncWrap* async_wrap,
82+
const std::string& format,
83+
Args&&... args) {
84+
Debug(async_wrap, format.c_str(), std::forward<Args>(args)...);
85+
}
86+
87+
88+
} // namespace node
89+
90+
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
91+
92+
#endif // SRC_DEBUG_UTILS_H_
Collapse file

‎src/env-inl.h‎

Copy file name to clipboardExpand all lines: src/env-inl.h
+18Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,24 @@ inline void Environment::set_http2_state(
519519
http2_state_ = std::move(buffer);
520520
}
521521

522+
bool Environment::debug_enabled(DebugCategory category) const {
523+
#ifdef DEBUG
524+
CHECK_GE(static_cast<int>(category), 0);
525+
CHECK_LT(static_cast<int>(category),
526+
static_cast<int>(DebugCategory::CATEGORY_COUNT));
527+
#endif
528+
return debug_enabled_[static_cast<int>(category)];
529+
}
530+
531+
void Environment::set_debug_enabled(DebugCategory category, bool enabled) {
532+
#ifdef DEBUG
533+
CHECK_GE(static_cast<int>(category), 0);
534+
CHECK_LT(static_cast<int>(category),
535+
static_cast<int>(DebugCategory::CATEGORY_COUNT));
536+
#endif
537+
debug_enabled_[static_cast<int>(category)] = enabled;
538+
}
539+
522540
inline AliasedBuffer<double, v8::Float64Array>*
523541
Environment::fs_stats_field_array() {
524542
return &fs_stats_field_array_;
Collapse file

‎src/env.cc‎

Copy file name to clipboardExpand all lines: src/env.cc
+26Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ Environment::Environment(IsolateData* isolate_data,
130130

131131
// By default, always abort when --abort-on-uncaught-exception was passed.
132132
should_abort_on_uncaught_toggle_[0] = 1;
133+
134+
std::string debug_cats;
135+
SafeGetenv("NODE_DEBUG_NATIVE", &debug_cats);
136+
set_debug_categories(debug_cats, true);
133137
}
134138

135139
Environment::~Environment() {
@@ -496,6 +500,28 @@ Local<Value> Environment::GetNow() {
496500
}
497501

498502

503+
void Environment::set_debug_categories(const std::string& cats, bool enabled) {
504+
std::string debug_categories = cats;
505+
while (!debug_categories.empty()) {
506+
std::string::size_type comma_pos = debug_categories.find(',');
507+
std::string wanted = ToLower(debug_categories.substr(0, comma_pos));
508+
509+
#define V(name) \
510+
{ \
511+
static const std::string available_category = ToLower(#name); \
512+
if (available_category.find(wanted) != std::string::npos) \
513+
set_debug_enabled(DebugCategory::name, enabled); \
514+
}
515+
516+
DEBUG_CATEGORY_NAMES(V)
517+
518+
if (comma_pos == std::string::npos)
519+
break;
520+
// Use everything after the `,` as the list for the next iteration.
521+
debug_categories = debug_categories.substr(comma_pos);
522+
}
523+
}
524+
499525
void CollectExceptionInfo(Environment* env,
500526
v8::Local<v8::Object> obj,
501527
int errorno,
Collapse file

‎src/env.h‎

Copy file name to clipboardExpand all lines: src/env.h
+19Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,19 @@ struct ContextInfo {
398398
bool is_default = false;
399399
};
400400

401+
// Listing the AsyncWrap provider types first enables us to cast directly
402+
// from a provider type to a debug category. Currently no other debug
403+
// categories are available.
404+
#define DEBUG_CATEGORY_NAMES(V) \
405+
NODE_ASYNC_PROVIDER_TYPES(V)
406+
407+
enum class DebugCategory {
408+
#define V(name) name,
409+
DEBUG_CATEGORY_NAMES(V)
410+
#undef V
411+
CATEGORY_COUNT
412+
};
413+
401414
class Environment {
402415
public:
403416
class AsyncHooks {
@@ -654,6 +667,10 @@ class Environment {
654667
inline http2::Http2State* http2_state() const;
655668
inline void set_http2_state(std::unique_ptr<http2::Http2State> state);
656669

670+
inline bool debug_enabled(DebugCategory category) const;
671+
inline void set_debug_enabled(DebugCategory category, bool enabled);
672+
void set_debug_categories(const std::string& cats, bool enabled);
673+
657674
inline AliasedBuffer<double, v8::Float64Array>* fs_stats_field_array();
658675

659676
// stat fields contains twice the number of entries because `fs.StatWatcher`
@@ -853,6 +870,8 @@ class Environment {
853870
bool http_parser_buffer_in_use_ = false;
854871
std::unique_ptr<http2::Http2State> http2_state_;
855872

873+
bool debug_enabled_[static_cast<int>(DebugCategory::CATEGORY_COUNT)] = {0};
874+
856875
AliasedBuffer<double, v8::Float64Array> fs_stats_field_array_;
857876

858877
std::vector<std::unique_ptr<fs::FileHandleReadWrap>>
Collapse file

‎src/node.cc‎

Copy file name to clipboardExpand all lines: src/node.cc
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3302,6 +3302,9 @@ static void PrintHelp() {
33023302
"Environment variables:\n"
33033303
"NODE_DEBUG ','-separated list of core modules\n"
33043304
" that should print debug information\n"
3305+
"NODE_DEBUG_NATIVE ','-separated list of C++ core debug\n"
3306+
" categories that should print debug\n"
3307+
" output\n"
33053308
"NODE_DISABLE_COLORS set to 1 to disable colors in the REPL\n"
33063309
"NODE_EXTRA_CA_CERTS path to additional CA certificates\n"
33073310
" file\n"
Collapse file

‎src/util-inl.h‎

Copy file name to clipboardExpand all lines: src/util-inl.h
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,13 @@ char ToLower(char c) {
293293
return c >= 'A' && c <= 'Z' ? c + ('a' - 'A') : c;
294294
}
295295

296+
std::string ToLower(const std::string& in) {
297+
std::string out(in.size(), 0);
298+
for (size_t i = 0; i < in.size(); ++i)
299+
out[i] = ToLower(in[i]);
300+
return out;
301+
}
302+
296303
bool StringEqualNoCase(const char* a, const char* b) {
297304
do {
298305
if (*a == '\0')
Collapse file

‎src/util.h‎

Copy file name to clipboardExpand all lines: src/util.h
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include <stdlib.h>
3535
#include <string.h>
3636

37+
#include <string>
3738
#include <functional> // std::function
3839

3940
namespace node {
@@ -250,6 +251,7 @@ inline void SwapBytes64(char* data, size_t nbytes);
250251

251252
// tolower() is locale-sensitive. Use ToLower() instead.
252253
inline char ToLower(char c);
254+
inline std::string ToLower(const std::string& in);
253255

254256
// strcasecmp() is locale-sensitive. Use StringEqualNoCase() instead.
255257
inline bool StringEqualNoCase(const char* a, const char* b);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.