blob: d87dd62c8952afb916bce2b5c25361f741f95725 [file] [log] [blame]
#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 <type_traits>
namespace node {
struct ToStringHelper {
template <typename T>
static std::string Convert(
const T& value,
std::string(T::* to_string)() const = &T::ToString) {
return (value.*to_string)();
}
template <typename T,
typename test_for_number = typename std::
enable_if<std::is_arithmetic<T>::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 <unsigned BASE_BITS,
typename T,
typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
static std::string BaseConvert(const T& value) {
auto v = static_cast<uint64_t>(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<char>('0' + digit) : digits[digit]);
} while ((v >>= BASE_BITS) != 0);
return ptr;
}
template <unsigned BASE_BITS,
typename T,
typename std::enable_if<!std::is_integral<T>::value, int>::type = 0>
static std::string BaseConvert(T value) {
return Convert(std::forward<T>(value));
}
};
template <typename T>
std::string ToString(const T& value) {
return ToStringHelper::Convert(value);
}
template <unsigned BASE_BITS, typename T>
std::string ToBaseString(const T& value) {
return ToStringHelper::BaseConvert<BASE_BITS>(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 <typename Arg, typename... Args>
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>(arg),
std::forward<Args>(args)...);
}
default: {
return ret + '%' + SPrintFImpl(p,
std::forward<Arg>(arg),
std::forward<Args>(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<typename std::remove_reference<Arg>::type>::value);
char out[20];
int n = snprintf(out,
sizeof(out),
"%p",
*reinterpret_cast<const void* const*>(&arg));
CHECK_GE(n, 0);
ret += out;
break;
}
}
return ret + SPrintFImpl(p + 1, std::forward<Args>(args)...);
}
template <typename... Args>
std::string COLD_NOINLINE SPrintF( // NOLINT(runtime/string)
const char* format, Args&&... args) {
return SPrintFImpl(format, std::forward<Args>(args)...);
}
template <typename... Args>
void COLD_NOINLINE FPrintF(FILE* file, const char* format, Args&&... args) {
FWrite(file, SPrintF(format, std::forward<Args>(args)...));
}
template <typename... Args>
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>(args)...);
}
inline void FORCE_INLINE Debug(EnabledDebugList* list,
DebugCategory cat,
const char* message) {
if (!UNLIKELY(list->enabled(cat))) return;
FPrintF(stderr, "%s", message);
}
template <typename... Args>
inline void FORCE_INLINE
Debug(Environment* env, DebugCategory cat, const char* format, Args&&... args) {
Debug(env->enabled_debug_list(), cat, format, std::forward<Args>(args)...);
}
inline void FORCE_INLINE Debug(Environment* env,
DebugCategory cat,
const char* message) {
Debug(env->enabled_debug_list(), cat, message);
}
template <typename... Args>
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>(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) {
DCHECK_NOT_NULL(async_wrap);
DebugCategory cat = static_cast<DebugCategory>(async_wrap->provider_type());
if (!UNLIKELY(async_wrap->env()->enabled_debug_list()->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)...);
}
namespace per_process {
template <typename... Args>
inline void FORCE_INLINE Debug(DebugCategory cat,
const char* format,
Args&&... args) {
Debug(&enabled_debug_list, cat, format, std::forward<Args>(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_