mirror of
https://github.com/zebrajr/node.git
synced 2026-01-15 12:15:26 +00:00
Similar to 2e2f4cd095, just missed them in that commit.
PR-URL: https://github.com/nodejs/node/pull/60592
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
Reviewed-By: Juan José Arboleda <soyjuanarbol@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
279 lines
9.1 KiB
C++
279 lines
9.1 KiB
C++
#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 "util-inl.h"
|
|
|
|
#include <type_traits>
|
|
|
|
namespace node {
|
|
|
|
template <typename T>
|
|
concept StringViewConvertible = requires(T a) {
|
|
{
|
|
a.ToStringView()
|
|
} -> std::convertible_to<std::string_view>;
|
|
};
|
|
template <typename T>
|
|
concept StringConvertible = requires(T a) {
|
|
{
|
|
a.ToString()
|
|
} -> std::convertible_to<std::string>;
|
|
};
|
|
// For std::filesystem::path and similar types
|
|
template <typename T>
|
|
concept StringConvertibleFSPathLike = requires(T a) {
|
|
{
|
|
a.string()
|
|
} -> std::convertible_to<std::string>;
|
|
};
|
|
|
|
struct ToStringHelper {
|
|
template <typename T>
|
|
requires(StringConvertible<T>) && (!StringViewConvertible<T>)
|
|
static std::string Convert(const T& value) {
|
|
return value.ToString();
|
|
}
|
|
template <typename T>
|
|
requires(StringConvertibleFSPathLike<T>) && (!StringViewConvertible<T>) &&
|
|
(!StringConvertible<T>)
|
|
static std::string Convert(const T& value) {
|
|
return value.string();
|
|
}
|
|
template <typename T>
|
|
requires StringViewConvertible<T>
|
|
static std::string_view Convert(const T& value) {
|
|
return value.ToStringView();
|
|
}
|
|
|
|
template <typename T,
|
|
typename test_for_number = typename std::
|
|
enable_if_t<std::is_arithmetic_v<T> || std::is_enum_v<T>, bool>,
|
|
typename dummy = bool>
|
|
static std::string Convert(const T& value) {
|
|
return std::to_string(value);
|
|
}
|
|
static std::string_view Convert(const char* value) {
|
|
return value != nullptr ? value : "(null)";
|
|
}
|
|
static std::string Convert(const std::string& value) { return value; }
|
|
static std::string_view Convert(std::string_view value) { return value; }
|
|
static std::string Convert(bool value) { return value ? "true" : "false"; }
|
|
template <unsigned BASE_BITS,
|
|
typename T,
|
|
typename = std::enable_if_t<std::is_integral_v<T>>>
|
|
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_t<!std::is_integral_v<T>>>
|
|
static auto BaseConvert(T&& value) {
|
|
return Convert(std::forward<T>(value));
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
auto ToStringOrStringView(const T& value) {
|
|
return ToStringHelper::Convert(value);
|
|
}
|
|
|
|
template <typename T>
|
|
std::string ToString(const T& value) {
|
|
return std::string(ToStringOrStringView(value));
|
|
}
|
|
|
|
template <unsigned BASE_BITS, typename T>
|
|
auto ToBaseString(const T& value) {
|
|
return ToStringHelper::BaseConvert<BASE_BITS>(value);
|
|
}
|
|
|
|
inline std::string SPrintFImpl(std::string_view format) {
|
|
auto offset = format.find('%');
|
|
if (offset == std::string_view::npos) return std::string(format);
|
|
CHECK_LT(offset + 1, format.size());
|
|
CHECK_EQ(format[offset + 1],
|
|
'%'); // Only '%%' allowed when there are no arguments.
|
|
|
|
return std::string(format.substr(0, offset + 1)) +
|
|
SPrintFImpl(format.substr(offset + 2));
|
|
}
|
|
|
|
template <typename Arg, typename... Args>
|
|
std::string COLD_NOINLINE SPrintFImpl( // NOLINT(runtime/string)
|
|
std::string_view format,
|
|
Arg&& arg,
|
|
Args&&... args) {
|
|
auto offset = format.find('%');
|
|
CHECK_NE(offset, std::string_view::npos); // If you hit this, you passed in
|
|
// too many arguments.
|
|
std::string ret(format.substr(0, offset));
|
|
// Ignore long / size_t modifiers
|
|
while (++offset < format.size() &&
|
|
(format[offset] == 'l' || format[offset] == 'z')) {
|
|
}
|
|
switch (offset == format.size() ? '\0' : format[offset]) {
|
|
case '%': {
|
|
return ret + '%' +
|
|
SPrintFImpl(format.substr(offset + 1),
|
|
std::forward<Arg>(arg),
|
|
std::forward<Args>(args)...);
|
|
}
|
|
default: {
|
|
return ret + '%' +
|
|
SPrintFImpl(format.substr(offset),
|
|
std::forward<Arg>(arg),
|
|
std::forward<Args>(args)...);
|
|
}
|
|
case 'd':
|
|
case 'i':
|
|
case 'u':
|
|
case 's':
|
|
ret += ToStringOrStringView(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_v<typename std::remove_reference_t<Arg>>);
|
|
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(format.substr(offset + 1), std::forward<Args>(args)...);
|
|
}
|
|
|
|
template <typename... Args>
|
|
std::string COLD_NOINLINE SPrintF( // NOLINT(runtime/string)
|
|
std::string_view format,
|
|
Args&&... args) {
|
|
return SPrintFImpl(format, std::forward<Args>(args)...);
|
|
}
|
|
|
|
template <typename... Args>
|
|
void COLD_NOINLINE FPrintF(FILE* file,
|
|
std::string_view 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 (!list->enabled(cat)) [[unlikely]]
|
|
return;
|
|
FPrintF(stderr, format, std::forward<Args>(args)...);
|
|
}
|
|
|
|
inline void FORCE_INLINE Debug(EnabledDebugList* list,
|
|
DebugCategory cat,
|
|
const char* message) {
|
|
if (!list->enabled(cat)) [[unlikely]]
|
|
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);
|
|
if (auto cat = static_cast<DebugCategory>(async_wrap->provider_type());
|
|
!async_wrap->env()->enabled_debug_list()->enabled(cat)) [[unlikely]] {
|
|
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_
|