GCC Code Coverage Report


Directory: ./
File: src/logging/logging.cpp
Date: 2023-04-27 00:55:30
Exec Total Coverage
Lines: 68 86 79.1%
Functions: 12 18 66.7%
Branches: 52 91 57.1%

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