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