TRLC Platform Library  1.0.0
Header-only C++ library for compile-time platform detection and abstraction
debug.hpp
Go to the documentation of this file.
1 #pragma once
2 
27 #include <atomic>
28 #include <cstdlib>
29 #include <iostream>
30 #include <string>
31 
32 namespace trlc {
33 namespace platform {
34 
35 //==============================================================================
36 // Forward Declarations and Type Definitions
37 //==============================================================================
38 
54 using AssertionHandler = void (*)(const char* expression,
55  const char* file,
56  int line,
57  const char* function);
58 
59 // Forward declaration of the default assertion handler
60 void defaultAssertionHandler(const char* expression,
61  const char* file,
62  int line,
63  const char* function) noexcept;
64 
65 //==============================================================================
66 // Debug Mode Detection Functions
67 //==============================================================================
68 
87 constexpr bool isDebugBuild() noexcept {
88 #ifdef NDEBUG
89  return false;
90 #else
91  return true;
92 #endif
93 }
94 
105 constexpr bool isReleaseBuild() noexcept {
106  return !isDebugBuild();
107 }
108 
121 constexpr bool hasDebugInfo() noexcept {
122  // In debug builds, debug info is usually available
123  if (isDebugBuild()) {
124  return true;
125  }
126 
127  // Some compilers provide debug info even in release builds
128 #if defined(__GNUC__) || defined(__clang__)
129  // GCC/Clang: Check for common debug flags
130  // Note: This is compile-time approximation, actual availability
131  // depends on command-line flags like -g
132  return isDebugBuild();
133 #elif defined(_MSC_VER)
134  // MSVC: Debug info can be enabled independently of release/debug
135  // We conservatively assume it's available in debug builds only
136  return isDebugBuild();
137 #else
138  // Unknown compiler: Conservative assumption
139  return isDebugBuild();
140 #endif
141 }
142 
143 //==============================================================================
144 // Debug Utilities Class
145 //==============================================================================
146 
157 class DebugUtils {
158 public:
159  //--------------------------------------------------------------------------
160  // Assertion Management
161  //--------------------------------------------------------------------------
162 
184  static void setAssertionHandler(AssertionHandler handler) noexcept {
185  if (handler == nullptr) {
186  handler = defaultAssertionHandler;
187  }
188  _assertion_handler.store(handler, std::memory_order_release);
189  }
190 
201  return _assertion_handler.load(std::memory_order_acquire);
202  }
203 
204  //--------------------------------------------------------------------------
205  // Debug Break Utilities
206  //--------------------------------------------------------------------------
207 
225  static void debugBreak() noexcept {
226 #if defined(_WIN32) || defined(_WIN64)
227  // Windows: Use __debugbreak() intrinsic
228  #if defined(_MSC_VER)
229  __debugbreak();
230  #elif defined(__GNUC__) || defined(__clang__)
231  // MinGW or Clang on Windows
232  __builtin_trap();
233  #else
234  // Unknown compiler on Windows
235  std::abort();
236  #endif
237 #elif defined(__i386__) || defined(__x86_64__)
238  // x86/x64: Use int3 instruction
239  #if defined(__GNUC__) || defined(__clang__)
240  __asm__ volatile("int3");
241  #else
242  // Fallback for unknown compiler
243  std::abort();
244  #endif
245 #elif defined(__aarch64__) || defined(__arm__)
246  // ARM: Use builtin trap or BKPT instruction
247  #if defined(__GNUC__) || defined(__clang__)
248  __builtin_trap();
249  #else
250  std::abort();
251  #endif
252 #else
253  // Unknown architecture: Use generic trap or abort
254  #if defined(__GNUC__) || defined(__clang__)
255  __builtin_trap();
256  #else
257  std::abort();
258  #endif
259 #endif
260  }
261 
281  [[noreturn]] static void unreachable() noexcept {
282  if constexpr (isDebugBuild()) {
283  // In debug builds, make unreachable code obvious
284  defaultAssertionHandler("unreachable code reached", __FILE__, __LINE__, __func__);
285  }
286 
287  // Hint to compiler that this is unreachable
288 #if defined(__GNUC__) || defined(__clang__)
289  __builtin_unreachable();
290 #elif defined(_MSC_VER)
291  __assume(false);
292 #else
293  // Fallback: abort the program
294  std::abort();
295 #endif
296  }
297 
315  [[noreturn]] static void abort(const char* message = nullptr) noexcept {
316  if (message != nullptr) {
317  std::cerr << "Program terminated: " << message << std::endl;
318  } else {
319  std::cerr << "Program terminated by DebugUtils::abort()" << std::endl;
320  }
321 
322  // In debug builds, trigger a breakpoint first
323  if constexpr (isDebugBuild()) {
324  debugBreak();
325  }
326 
327  std::abort();
328  }
329 
330  //--------------------------------------------------------------------------
331  // Stack Trace Support
332  //--------------------------------------------------------------------------
333 
344  static bool canCaptureStackTrace() noexcept {
345  // Platform-specific stack trace support detection
346 #if defined(_WIN32) && defined(_MSC_VER)
347  // Windows with MSVC: StackWalk64 API available
348  return true;
349 #elif defined(__GNUC__) || defined(__clang__)
350  // GCC/Clang: Check for backtrace support
351  #if defined(__GLIBC__) || defined(__APPLE__) || defined(__FreeBSD__)
352  return true; // backtrace() available
353  #else
354  return false; // No backtrace support
355  #endif
356 #else
357  return false; // Unknown platform/compiler
358 #endif
359  }
360 
379  static void printStackTrace() noexcept {
380  if (!canCaptureStackTrace()) {
381  std::cerr << "Stack trace not available on this platform" << std::endl;
382  return;
383  }
384 
385  // Platform-specific stack trace implementation
386 #if defined(_WIN32) && defined(_MSC_VER)
387  printStackTraceWindows();
388 #elif defined(__GNUC__) || defined(__clang__)
389  #if defined(__GLIBC__) || defined(__APPLE__) || defined(__FreeBSD__)
390  printStackTraceUnix();
391  #else
392  std::cerr << "Stack trace not implemented for this Unix variant" << std::endl;
393  #endif
394 #else
395  std::cerr << "Stack trace not implemented for this platform" << std::endl;
396 #endif
397  }
398 
399 private:
400  //--------------------------------------------------------------------------
401  // Private Members and Helper Functions
402  //--------------------------------------------------------------------------
403 
405  static std::atomic<AssertionHandler> _assertion_handler;
406 
413 #if defined(_WIN32) && defined(_MSC_VER)
414  static void printStackTraceWindows() noexcept {
415  // Implementation would use StackWalk64 API
416  // For brevity, we just print a placeholder message
417  std::cerr << "Windows stack trace would be printed here" << std::endl;
418  std::cerr << "(StackWalk64 implementation not included in this example)" << std::endl;
419  }
420 #endif
421 
428 #if defined(__GNUC__) || defined(__clang__)
429  #if defined(__GLIBC__) || defined(__APPLE__) || defined(__FreeBSD__)
430  static void printStackTraceUnix() noexcept {
431  // Implementation would use backtrace() and backtrace_symbols()
432  // For brevity, we just print a placeholder message
433  std::cerr << "Unix stack trace would be printed here" << std::endl;
434  std::cerr << "(backtrace() implementation not included in this example)" << std::endl;
435  }
436  #endif
437 #endif
438 };
439 
440 // Initialize the atomic assertion handler with the default (inline to avoid ODR issues)
441 inline std::atomic<AssertionHandler> DebugUtils::_assertion_handler{defaultAssertionHandler};
442 
443 //==============================================================================
444 // Default Assertion Handler
445 //==============================================================================
446 
461 inline void defaultAssertionHandler(const char* expression,
462  const char* file,
463  int line,
464  const char* function) noexcept {
465  std::cerr << "\n" << std::string(60, '=') << "\n";
466  std::cerr << "ASSERTION FAILED\n";
467  std::cerr << std::string(60, '=') << "\n";
468  std::cerr << "Expression: " << expression << "\n";
469  std::cerr << "File: " << file << "\n";
470  std::cerr << "Line: " << line << "\n";
471  std::cerr << "Function: " << function << "\n";
472  std::cerr << std::string(60, '=') << "\n";
473 
474  // Print stack trace if available
476  std::cerr << "\nStack trace:\n";
478  std::cerr << "\n";
479  }
480 
481  std::cerr << "Program will now terminate.\n" << std::endl;
482 
483  // Trigger debugger break in debug builds
484  if constexpr (isDebugBuild()) {
486  }
487 
488  std::abort();
489 }
490 
491 } // namespace platform
492 } // namespace trlc
493 
494 //==============================================================================
495 // Debug Macros
496 //==============================================================================
497 
511 #ifdef NDEBUG
512  #define TRLC_DEBUG_BUILD 0
513 #else
514  #define TRLC_DEBUG_BUILD 1
515 #endif
516 
535 #if TRLC_DEBUG_BUILD
536  #define TRLC_ASSERT(expr) \
537  ((expr) ? void(0) \
538  : trlc::platform::DebugUtils::getAssertionHandler()( \
539  #expr, __FILE__, __LINE__, __func__))
540 #else
541  #define TRLC_ASSERT(expr) ((void)0)
542 #endif
543 
559 #if TRLC_DEBUG_BUILD
560  #define TRLC_DEBUG_ONLY(code) code
561 #else
562  #define TRLC_DEBUG_ONLY(code) ((void)0)
563 #endif
564 
585 #ifdef TRLC_UNREACHABLE
586  #undef TRLC_UNREACHABLE
587 #endif
588 #define TRLC_UNREACHABLE() trlc::platform::DebugUtils::unreachable()
589 
605 #define TRLC_ABORT(msg) trlc::platform::DebugUtils::abort(msg)
606 
623 #if defined(_MSC_VER)
624  // Microsoft Visual C++: Use intrinsic
625  #define TRLC_DEBUG_BREAK() __debugbreak()
626 #elif defined(__GNUC__) || defined(__clang__)
627  // GCC or Clang: Use platform-specific inline assembly or builtin
628  #if defined(__i386__) || defined(__x86_64__)
629  // x86/x64: Use int3 instruction
630  #define TRLC_DEBUG_BREAK() __asm__ volatile("int3")
631  #else
632  // Other architectures: Use compiler builtin
633  #define TRLC_DEBUG_BREAK() __builtin_trap()
634  #endif
635 #else
636  // Unknown compiler: Fall back to DebugUtils implementation
637  #define TRLC_DEBUG_BREAK() trlc::platform::DebugUtils::debugBreak()
638 #endif
639 
640 //==============================================================================
641 // Compile-Time Validation
642 //==============================================================================
643 
644 // Verify that debug detection works correctly
646  "Debug and release build detection must be mutually exclusive");
647 
648 // Verify that the debug build macro is consistent
649 #if TRLC_DEBUG_BUILD
650 static_assert(trlc::platform::isDebugBuild(),
651  "TRLC_DEBUG_BUILD macro should match isDebugBuild() function");
652 #else
653 static_assert(trlc::platform::isReleaseBuild(),
654  "TRLC_DEBUG_BUILD macro should match build detection functions");
655 #endif
656 
657 // Verify that the assertion handler type is correctly defined
658 static_assert(std::is_same_v<trlc::platform::AssertionHandler,
659  void (*)(const char*, const char*, int, const char*)>,
660  "AssertionHandler type signature must match specification");
661 
662 //==============================================================================
663 // Documentation Examples and Usage Notes
664 //==============================================================================
665 
666 /*
667 Example Usage:
668 
669 1. Basic assertion:
670  TRLC_ASSERT(ptr != nullptr);
671  TRLC_ASSERT(index < size);
672 
673 2. Custom assertion handler:
674  trlc::platform::DebugUtils::setAssertionHandler(
675  [](const char* expr, const char* file, int line, const char* func) {
676  // Custom logging or error handling
677  myLogger.error("Assertion failed: {} at {}:{} in {}",
678  expr, file, line, func);
679  throw std::runtime_error("Assertion failed");
680  }
681  );
682 
683 3. Debug-only code:
684  TRLC_DEBUG_ONLY(
685  auto start_time = std::chrono::high_resolution_clock::now();
686  );
687 
688  // ... some operation ...
689 
690  TRLC_DEBUG_ONLY(
691  auto end_time = std::chrono::high_resolution_clock::now();
692  std::cout << "Operation took: "
693  << std::chrono::duration_cast<std::chrono::milliseconds>(
694  end_time - start_time).count() << "ms" << std::endl;
695  );
696 
697 4. Unreachable code marking:
698  enum class State { A, B, C };
699 
700  std::string toString(State s) {
701  switch (s) {
702  case State::A: return "A";
703  case State::B: return "B";
704  case State::C: return "C";
705  }
706  TRLC_UNREACHABLE(); // Should never reach here
707  }
708 
709 5. Controlled termination:
710  if (!initialize_critical_resource()) {
711  TRLC_ABORT("Failed to initialize critical resource");
712  }
713 
714 Thread Safety:
715 - All DebugUtils static methods are thread-safe
716 - The assertion handler can be safely changed from any thread
717 - Multiple threads can safely call assertion-related functions
718 
719 Performance:
720 - Zero overhead in release builds (macros expand to nothing)
721 - Minimal overhead in debug builds
722 - Assertion handler lookup uses atomic operations
723 - Stack trace capture is expensive but only used on failures
724 */
Debug utilities class with static methods.
Definition: debug.hpp:157
static void printStackTrace()
Definition: debug.hpp:379
static AssertionHandler getAssertionHandler()
Get the current assertion handler.
Definition: debug.hpp:200
static bool canCaptureStackTrace()
Check if stack trace capture is supported.
Definition: debug.hpp:344
static void debugBreak()
Definition: debug.hpp:225
static void abort(const char *message=nullptr)
Definition: debug.hpp:315
static void setAssertionHandler(AssertionHandler handler)
Definition: debug.hpp:184
static void unreachable()
Definition: debug.hpp:281
static bool isReleaseBuild()
Check if the current build is a release build.
Definition: debug.hpp:105
static bool hasDebugInfo()
Check if debug information is available.
Definition: debug.hpp:121
void defaultAssertionHandler(const char *expression, const char *file, int line, const char *function)
Default assertion handler implementation.
Definition: debug.hpp:461
static bool isDebugBuild()
Definition: debug.hpp:87
void(*)(const char *expression, const char *file, int line, const char *function) AssertionHandler
Assertion handler function pointer type.
Definition: debug.hpp:57