Skip to content
Closed
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,7 @@
'src/connect_wrap.h',
'src/connection_wrap.h',
'src/debug_utils.h',
'src/debug_utils-inl.h',
'src/env.h',
'src/env-inl.h',
'src/handle_wrap.h',
Expand Down
95 changes: 95 additions & 0 deletions src/debug_utils-inl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#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 <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; }
static std::string Convert(const std::string& value) { return value; }
static std::string Convert(bool value) { return value ? "true" : "false"; }
};

template <typename T>
std::string ToString(const T& value) {
return ToStringHelper::Convert(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);
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 '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)...));
}

} // namespace node

#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#endif // SRC_DEBUG_UTILS_INL_H_
44 changes: 43 additions & 1 deletion src/debug_utils.cc
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
#include "debug_utils.h"
#include "debug_utils-inl.h" // NOLINT(build/include)
#include "env-inl.h"

#ifdef __POSIX__
#if defined(__linux__)
#include <features.h>
#endif

#ifdef __ANDROID__
#include <android/log.h>
#endif

#if defined(__linux__) && !defined(__GLIBC__) || \
defined(__UCLIBC__) || \
defined(_AIX)
Expand Down Expand Up @@ -437,6 +441,44 @@ std::vector<std::string> NativeSymbolDebuggingContext::GetLoadedLibraries() {
return list;
}

void FWrite(FILE* file, const std::string& str) {
auto simple_fwrite = [&]() {
fwrite(str.data(), str.size(), 1, file);
};

if (file != stderr && file != stdout) {
simple_fwrite();
return;
}
#ifdef _WIN32
HANDLE handle =
GetStdHandle(file == stdout ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);

// Check if stderr is something other than a tty/console
if (handle == INVALID_HANDLE_VALUE || handle == nullptr ||
uv_guess_handle(_fileno(file)) != UV_TTY) {
simple_fwrite();
return;
}

// Get required wide buffer size
int n = MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), nullptr, 0);

std::vector<wchar_t> wbuf(n);
MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), wbuf.data(), n);

// Don't include the final null character in the output
CHECK_GT(n, 0);
WriteConsoleW(handle, wbuf.data(), n - 1, nullptr, nullptr);
return;
#elif defined(__ANDROID__)
if (file == stderr) {
__android_log_print(ANDROID_LOG_ERROR, "nodejs", "%s", str.data());
return;
}
#endif
simple_fwrite();
}

} // namespace node

Expand Down
18 changes: 16 additions & 2 deletions src/debug_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,36 @@

namespace node {

template <typename T>
inline std::string ToString(const T& value);

// C++-style variant of sprintf()/fprintf() that:
// - Returns an std::string
// - Handles \0 bytes correctly
// - Supports %p and %s. %d, %i and %u are aliases for %s.
// - Accepts any class that has a ToString() method for stringification.
template <typename... Args>
inline std::string SPrintF(const char* format, Args&&... args);
template <typename... Args>
inline void FPrintF(FILE* file, const char* format, Args&&... args);
void FWrite(FILE* file, const std::string& str);

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)...);
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);
FPrintF(stderr, "%s", message);
}

template <typename... Args>
Expand Down
2 changes: 1 addition & 1 deletion src/inspector_io.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include "inspector/main_thread_interface.h"
#include "inspector/node_string.h"
#include "base_object-inl.h"
#include "debug_utils.h"
#include "debug_utils-inl.h"
#include "node.h"
#include "node_crypto.h"
#include "node_internals.h"
Expand Down
2 changes: 1 addition & 1 deletion src/inspector_profiler.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include "inspector_profiler.h"
#include "base_object-inl.h"
#include "debug_utils.h"
#include "debug_utils-inl.h"
#include "diagnosticfilename-inl.h"
#include "memory_tracker-inl.h"
#include "node_file.h"
Expand Down
2 changes: 1 addition & 1 deletion src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

// ========== local headers ==========

#include "debug_utils.h"
#include "debug_utils-inl.h"
#include "env-inl.h"
#include "memory_tracker-inl.h"
#include "node_binding.h"
Expand Down
Loading