1 #ifndef SRC_DEBUG_UTILS_INL_H_
2 #define SRC_DEBUG_UTILS_INL_H_
3
4 #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5
6 #include "debug_utils.h"
7 #include "env.h"
8
9 #include <type_traits>
10
11 namespace node {
12
13 struct ToStringHelper {
14 template <typename T>
Convertnode::ToStringHelper15 static std::string Convert(
16 const T& value,
17 std::string(T::* to_string)() const = &T::ToString) {
18 return (value.*to_string)();
19 }
20 template <typename T,
21 typename test_for_number = typename std::
22 enable_if<std::is_arithmetic<T>::value, bool>::type,
23 typename dummy = bool>
Convertnode::ToStringHelper24 static std::string Convert(const T& value) { return std::to_string(value); }
Convertnode::ToStringHelper25 static std::string Convert(const char* value) {
26 return value != nullptr ? value : "(null)";
27 }
Convertnode::ToStringHelper28 static std::string Convert(const std::string& value) { return value; }
Convertnode::ToStringHelper29 static std::string Convert(std::string_view value) {
30 return std::string(value);
31 }
Convertnode::ToStringHelper32 static std::string Convert(bool value) { return value ? "true" : "false"; }
33 template <unsigned BASE_BITS,
34 typename T,
35 typename = std::enable_if_t<std::is_integral_v<T>>>
BaseConvertnode::ToStringHelper36 static std::string BaseConvert(const T& value) {
37 auto v = static_cast<uint64_t>(value);
38 char ret[3 * sizeof(T)];
39 char* ptr = ret + 3 * sizeof(T) - 1;
40 *ptr = '\0';
41 const char* digits = "0123456789abcdef";
42 do {
43 unsigned digit = v & ((1 << BASE_BITS) - 1);
44 *--ptr =
45 (BASE_BITS < 4 ? static_cast<char>('0' + digit) : digits[digit]);
46 } while ((v >>= BASE_BITS) != 0);
47 return ptr;
48 }
49 template <unsigned BASE_BITS,
50 typename T,
51 typename = std::enable_if_t<!std::is_integral_v<T>>>
BaseConvertnode::ToStringHelper52 static std::string BaseConvert(T value) {
53 return Convert(std::forward<T>(value));
54 }
55 };
56
57 template <typename T>
ToString(const T& value)58 std::string ToString(const T& value) {
59 return ToStringHelper::Convert(value);
60 }
61
62 template <unsigned BASE_BITS, typename T>
ToBaseString(const T& value)63 std::string ToBaseString(const T& value) {
64 return ToStringHelper::BaseConvert<BASE_BITS>(value);
65 }
66
SPrintFImpl(const char* format)67 inline std::string SPrintFImpl(const char* format) {
68 const char* p = strchr(format, '%');
69 if (LIKELY(p == nullptr)) return format;
70 CHECK_EQ(p[1], '%'); // Only '%%' allowed when there are no arguments.
71
72 return std::string(format, p + 1) + SPrintFImpl(p + 2);
73 }
74
75 template <typename Arg, typename... Args>
SPrintFImpl( const char* format, Arg&& arg, Args&&... args)76 std::string COLD_NOINLINE SPrintFImpl( // NOLINT(runtime/string)
77 const char* format, Arg&& arg, Args&&... args) {
78 const char* p = strchr(format, '%');
79 CHECK_NOT_NULL(p); // If you hit this, you passed in too many arguments.
80 std::string ret(format, p);
81 // Ignore long / size_t modifiers
82 while (strchr("lz", *++p) != nullptr) {}
83 switch (*p) {
84 case '%': {
85 return ret + '%' + SPrintFImpl(p + 1,
86 std::forward<Arg>(arg),
87 std::forward<Args>(args)...);
88 }
89 default: {
90 return ret + '%' + SPrintFImpl(p,
91 std::forward<Arg>(arg),
92 std::forward<Args>(args)...);
93 }
94 case 'd':
95 case 'i':
96 case 'u':
97 case 's':
98 ret += ToString(arg);
99 break;
100 case 'o':
101 ret += ToBaseString<3>(arg);
102 break;
103 case 'x':
104 ret += ToBaseString<4>(arg);
105 break;
106 case 'X':
107 ret += node::ToUpper(ToBaseString<4>(arg));
108 break;
109 case 'p': {
110 CHECK(std::is_pointer<typename std::remove_reference<Arg>::type>::value);
111 char out[20];
112 int n = snprintf(out,
113 sizeof(out),
114 "%p",
115 *reinterpret_cast<const void* const*>(&arg));
116 CHECK_GE(n, 0);
117 ret += out;
118 break;
119 }
120 }
121 return ret + SPrintFImpl(p + 1, std::forward<Args>(args)...);
122 }
123
124 template <typename... Args>
SPrintF( const char* format, Args&&... args)125 std::string COLD_NOINLINE SPrintF( // NOLINT(runtime/string)
126 const char* format, Args&&... args) {
127 return SPrintFImpl(format, std::forward<Args>(args)...);
128 }
129
130 template <typename... Args>
FPrintF(FILE* file, const char* format, Args&&... args)131 void COLD_NOINLINE FPrintF(FILE* file, const char* format, Args&&... args) {
132 FWrite(file, SPrintF(format, std::forward<Args>(args)...));
133 }
134
135 template <typename... Args>
Debug(EnabledDebugList* list, DebugCategory cat, const char* format, Args&&... args)136 inline void FORCE_INLINE Debug(EnabledDebugList* list,
137 DebugCategory cat,
138 const char* format,
139 Args&&... args) {
140 if (!UNLIKELY(list->enabled(cat))) return;
141 FPrintF(stderr, format, std::forward<Args>(args)...);
142 }
143
Debug(EnabledDebugList* list, DebugCategory cat, const char* message)144 inline void FORCE_INLINE Debug(EnabledDebugList* list,
145 DebugCategory cat,
146 const char* message) {
147 if (!UNLIKELY(list->enabled(cat))) return;
148 FPrintF(stderr, "%s", message);
149 }
150
151 template <typename... Args>
152 inline void FORCE_INLINE
Debug(Environment* env, DebugCategory cat, const char* format, Args&&... args)153 Debug(Environment* env, DebugCategory cat, const char* format, Args&&... args) {
154 Debug(env->enabled_debug_list(), cat, format, std::forward<Args>(args)...);
155 }
156
Debug(Environment* env, DebugCategory cat, const char* message)157 inline void FORCE_INLINE Debug(Environment* env,
158 DebugCategory cat,
159 const char* message) {
160 Debug(env->enabled_debug_list(), cat, message);
161 }
162
163 template <typename... Args>
Debug(Environment* env, DebugCategory cat, const std::string& format, Args&&... args)164 inline void Debug(Environment* env,
165 DebugCategory cat,
166 const std::string& format,
167 Args&&... args) {
168 Debug(env->enabled_debug_list(),
169 cat,
170 format.c_str(),
171 std::forward<Args>(args)...);
172 }
173
174 // Used internally by the 'real' Debug(AsyncWrap*, ...) functions below, so that
175 // the FORCE_INLINE flag on them doesn't apply to the contents of this function
176 // as well.
177 // We apply COLD_NOINLINE to tell the compiler that it's not worth optimizing
178 // this function for speed and it should rather focus on keeping it out of
179 // hot code paths. In particular, we want to keep the string concatenating code
180 // out of the function containing the original `Debug()` call.
181 template <typename... Args>
UnconditionalAsyncWrapDebug(AsyncWrap* async_wrap, const char* format, Args&&... args)182 void COLD_NOINLINE UnconditionalAsyncWrapDebug(AsyncWrap* async_wrap,
183 const char* format,
184 Args&&... args) {
185 Debug(async_wrap->env(),
186 static_cast<DebugCategory>(async_wrap->provider_type()),
187 async_wrap->diagnostic_name() + " " + format + "\n",
188 std::forward<Args>(args)...);
189 }
190
191 template <typename... Args>
Debug(AsyncWrap* async_wrap, const char* format, Args&&... args)192 inline void FORCE_INLINE Debug(AsyncWrap* async_wrap,
193 const char* format,
194 Args&&... args) {
195 DCHECK_NOT_NULL(async_wrap);
196 DebugCategory cat = static_cast<DebugCategory>(async_wrap->provider_type());
197 if (!UNLIKELY(async_wrap->env()->enabled_debug_list()->enabled(cat))) return;
198 UnconditionalAsyncWrapDebug(async_wrap, format, std::forward<Args>(args)...);
199 }
200
201 template <typename... Args>
Debug(AsyncWrap* async_wrap, const std::string& format, Args&&... args)202 inline void FORCE_INLINE Debug(AsyncWrap* async_wrap,
203 const std::string& format,
204 Args&&... args) {
205 Debug(async_wrap, format.c_str(), std::forward<Args>(args)...);
206 }
207
208 namespace per_process {
209
210 template <typename... Args>
Debug(DebugCategory cat, const char* format, Args&&... args)211 inline void FORCE_INLINE Debug(DebugCategory cat,
212 const char* format,
213 Args&&... args) {
214 Debug(&enabled_debug_list, cat, format, std::forward<Args>(args)...);
215 }
216
Debug(DebugCategory cat, const char* message)217 inline void FORCE_INLINE Debug(DebugCategory cat, const char* message) {
218 Debug(&enabled_debug_list, cat, message);
219 }
220
221 } // namespace per_process
222 } // namespace node
223
224 #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
225
226 #endif // SRC_DEBUG_UTILS_INL_H_
227