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