mirror of
https://github.com/zebrajr/node.git
synced 2026-01-15 12:15:26 +00:00
Right now, we crash the process if there are handles remaining on the event loop when we exit (except for the main thread). This does not provide a lot of information about causes, though; in particular, we don’t show which handles are pending and who own them. This patch adds debug output to these cases to help with the situation. PR-URL: https://github.com/nodejs/node/pull/21238 Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
116 lines
3.8 KiB
C++
116 lines
3.8 KiB
C++
#ifndef SRC_DEBUG_UTILS_H_
|
|
#define SRC_DEBUG_UTILS_H_
|
|
|
|
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
|
|
|
#include "async_wrap.h"
|
|
#include "env.h"
|
|
#include <string>
|
|
|
|
// Use FORCE_INLINE on functions that have a debug-category-enabled check first
|
|
// and then ideally only a single function call following it, to maintain
|
|
// performance for the common case (no debugging used).
|
|
#ifdef __GNUC__
|
|
#define FORCE_INLINE __attribute__((always_inline))
|
|
#define COLD_NOINLINE __attribute__((cold, noinline))
|
|
#else
|
|
#define FORCE_INLINE
|
|
#define COLD_NOINLINE
|
|
#endif
|
|
|
|
namespace node {
|
|
|
|
template <typename... Args>
|
|
inline void FORCE_INLINE Debug(Environment* env,
|
|
DebugCategory cat,
|
|
const char* format,
|
|
Args&&... args) {
|
|
if (!UNLIKELY(env->debug_enabled(cat)))
|
|
return;
|
|
fprintf(stderr, format, std::forward<Args>(args)...);
|
|
}
|
|
|
|
inline void FORCE_INLINE Debug(Environment* env,
|
|
DebugCategory cat,
|
|
const char* message) {
|
|
if (!UNLIKELY(env->debug_enabled(cat)))
|
|
return;
|
|
fprintf(stderr, "%s", message);
|
|
}
|
|
|
|
template <typename... Args>
|
|
inline void Debug(Environment* env,
|
|
DebugCategory cat,
|
|
const std::string& format,
|
|
Args&&... args) {
|
|
Debug(env, cat, format.c_str(), std::forward<Args>(args)...);
|
|
}
|
|
|
|
// Used internally by the 'real' Debug(AsyncWrap*, ...) functions below, so that
|
|
// the FORCE_INLINE flag on them doesn't apply to the contents of this function
|
|
// as well.
|
|
// We apply COLD_NOINLINE to tell the compiler that it's not worth optimizing
|
|
// this function for speed and it should rather focus on keeping it out of
|
|
// hot code paths. In particular, we want to keep the string concatenating code
|
|
// out of the function containing the original `Debug()` call.
|
|
template <typename... Args>
|
|
void COLD_NOINLINE UnconditionalAsyncWrapDebug(AsyncWrap* async_wrap,
|
|
const char* format,
|
|
Args&&... args) {
|
|
Debug(async_wrap->env(),
|
|
static_cast<DebugCategory>(async_wrap->provider_type()),
|
|
async_wrap->diagnostic_name() + " " + format + "\n",
|
|
std::forward<Args>(args)...);
|
|
}
|
|
|
|
template <typename... Args>
|
|
inline void FORCE_INLINE Debug(AsyncWrap* async_wrap,
|
|
const char* format,
|
|
Args&&... args) {
|
|
#ifdef DEBUG
|
|
CHECK_NOT_NULL(async_wrap);
|
|
#endif
|
|
DebugCategory cat =
|
|
static_cast<DebugCategory>(async_wrap->provider_type());
|
|
if (!UNLIKELY(async_wrap->env()->debug_enabled(cat)))
|
|
return;
|
|
UnconditionalAsyncWrapDebug(async_wrap, format, std::forward<Args>(args)...);
|
|
}
|
|
|
|
template <typename... Args>
|
|
inline void FORCE_INLINE Debug(AsyncWrap* async_wrap,
|
|
const std::string& format,
|
|
Args&&... args) {
|
|
Debug(async_wrap, format.c_str(), std::forward<Args>(args)...);
|
|
}
|
|
|
|
// Debug helper for inspecting the currently running `node` executable.
|
|
class NativeSymbolDebuggingContext {
|
|
public:
|
|
static std::unique_ptr<NativeSymbolDebuggingContext> New();
|
|
|
|
class SymbolInfo {
|
|
public:
|
|
std::string name;
|
|
std::string filename;
|
|
|
|
std::string Display() const;
|
|
};
|
|
|
|
virtual ~NativeSymbolDebuggingContext() {}
|
|
virtual SymbolInfo LookupSymbol(void* address) { return { "", "" }; }
|
|
virtual bool IsMapped(void* address) { return false; }
|
|
virtual int GetStackTrace(void** frames, int count) { return 0; }
|
|
};
|
|
|
|
// Variant of `uv_loop_close` that tries to be as helpful as possible
|
|
// about giving information on currently existing handles, if there are any,
|
|
// but still aborts the process.
|
|
void CheckedUvLoopClose(uv_loop_t* loop);
|
|
|
|
} // namespace node
|
|
|
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
|
|
|
#endif // SRC_DEBUG_UTILS_H_
|