cpp-common
common.hh
Go to the documentation of this file.
1 // -*- mode: c++; c-basic-offset: 2; -*-
2 
9 #pragma once
10 
11 #include "nvsl/constants.hh"
12 #include "nvsl/envvars.hh"
13 #include <chrono>
14 #include <fcntl.h>
15 #include <fstream>
16 #include <iomanip>
17 #include <ios>
18 #include <iostream>
19 #include <memory>
20 #include <sstream>
21 #include <stdexcept>
22 #include <string>
23 #include <sys/wait.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26 
27 #include <fnmatch.h>
28 
29 #define NVSL_NODISCARD [[nodiscard]]
30 #define NVSL_UNUSED __attribute__((unused))
31 #define NVSL_EXPORT __attribute__((visibility("default")))
32 #define NVSL_NOINLINE __attribute__((noinline))
33 
34 #define NVSL_BEGIN_IGNORE_WPEDANTIC \
35  _Pragma("GCC diagnostic push") \
36  _Pragma("GCC diagnostic ignored \"-Wpedantic\"")
37 
38 #define NVSL_END_IGNORE_WPEDANTIC _Pragma("GCC diagnostic pop")
39 
40 // Do magic! Creates a unique name using the line number
41 #define NVSL_LINE_NAME(prefix) NVSL_JOIN(prefix, __LINE__)
42 #define NVSL_JOIN(symbol1, symbol2) NVSL_DO_JOIN(symbol1, symbol2)
43 #define NVSL_DO_JOIN(symbol1, symbol2) symbol1##symbol2
44 
46 #define NVSL_GUARD(mtx) \
47  std::lock_guard<std::mutex> NVSL_LINE_NAME(nvsl_macro_lock_guard)((mtx))
48 
50 #define NVSL_GUARD_PTR(mtx) \
51  std::lock_guard<std::mutex> NVSL_LINE_NAME(nvsl_macro_lock_guard)((*mtx))
52 
53 static const char *NVSL_LOG_LEVEL_ENV NVSL_UNUSED =
54  (char *)("NVSL_LOG_LEVEL");
55 
56 static std::ofstream nullstream;
57 
58 extern std::ofstream log_st;
59 #ifdef RELEASE
60 #define DBG 0 && std::cerr
61 #elif defined(NVSL_SIMPLIFIED_TERM_IO)
62 #define DBG log_st
63 #else
64 #define DBG (std::cerr)
65 #endif
66 
67 namespace nvsl {
69  inline bool wildcard(const std::string &pat, const std::string &str) {
70  return not fnmatch(pat.c_str(), str.c_str(), 0);
71  }
72 
73  static inline bool is_log_enabled(int level) {
74  uint8_t log_lvl = 0;
75 
76  const char *val = std::getenv(NVSL_LOG_LEVEL_ENV);
77  if (val != nullptr) {
78  const std::string val_str = std::string(val);
79 
80  try {
81  log_lvl = std::stoul(val_str, nullptr, 10);
82 
83  if (log_lvl > 4) {
84  throw std::out_of_range("Valid values: [0-4]");
85  }
86  } catch (std::out_of_range &e) {
87  std::cerr << "LP FATAL: " << NVSL_LOG_LEVEL_ENV
88  << " is out of range. Valid values: [0-4]" << std::endl;
89  std::exit(1);
90  } catch (std::invalid_argument &e) {
91  std::cerr << "LP FATAL: "
92  << "Unable to parse " << NVSL_LOG_LEVEL_ENV
93  << " env variable." << std::endl;
94  std::exit(1);
95  }
96  }
97 
98  if (level <= log_lvl) {
99  return true;
100  } else {
101  return false;
102  }
103  }
104 
105  static inline bool is_caller_enabled(const std::string &caller) {
106  bool result = true;
107 
108  const char *val = std::getenv(NVSL_LOG_WILDCARD_ENV);
109  if (val != nullptr) {
110  const std::string val_str = std::string(val);
111 
112  result = wildcard(val_str, caller);
113  }
114 
115  return result;
116  }
117 } // namespace nvsl
118 
119 inline std::string nvsl_cur_time_str() {
120  std::stringstream ss;
121 
122  auto now = std::chrono::system_clock::now();
123  auto timeNow = std::chrono::system_clock::to_time_t(now);
124  auto time_str = std::string(std::ctime(&timeNow));
125 
126  ss << time_str.substr(0, time_str.length() - 1) << " [" << getpid() << "]";
127 
128  return ss.str();
129 }
130 
131 /* Log to stderr with a decorator */
132 struct DBGH {
133  bool enabled;
134  DBGH(const DBGH &obj) { this->enabled = obj.enabled; }
135 
136 #ifndef RELEASE
137  DBGH(uint8_t lvl, const char *caller = __builtin_FUNCTION()) {
138 #else
139  DBGH(uint8_t lvl) {
140 #endif
141 
142 #ifndef RELEASE
143  this->enabled =
144  nvsl::is_log_enabled(lvl) && nvsl::is_caller_enabled(caller);
145 
146  if (this->enabled) [[likely]] {
147 #ifdef NVSL_SIMPLIFIED_TERM_IO
148  DBG << lp_cur_time_str() << " | ";
149 #else
150  DBG << "[\x1B[1m" << std::setw(20) << std::string(caller) << "()"
151  << "\x1B[0m"
152  << "] ";
153 #endif // NVSL_SIMPLIFIED_TERM_IO
154  }
155 #endif // !RELEASE
156  }
157 
158  template <typename T>
159  friend const DBGH &operator<<(const DBGH &dbgh, const T &obj);
160 
161  friend const DBGH &operator<<(const DBGH &s,
162  std::ostream &(*f)(std::ostream &));
163  friend const DBGH &operator<<(const DBGH &s, std::ostream &(*f)(std::ios &));
164  friend const DBGH &operator<<(const DBGH &s,
165  std::ostream &(*f)(std::ios_base &));
166 };
167 
168 template <typename T>
169 inline const DBGH &operator<<(const DBGH &dbgh, const T &obj) {
170  if (dbgh.enabled) {
171  DBG << obj;
172  }
173 
174  return dbgh;
175 }
176 
177 inline const DBGH &operator<<(const DBGH &s,
178  std::ostream &(*f)(std::ostream &)) {
179 #ifndef RELEASE
180  if (s.enabled) f(DBG);
181 #endif
182  return s;
183 }
184 
185 inline const DBGH &operator<<(const DBGH &s, std::ostream &(*f)(std::ios &)) {
186 #ifndef RELEASE
187  if (s.enabled) f(DBG);
188 #endif
189  return s;
190 }
191 
192 inline const DBGH &operator<<(const DBGH &s,
193  std::ostream &(*f)(std::ios_base &)) {
194 #ifndef RELEASE
195  if (s.enabled) f(DBG);
196 #endif
197  return s;
198 }
199 
200 #ifdef RELEASE
201 
202 #define DBG 0 && std::cerr
203 #define DBGF(s)
204 #define DBGW (std::cerr << "Warning: ")
205 #define DBGE (std::cerr << "Error: ")
206 
207 #else
208 
209 /* Log current function to stderr with a decorator */
210 #define DBGF(l) \
211  (DBGH(l) << std::string(__FUNCTION__) << "() called." << std::endl);
212 
213 /* Log warning to stderr with decorator */
214 #ifdef NVSL_SIMPLIFIED_TERM_IO
215 #define DBGW (DBGH(0) << "Warning: ")
216 #else
217 #define DBGW \
218  (DBG << "[\x1B[1m" << std::setw(20) << std::string(__FUNCTION__) << "()" \
219  << "\x1B[0m" \
220  << "]\x1B[95m WARNING: \x1B[0m")
221 #endif
222 
223 /* Log error to stderr with decorator */
224 #ifdef NVSL_SIMPLIFIED_TERM_IO
225 #define DBGE (DBG << "ERROR: \x1B[0m")
226 #else
227 #define DBGE \
228  (DBG << "[\x1B[31m" << std::setw(20) << std::string(__FUNCTION__) << "()" \
229  << "\x1B[0m" \
230  << "]\x1B[95m ERROR: \x1B[0m")
231 #endif
232 #endif
233 
234 namespace nvsl {
236  static inline std::string ptr_to_string(const void *addr) {
237  std::stringstream ss;
238 
239  ss << addr;
240 
241  return ss.str();
242  }
243 
245  inline constexpr void *align_cl(auto *ptr) {
246  uint8_t *bptr = (uint8_t *)ptr;
247  auto result = (void *)(((uint64_t)(bptr + 63) >> 6) << 6);
248 
249  DBGH(4) << "Aligned " << (void *)ptr << " -> " << (void *)result
250  << std::endl;
251 
252  return result;
253  }
254 
256  inline constexpr void *align_4kb(auto *ptr) {
257  uint8_t *bptr = (uint8_t *)ptr;
258  auto result = (void *)(((uint64_t)(bptr + (4 * KiB - 1)) >> 12) << 12);
259 
260  DBGH(4) << "Aligned " << (void *)ptr << " -> " << (void *)result
261  << std::endl;
262 
263  return result;
264  }
265 
267  inline constexpr void *align_2mb(auto *ptr) {
268  uint8_t *bptr = (uint8_t *)ptr;
269  auto result = (void *)(((uint64_t)(bptr + (2 * MiB - 1)) >> 21) << 21);
270 
271  DBGH(4) << "Aligned " << (void *)ptr << " -> " << (void *)result
272  << std::endl;
273 
274  return result;
275  }
276 
278  inline constexpr auto round_bytes(auto bytes, auto mult) -> decltype(bytes) {
279  if (mult > bytes)
280  throw std::runtime_error("Mult " + std::to_string(mult) +
281  " is greater than bytes " +
282  std::to_string(bytes));
283 
284  size_t alloc_bytes = bytes;
285  if (bytes % mult != 0) {
286  alloc_bytes = ((bytes / mult) + 1) * mult;
287  }
288  return alloc_bytes;
289  }
290 
292  static inline std::string ptr_to_hexstr(void *ptr) {
293  std::stringstream ss;
294  ss << (void *)ptr;
295  std::string result = ss.str();
296  return ss.str();
297  }
298 
299  /* NOTE: First template is filled automatically */
300 
302  template <typename O, typename I>
303  static inline O LP_RCast(I arg) {
304  return reinterpret_cast<O>(arg);
305  }
306 
308  template <typename O, typename I>
309  static inline O LP_SCast(I arg) {
310  return static_cast<O>(arg);
311  }
312 
314  template <typename O, typename I>
315  static inline O LP_DCast(I arg) {
316  return dynamic_cast<O>(arg);
317  }
318 
320  auto page_num(auto ptr) -> decltype(ptr) {
321  using retType = decltype(ptr);
322 
323  auto ptr_ul = LP_RCast<uint64_t>(ptr);
324  return LP_RCast<retType>(ptr_ul >> 12);
325  }
326 
333  static inline std::string __attribute__((__const__))
334  buf_to_hexstr(const char *buf, size_t bytes) {
335  std::stringstream result;
336 
337  result << std::hex;
338 
339  size_t cur_byte = 0;
340  std::stringstream val;
341  while (cur_byte < bytes) {
342  result << "0x" << std::hex << std::setfill('0') << std::setw(8)
343  << ((uint64_t)(buf + cur_byte) >> 4) << 4 << " ";
344  for (size_t i = 0; i < 16; i++) {
345  const size_t idx = cur_byte + i;
346 
347  if (i % 4 == 0 && i != 0) {
348  result << " ";
349  }
350 
351  uint32_t elem = static_cast<uint8_t>(buf[idx]);
352  result << std::hex << std::setfill('0') << std::setw(2)
353  << std::uppercase << elem;
354 
355  if (buf[idx] >= ' ' and buf[idx] <= '~') {
356  val << buf[idx];
357  } else {
358  val << ".";
359  }
360  }
361  cur_byte += 16;
362 
363  if (cur_byte < bytes) {
364  result << " " << val.str() << std::endl;
365  std::stringstream().swap(val);
366  }
367  }
368 
369  return result.str();
370  }
371 
372  inline bool is_pid_running(pid_t pid) {
373  while (waitpid(-1, 0, WNOHANG) > 0) {
374  // Wait for defunct....
375  }
376 
377  if (0 == kill(pid, 0)) return 1; // Process exists
378 
379  return 0;
380  }
381 
382  template <typename A, typename B, typename C>
383  inline uint64_t rebase_ptr(A old_base, B new_base, C ptr) {
384  uint64_t result =
385  (uint64_t)(new_base) + ((uint64_t)(ptr) - (uint64_t)(old_base));
386  DBGH(4) << "old_base: " << (void *)(old_base)
387  << " new_base: " << (void *)new_base
388  << " off: " << ((uint64_t)(ptr) - (uint64_t)(old_base))
389  << " result: " << (void *)(result) << std::endl;
390 
391  return result;
392  }
393 
394  inline std::string ns_to_hr(size_t ns_total) {
395  std::stringstream ss;
396 
397  const size_t s = ns_total / (1000000000);
398  const size_t ms = (ns_total - s * 1000000000) / (1000000);
399  const size_t us = (ns_total - s * 1000000000 - ms * 1000000) / (1000);
400  const size_t ns =
401  (ns_total - s * 1000000000 - ms * 1000000 - us * 1000) / (1000);
402 
403  ss << s << "s " << ms << "ms " << us << "us " << ns << "ns";
404 
405  return ss.str();
406  }
407 
408  template <typename T>
409  std::string to_latex(const std::string &name, T val,
410  const std::string &suffix, size_t div_factor = 1) {
411  double scale = 0.1;
412  double div_val = (int)((val / (double)div_factor) / scale) * scale;
413  std::stringstream ss;
414 
415  ss << "\\newcommand{\\" << name << "}{" << std::fixed
416  << std::setprecision(1) << div_val << suffix << "}";
417 
418  return ss.str();
419  }
420 
421  std::string ns_to_latex(size_t ns, const std::string &name,
422  time_unit unit = time_unit::any_unit);
423 
424  inline std::string uint64_to_base64(uint64_t val) {
425  std::string result = "12345678901";
426 
427  for (size_t i = 0; i < 11; i++) {
428  uint8_t chunk = (val >> (i * 6)) & ((1 << 6) - 1);
429  result[i] = (char)(chunk + ' ');
430  }
431  return result;
432  }
433 
434  inline uint64_t base64_to_uint64(std::string val) {
435  uint64_t result = 0;
436 
437  for (size_t i = 0; i < 11; i++) {
438  result += (val[i] - ' ') << (i * 6);
439  }
440 
441  return result;
442  }
443 } // namespace nvsl
nvsl::round_bytes
constexpr auto round_bytes(auto bytes, auto mult) -> decltype(bytes)
Rounds bytes to the multiple of mult greater than it.
Definition: common.hh:278
nvsl::align_4kb
constexpr void * align_4kb(auto *ptr)
Align a ptr to 4KiB page boundary.
Definition: common.hh:256
constants.hh
Brief description here.
nvsl::align_2mb
constexpr void * align_2mb(auto *ptr)
Align a ptr to 2MiB page boundary.
Definition: common.hh:267
envvars.hh
Declare environment variables and access their values.
nvsl::wildcard
bool wildcard(const std::string &pat, const std::string &str)
Match a pattern using wildcar.
Definition: common.hh:69
DBGH
Definition: common.hh:132
nvsl::align_cl
constexpr void * align_cl(auto *ptr)
Align ptr to the next cache line (64 bytes)
Definition: common.hh:245
nvsl::page_num
auto page_num(auto ptr) -> decltype(ptr)
Get page number from virtual memory address.
Definition: common.hh:320