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 |