77#include < array>
88#include < cstddef>
99#include < format>
10+ #include < optional>
1011#include < source_location>
12+ #include < span>
1113#include < type_traits>
1214#include < utility>
1315
1416#include " runtime-light/k2-platform/k2-api.h"
15-
16- enum class LogLevel : size_t {
17- Error = 1 ,
18- Warn = 2 ,
19- Info = 3 ,
20- Debug = 4 ,
21- Trace = 5 ,
22- };
17+ #include " runtime-light/stdlib/diagnostics/backtrace.h"
2318
2419namespace kphp ::log {
2520
@@ -46,51 +41,78 @@ using wrapped_arg_t = std::invoke_result_t<decltype(impl::wrap_log_argument<T>),
4641enum class level : size_t { error = 1 , warn, info, debug, trace };
4742
4843template <typename ... Args>
49- void log (level level, std::format_string<impl::wrapped_arg_t <Args>...> fmt, Args&&... args) noexcept {
44+ void log (level level, std::optional<std::span<void * const >> trace, std::format_string<impl::wrapped_arg_t <Args>...> fmt, Args&&... args) noexcept {
45+ static constexpr size_t LOG_BUFFER_SIZE = 1024UZ * 4UZ;
5046 if (std::to_underlying (level) > k2::log_level_enabled ()) {
5147 return ;
5248 }
5349
54- static constexpr size_t LOG_BUFFER_SIZE = 512 ;
55- std::array<char , LOG_BUFFER_SIZE> log_buffer{};
56- const auto [out, size]{std::format_to_n<decltype (log_buffer.data ()), impl::wrapped_arg_t <Args>...>(log_buffer.data (), log_buffer.size () - 1 , fmt,
57- impl::wrap_log_argument (std::forward<Args>(args))...)};
50+ std::array<char , LOG_BUFFER_SIZE> log_buffer;
51+ auto [out, size]{std::format_to_n<decltype (log_buffer.data ()), impl::wrapped_arg_t <Args>...>(log_buffer.data (), log_buffer.size () - 1 , fmt,
52+ impl::wrap_log_argument (std::forward<Args>(args))...)};
53+ if (trace.has_value ()) {
54+ if (auto backtrace_symbols{kphp::diagnostic::backtrace_symbols (*trace)}; !backtrace_symbols.empty ()) {
55+ const auto [trace_out, trace_size]{std::format_to_n (out, std::distance (out, log_buffer.end ()) - 1 , " \n Backtrace\n {}" , backtrace_symbols)};
56+ out = trace_out;
57+ size += trace_size;
58+ } else if (auto backtrace_addresses{kphp::diagnostic::backtrace_addresses (*trace)}; !backtrace_addresses.empty ()) {
59+ const auto [trace_out, trace_size]{std::format_to_n (out, std::distance (out, log_buffer.end ()) - 1 , " \n Backtrace\n {}" , backtrace_addresses)};
60+ out = trace_out;
61+ size += trace_size;
62+ }
63+ }
64+
5865 *out = ' \0 ' ;
5966 k2::log (std::to_underlying (level), size, log_buffer.data ());
6067}
6168
62- } // namespace impl
63-
6469template <typename ... Args>
65- [[noreturn]] void error (std::format_string<impl::wrapped_arg_t <Args>...> fmt, Args&&... args) noexcept {
66- impl::log (impl::level::error, fmt, std::forward<Args>(args)...);
67- k2::exit (1 );
70+ void log_with_backtrace (level level, std::format_string<impl::wrapped_arg_t <Args>...> fmt, Args&&... args) noexcept {
71+ static constexpr size_t MAX_BACKTRACE_SIZE = 64 ;
72+ if (std::to_underlying (level) > k2::log_level_enabled ()) {
73+ return ;
74+ }
75+
76+ std::array<void *, MAX_BACKTRACE_SIZE> backtrace{};
77+ const size_t num_frames{kphp::diagnostic::backtrace (backtrace)};
78+ const std::span<void * const > backtrace_view{backtrace.data (), num_frames};
79+ impl::log (level, backtrace_view, fmt, std::forward<Args>(args)...);
6880}
81+ } // namespace impl
6982
83+ // The backtrace algorithm relies on the fact that assertion does not call backtrace.
84+ // If assertion is modified, the backtrace algorithm should be updated accordingly
7085inline void assertion (bool condition, const std::source_location& location = std::source_location::current()) noexcept {
7186 if (!condition) [[unlikely]] {
72- kphp::log::error (" assertion failed at {}:{}" , location.file_name (), location.line ());
87+ impl::log (impl::level::error, std::nullopt , " assertion failed at {}:{}" , location.file_name (), location.line ());
88+ k2::exit (1 );
7389 }
7490}
7591
92+ template <typename ... Args>
93+ [[noreturn]] void error (std::format_string<impl::wrapped_arg_t <Args>...> fmt, Args&&... args) noexcept {
94+ impl::log_with_backtrace (impl::level::error, fmt, std::forward<Args>(args)...);
95+ k2::exit (1 );
96+ }
97+
7698template <typename ... Args>
7799void warning (std::format_string<impl::wrapped_arg_t <Args>...> fmt, Args&&... args) noexcept {
78- impl::log (impl::level::warn, fmt, std::forward<Args>(args)...);
100+ impl::log_with_backtrace (impl::level::warn, fmt, std::forward<Args>(args)...);
79101}
80102
81103template <typename ... Args>
82104void info (std::format_string<impl::wrapped_arg_t <Args>...> fmt, Args&&... args) noexcept {
83- impl::log (impl::level::info, fmt, std::forward<Args>(args)...);
105+ impl::log (impl::level::info, std:: nullopt , fmt, std::forward<Args>(args)...);
84106}
85107
86108template <typename ... Args>
87109void debug (std::format_string<impl::wrapped_arg_t <Args>...> fmt, Args&&... args) noexcept {
88- impl::log (impl::level::debug, fmt, std::forward<Args>(args)...);
110+ impl::log (impl::level::debug, std:: nullopt , fmt, std::forward<Args>(args)...);
89111}
90112
91113template <typename ... Args>
92114void trace (std::format_string<impl::wrapped_arg_t <Args>...> fmt, Args&&... args) noexcept {
93- impl::log (impl::level::trace, fmt, std::forward<Args>(args)...);
115+ impl::log (impl::level::trace, std:: nullopt , fmt, std::forward<Args>(args)...);
94116}
95117
96118} // namespace kphp::log
0 commit comments