| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #if WITH_LOG | ||
| 2 | # include <spdlog/sinks/ostream_sink.h> | ||
| 3 | # include <spdlog/sinks/stdout_color_sinks.h> | ||
| 4 | # include <spdlog/spdlog.h> | ||
| 5 | #elif !(defined SPDLOG_COMPILED_LIB) | ||
| 6 | # define SPDLOG_COMPILED_LIB | ||
| 7 | # include <spdlog/fmt/bundled/format-inl.h> | ||
| 8 | # include <spdlog/fmt/fmt.h> | ||
| 9 | # include <spdlog/src/fmt.cpp> | ||
| 10 | #endif | ||
| 11 | |||
| 12 | #include "logging.h" | ||
| 13 | |||
| 14 | #include <cstdarg> | ||
| 15 | #include <cstdio> | ||
| 16 | #include <cstring> | ||
| 17 | #include <memory> | ||
| 18 | #include <unordered_map> | ||
| 19 | |||
| 20 | // Linux signal handling & stack trace printing | ||
| 21 | #ifdef __linux__ | ||
| 22 | # include <cxxabi.h> | ||
| 23 | # include <execinfo.h> | ||
| 24 | # include <regex> | ||
| 25 | # include <signal.h> | ||
| 26 | #endif | ||
| 27 | |||
| 28 | namespace lython { | ||
| 29 | #ifdef __linux__ | ||
| 30 | |||
| 31 | // _ZN6lython11builtin_maxERSt6vectorINS_5ValueENS_9AllocatorIS1_EEE+0x368 | ||
| 32 | static std::regex mangled_name("\\([A-Za-z0-9_]*"); | ||
| 33 | static std::regex remove_namespaces("(lython::|std::)"); | ||
| 34 | |||
| 35 | std::string demangle(std::string const& original_str) { | ||
| 36 | 78 | std::string matched_str; | |
| 37 | 78 | std::string result_str; | |
| 38 | |||
| 39 |
1/1✓ Branch 3 taken 78 times.
|
78 | auto begin = std::sregex_iterator(original_str.begin(), original_str.end(), mangled_name); |
| 40 |
1/1✓ Branch 1 taken 78 times.
|
78 | auto end = std::sregex_iterator(); |
| 41 | |||
| 42 | 78 | char* buffer = nullptr; | |
| 43 | |||
| 44 | 78 | int status = 0; | |
| 45 |
2/3✓ Branch 1 taken 78 times.
✓ Branch 4 taken 78 times.
✗ Branch 5 not taken.
|
78 | for (auto i = begin; i != end; ++i) { |
| 46 |
1/1✓ Branch 2 taken 78 times.
|
78 | matched_str = (*i).str(); |
| 47 | // ignore first ( | ||
| 48 |
1/1✓ Branch 2 taken 78 times.
|
78 | buffer = abi::__cxa_demangle(matched_str.c_str() + 1, nullptr, nullptr, &status); |
| 49 | 78 | break; | |
| 50 | 78 | } | |
| 51 | |||
| 52 |
5/6✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 51 times.
✓ Branch 4 taken 27 times.
✓ Branch 5 taken 51 times.
✓ Branch 6 taken 27 times.
|
78 | if (!matched_str.empty() && status == 0) { |
| 53 |
1/1✓ Branch 2 taken 51 times.
|
51 | result_str = std::string(buffer); |
| 54 | } else { | ||
| 55 |
1/1✓ Branch 1 taken 27 times.
|
27 | result_str = original_str; |
| 56 | } | ||
| 57 | |||
| 58 | 78 | free(buffer); | |
| 59 | 156 | return result_str; | |
| 60 | 78 | } | |
| 61 | |||
| 62 | std::vector<std::string> get_backtrace(size_t size = 32) { | ||
| 63 | // avoid allocating memory dynamically | ||
| 64 |
1/1✓ Branch 2 taken 3 times.
|
3 | std::vector<void*> ptrs(size); |
| 65 | // static std::vector<std::string> ignore = { | ||
| 66 | // "libstdc++.so", | ||
| 67 | // "lython::get_backtrace[abi:cxx11](unsigned long)", | ||
| 68 | // "lython::show_backkwtrace()" | ||
| 69 | // }; | ||
| 70 | |||
| 71 |
1/1✓ Branch 2 taken 3 times.
|
3 | int real_size = backtrace(ptrs.data(), int(size)); |
| 72 | 3 | char** symbols = backtrace_symbols(ptrs.data(), int(real_size)); | |
| 73 | |||
| 74 | 3 | std::vector<std::string> names; | |
| 75 |
1/1✓ Branch 1 taken 3 times.
|
3 | names.reserve(size_t(real_size)); |
| 76 | |||
| 77 |
2/2✓ Branch 0 taken 78 times.
✓ Branch 1 taken 3 times.
|
81 | for (int i = 0; i < real_size; ++i) { |
| 78 |
2/2✓ Branch 2 taken 78 times.
✓ Branch 5 taken 78 times.
|
78 | std::string original_str = demangle(symbols[i]); |
| 79 | |||
| 80 | 78 | bool skip = false; | |
| 81 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 78 times.
|
78 | if (original_str.find("libstdc++.so") != std::string::npos) { |
| 82 | ✗ | skip = true; | |
| 83 | } | ||
| 84 | // for (auto& str: ignore){ | ||
| 85 | // if (original_str.find(str) != std::string::npos){ | ||
| 86 | // skip = true; | ||
| 87 | // } | ||
| 88 | // } | ||
| 89 | // skip libstdc++ calls | ||
| 90 |
1/2✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
|
78 | if (!skip) { |
| 91 |
1/1✓ Branch 1 taken 78 times.
|
78 | auto simplified = std::regex_replace(original_str, remove_namespaces, ""); |
| 92 |
1/1✓ Branch 1 taken 78 times.
|
78 | names.push_back(simplified); |
| 93 | 78 | } | |
| 94 | 78 | } | |
| 95 | |||
| 96 | 3 | free(symbols); | |
| 97 | 6 | return names; | |
| 98 | 3 | } | |
| 99 | |||
| 100 | void show_backtrace() { | ||
| 101 |
1/1✓ Branch 1 taken 3 times.
|
3 | std::vector<std::string> symbols = get_backtrace(32); |
| 102 | 3 | int i = 0; | |
| 103 |
2/2✓ Branch 4 taken 78 times.
✓ Branch 5 taken 3 times.
|
81 | for (auto& sym: symbols) { |
| 104 | 78 | i += 1; | |
| 105 |
2/2✓ Branch 1 taken 78 times.
✓ Branch 4 taken 78 times.
|
156 | spdlog_log(LogLevel::Error, fmt::format(" TB {:2} -> {}", i, sym)); |
| 106 | } | ||
| 107 | 3 | } | |
| 108 | |||
| 109 | [[noreturn]] void signal_handler(int sig) { | ||
| 110 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 4 taken 3 times.
|
6 | spdlog_log(LogLevel::Fatal, fmt::format("Received signal {} >>>", sig)); |
| 111 | 3 | show_backtrace(); | |
| 112 |
2/2✓ Branch 2 taken 3 times.
✓ Branch 5 taken 3 times.
|
3 | spdlog_log(LogLevel::Fatal, "<<< Exiting"); |
| 113 | 3 | exit(1); | |
| 114 | } | ||
| 115 | |||
| 116 | int register_signal_handler() { | ||
| 117 | 9 | signal(SIGSEGV, signal_handler); // 11, install our handler | |
| 118 | 9 | signal(SIGINT, signal_handler); | |
| 119 | 9 | signal(SIGQUIT, signal_handler); | |
| 120 | // Sent on exceptions which already print the stack trace | ||
| 121 | 9 | signal(SIGABRT, signal_handler); | |
| 122 | 9 | signal(SIGKILL, signal_handler); | |
| 123 | 9 | signal(SIGTERM, signal_handler); | |
| 124 | 9 | return 0; | |
| 125 | } | ||
| 126 | #else | ||
| 127 | int register_signal_handler() { return 0; } | ||
| 128 | |||
| 129 | void show_backtrace() {} | ||
| 130 | |||
| 131 | std::vector<std::string> get_backtrace(size_t size) { return std::vector<std::string>(); } | ||
| 132 | #endif | ||
| 133 | |||
| 134 | #if WITH_LOG | ||
| 135 | using Logger = std::shared_ptr<spdlog::logger>; | ||
| 136 | |||
| 137 | Logger new_logger(char const* name) { | ||
| 138 | // Static so only executed once | ||
| 139 |
2/4✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
|
9 | static int _ = register_signal_handler(); |
| 140 | |||
| 141 |
1/1✓ Branch 1 taken 9 times.
|
9 | spdlog::enable_backtrace(32); |
| 142 | |||
| 143 |
1/1✓ Branch 1 taken 9 times.
|
9 | auto stdout_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>(); |
| 144 | |||
| 145 |
1/1✓ Branch 1 taken 9 times.
|
9 | auto console = std::make_shared<spdlog::logger>(name, stdout_sink); |
| 146 | |||
| 147 |
1/1✓ Branch 2 taken 9 times.
|
9 | console->set_level(spdlog::level::level_enum::trace); |
| 148 |
1/1✓ Branch 2 taken 9 times.
|
9 | console->flush_on(spdlog::level::level_enum::trace); |
| 149 | |||
| 150 |
1/1✓ Branch 2 taken 9 times.
|
9 | spdlog::register_logger(console); |
| 151 | // %Y-%m-%d %H:%M:%S.%e | ||
| 152 |
2/2✓ Branch 2 taken 9 times.
✓ Branch 5 taken 9 times.
|
9 | spdlog::set_pattern("[%L] [%t] %v"); |
| 153 | |||
| 154 | 18 | return console; | |
| 155 | 9 | } | |
| 156 | |||
| 157 | Logger new_ostream_logger(char const* name, std::ostream& out) { | ||
| 158 | ✗ | auto ossink = std::make_shared<spdlog::sinks::ostream_sink_st>(out); | |
| 159 | ✗ | auto console = std::make_shared<spdlog::logger>(name, ossink); | |
| 160 | |||
| 161 | ✗ | console->set_level(spdlog::level::level_enum::trace); | |
| 162 | ✗ | console->flush_on(spdlog::level::level_enum::trace); | |
| 163 | |||
| 164 | ✗ | spdlog::register_logger(console); | |
| 165 | // %Y-%m-%d %H:%M:%S.%e | ||
| 166 | ✗ | spdlog::set_pattern("[%L] [%t] %v"); | |
| 167 | ✗ | return console; | |
| 168 | ✗ | } | |
| 169 | |||
| 170 | std::unordered_map<const char*, Logger>& logger_handles() { | ||
| 171 | ✗ | static std::unordered_map<const char*, Logger> handles; | |
| 172 | ✗ | return handles; | |
| 173 | } | ||
| 174 | |||
| 175 | LoggerHandle new_log(const char* name, std::ostream& out) { | ||
| 176 | ✗ | Logger log = new_ostream_logger(name, out); | |
| 177 | ✗ | logger_handles()[name] = log; | |
| 178 | ✗ | return (LoggerHandle)(log.get()); | |
| 179 | ✗ | } | |
| 180 | |||
| 181 | Logger root() { | ||
| 182 |
4/7✓ Branch 0 taken 9 times.
✓ Branch 1 taken 34967 times.
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 9 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
|
34976 | static Logger log = new_logger("root"); |
| 183 | 34976 | return log; | |
| 184 | } | ||
| 185 | |||
| 186 | static constexpr spdlog::level::level_enum log_level_spd[] = {spdlog::level::level_enum::trace, | ||
| 187 | spdlog::level::level_enum::debug, | ||
| 188 | spdlog::level::level_enum::info, | ||
| 189 | spdlog::level::level_enum::warn, | ||
| 190 | spdlog::level::level_enum::err, | ||
| 191 | spdlog::level::level_enum::critical, | ||
| 192 | spdlog::level::level_enum::off}; | ||
| 193 | |||
| 194 | void show_log_backkwtrace() { spdlog::dump_backtrace(); } | ||
| 195 | |||
| 196 | void spdlog_log(LogLevel level, std::string const& msg) { root()->log(log_level_spd[level], msg); } | ||
| 197 | |||
| 198 | #else | ||
| 199 | void show_log_backtrace() {} | ||
| 200 | void spdlog_log(LogLevel level, std::string const& msg) {} | ||
| 201 | #endif | ||
| 202 | |||
| 203 | const char* log_level_str[] = { | ||
| 204 | "[T] TRACE", "[D] DEBUG", "[I] INFO", "/!\\ WARN", "[E] ERROR", "[!] FATAL", ""}; | ||
| 205 | |||
| 206 | std::string format_code_loc(const char* file, const char* function, int line) { | ||
| 207 | ✗ | return fmt::format("{} {}:{}", file, function, line); | |
| 208 | } | ||
| 209 | |||
| 210 | std::string format_code_loc_kwtrace(const char*, const char* function, int line) { | ||
| 211 | ✗ | return fmt::format("{:>25}:{:4}", function, line); | |
| 212 | } | ||
| 213 | |||
| 214 | std::string format_function(std::string const& fun) { | ||
| 215 | 34892 | auto start = fun.find_last_of(':'); | |
| 216 |
1/2✓ Branch 0 taken 34892 times.
✗ Branch 1 not taken.
|
34892 | if (start == std::string::npos) { |
| 217 | 34892 | return fun; | |
| 218 | } | ||
| 219 | ✗ | return fun.substr(start + 1); | |
| 220 | } | ||
| 221 | |||
| 222 | // instead of setting a single log level for the entire program allow to cherry pick | ||
| 223 | // which level is enabled | ||
| 224 | std::unordered_map<LogLevel, bool>& log_levels() { | ||
| 225 | static std::unordered_map<LogLevel, bool> levels{ | ||
| 226 | {Info, true}, | ||
| 227 | {Warn, true}, | ||
| 228 | {Debug, true}, | ||
| 229 | {Error, true}, | ||
| 230 | {Fatal, true}, | ||
| 231 | {Trace, true}, | ||
| 232 |
4/7✓ Branch 0 taken 12 times.
✓ Branch 1 taken 34946 times.
✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 12 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
|
34958 | }; |
| 233 | 34958 | return levels; | |
| 234 | } | ||
| 235 | |||
| 236 | void set_log_level(LogLevel level, bool enabled) { log_levels()[level] = enabled; } | ||
| 237 | |||
| 238 | bool is_log_enabled(LogLevel level) { return log_levels()[level]; } | ||
| 239 | |||
| 240 | } // namespace lython | ||
| 241 |