Echo Writes Code

terminate_handler.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include "crucible/boot/terminate_handler.hpp"

#include "crucible/core/arrays.hpp"
#include "crucible/core/backtrace.hpp"
#include "crucible/core/format.hpp"

namespace crucible::boot::terminate_handler
{
  [[noreturn]] auto abort_with_detailed_backtrace(core::arrays::DynamicArray<core::backtrace::DetailedStackFrame> const &frames) -> void
  {
    auto print_detailed_stack_frame {
      [i = 0](core::backtrace::DetailedStackFrame const &frame) mutable -> void {
        std::cerr << "• #" << i << ": " << frame.describe() << "\n";
        ++i;
      }
    };

    std::cerr
      << "Full backtrace of the calling thread:\n"
      << "─────────────────────────────────────\n";

    frames
      .iterate()
      .for_each_do(print_detailed_stack_frame);

    std::cerr << "\n";
    std::abort();
  }

  auto print_failed_detailed_backtrace(core::backtrace::Error const &error) -> void
  {
    std::cerr
      << "Failed to get full backtrace of the calling thread:\n"
      << "───────────────────────────────────────────────────\n"
      << "• " << error.describe() << "\n"
      << "\n";
  }

  [[noreturn]] auto abort_with_simple_backtrace(core::arrays::DynamicArray<core::backtrace::SimpleStackFrame> const &frames) -> void
  {
    auto print_simple_stack_frame {
      [i = 0](core::backtrace::SimpleStackFrame const &frame) mutable -> void {
        std::cerr << "• #" << i << ": " << frame.describe() << "\n";
        ++i;
      }
    };

    std::cerr
      << "Simple backtrace of the calling thread:\n"
      << "───────────────────────────────────────\n";

    frames
      .iterate()
      .for_each_do(print_simple_stack_frame);

    std::cerr << "\n";
    std::abort();
  }

  auto print_failed_simple_backtrace(core::backtrace::Error const &error) -> void
  {
    std::cerr
      << "Failed to get simple backtrace of the calling thread:\n"
      << "─────────────────────────────────────────────────────\n"
      << "• " << error.describe() << "\n"
      << "\n";
  }

  auto handle_terminate() -> void
  {
    std::cerr
      <<"\n"
      << "╭────────────────────────────────────────────────────────────╮\n"
      << "│         std::terminate() called (this is a crash!)         │\n"
      << "│                                                            │\n"
      << "│ Here's all the information I could find about the problem: │\n"
      << "╰────────────────────────────────────────────────────────────╯\n"
      << "\n";

    auto const exception { std::current_exception() };

    try {
      if (exception) {
        std::rethrow_exception(exception);
      }

      std::cerr
        << "No std::exception objects are currently being thrown in the calling thread\n"
        << "──────────────────────────────────────────────────────────────────────────\n"
        << "\n";
    } catch (std::exception const &e) {
      std::cerr
        << "Uncaught std::exception object in the calling thread:\n"
        << "─────────────────────────────────────────────────────\n"
        << "• typeid(): " << core::format::format_std_type_info(typeid(e)) << "\n"
        << "• what(): " << e.what() << "\n"
        << "\n";
    }

    // `abort_with_detailed_backtrace()` will exit if we end up calling it
    auto const detailed_backtrace { core::backtrace::take_detailed_snapshot() };
    detailed_backtrace.resolve(print_failed_detailed_backtrace, abort_with_detailed_backtrace);

    // `abort_with_simple_backtrace()` will exit if we end up calling it
    auto const simple_backtrace { core::backtrace::take_simple_snapshot() };
    simple_backtrace.resolve(print_failed_simple_backtrace, abort_with_simple_backtrace);

    // If we failed both backtraces, just give up
    std::abort();
  }
}