#ifndef SRC_DEBUG_UTILS_INL_H_ #define SRC_DEBUG_UTILS_INL_H_ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include "debug_utils.h" #include "env.h" #include namespace node { struct ToStringHelper { template static std::string Convert( const T& value, std::string(T::* to_string)() const = &T::ToString) { return (value.*to_string)(); } template ::value, bool>::type, typename dummy = bool> static std::string Convert(const T& value) { return std::to_string(value); } static std::string Convert(const char* value) { return value != nullptr ? value : "(null)"; } static std::string Convert(const std::string& value) { return value; } static std::string Convert(bool value) { return value ? "true" : "false"; } template ::value, int>::type = 0> static std::string BaseConvert(const T& value) { auto v = static_cast(value); char ret[3 * sizeof(T)]; char* ptr = ret + 3 * sizeof(T) - 1; *ptr = '\0'; const char* digits = "0123456789abcdef"; do { unsigned digit = v & ((1 << BASE_BITS) - 1); *--ptr = (BASE_BITS < 4 ? static_cast('0' + digit) : digits[digit]); } while ((v >>= BASE_BITS) != 0); return ptr; } template ::value, int>::type = 0> static std::string BaseConvert(T value) { return Convert(std::forward(value)); } }; template std::string ToString(const T& value) { return ToStringHelper::Convert(value); } template std::string ToBaseString(const T& value) { return ToStringHelper::BaseConvert(value); } inline std::string SPrintFImpl(const char* format) { const char* p = strchr(format, '%'); if (LIKELY(p == nullptr)) return format; CHECK_EQ(p[1], '%'); // Only '%%' allowed when there are no arguments. return std::string(format, p + 1) + SPrintFImpl(p + 2); } template std::string COLD_NOINLINE SPrintFImpl( // NOLINT(runtime/string) const char* format, Arg&& arg, Args&&... args) { const char* p = strchr(format, '%'); CHECK_NOT_NULL(p); // If you hit this, you passed in too many arguments. std::string ret(format, p); // Ignore long / size_t modifiers while (strchr("lz", *++p) != nullptr) {} switch (*p) { case '%': { return ret + '%' + SPrintFImpl(p + 1, std::forward(arg), std::forward(args)...); } default: { return ret + '%' + SPrintFImpl(p, std::forward(arg), std::forward(args)...); } case 'd': case 'i': case 'u': case 's': ret += ToString(arg); break; case 'o': ret += ToBaseString<3>(arg); break; case 'x': ret += ToBaseString<4>(arg); break; case 'X': ret += node::ToUpper(ToBaseString<4>(arg)); break; case 'p': { CHECK(std::is_pointer::type>::value); char out[20]; int n = snprintf(out, sizeof(out), "%p", *reinterpret_cast(&arg)); CHECK_GE(n, 0); ret += out; break; } } return ret + SPrintFImpl(p + 1, std::forward(args)...); } template std::string COLD_NOINLINE SPrintF( // NOLINT(runtime/string) const char* format, Args&&... args) { return SPrintFImpl(format, std::forward(args)...); } template void COLD_NOINLINE FPrintF(FILE* file, const char* format, Args&&... args) { FWrite(file, SPrintF(format, std::forward(args)...)); } template inline void FORCE_INLINE Debug(EnabledDebugList* list, DebugCategory cat, const char* format, Args&&... args) { if (!UNLIKELY(list->enabled(cat))) return; FPrintF(stderr, format, std::forward(args)...); } inline void FORCE_INLINE Debug(EnabledDebugList* list, DebugCategory cat, const char* message) { if (!UNLIKELY(list->enabled(cat))) return; FPrintF(stderr, "%s", message); } template inline void FORCE_INLINE Debug(Environment* env, DebugCategory cat, const char* format, Args&&... args) { Debug(env->enabled_debug_list(), cat, format, std::forward(args)...); } inline void FORCE_INLINE Debug(Environment* env, DebugCategory cat, const char* message) { Debug(env->enabled_debug_list(), cat, message); } template inline void Debug(Environment* env, DebugCategory cat, const std::string& format, Args&&... args) { Debug(env->enabled_debug_list(), cat, format.c_str(), std::forward(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 void COLD_NOINLINE UnconditionalAsyncWrapDebug(AsyncWrap* async_wrap, const char* format, Args&&... args) { Debug(async_wrap->env(), static_cast(async_wrap->provider_type()), async_wrap->diagnostic_name() + " " + format + "\n", std::forward(args)...); } template inline void FORCE_INLINE Debug(AsyncWrap* async_wrap, const char* format, Args&&... args) { DCHECK_NOT_NULL(async_wrap); DebugCategory cat = static_cast(async_wrap->provider_type()); if (!UNLIKELY(async_wrap->env()->enabled_debug_list()->enabled(cat))) return; UnconditionalAsyncWrapDebug(async_wrap, format, std::forward(args)...); } template inline void FORCE_INLINE Debug(AsyncWrap* async_wrap, const std::string& format, Args&&... args) { Debug(async_wrap, format.c_str(), std::forward(args)...); } namespace per_process { template inline void FORCE_INLINE Debug(DebugCategory cat, const char* format, Args&&... args) { Debug(&enabled_debug_list, cat, format, std::forward(args)...); } inline void FORCE_INLINE Debug(DebugCategory cat, const char* message) { Debug(&enabled_debug_list, cat, message); } } // namespace per_process } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #endif // SRC_DEBUG_UTILS_INL_H_