Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions runtime-light/stdlib/diagnostics/detail/logs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Compiler for PHP (aka KPHP)
// Copyright (c) 2026 LLC «V Kontakte»
// Distributed under the GPL v3 License, see LICENSE.notice.txt

#include "runtime-light/stdlib/diagnostics/detail/logs.h"

#include <cstddef>
#include <format>
#include <iterator>
#include <span>
#include <string_view>

#include "runtime-light/stdlib/diagnostics/backtrace.h"
Comment thread
PetrShumilov marked this conversation as resolved.

namespace kphp::log::impl {

namespace {

struct truncating_iterator {
Comment thread
Shamzik marked this conversation as resolved.
using iterator_category = std::output_iterator_tag;
using value_type = void;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = void;

char* dst;
size_t max_size;
size_t count = 0;
Comment thread
PetrShumilov marked this conversation as resolved.
Outdated

struct proxy {
truncating_iterator& it;
void operator=(char c) const { // NOLINT
if (it.count < it.max_size) {
*it.dst++ = c;
}
it.count++;
}
};

proxy operator*() {
return proxy{*this};
}
truncating_iterator& operator++() {
return *this;
}
truncating_iterator operator++(int) { // NOLINT
return *this;
}
};

} // namespace

size_t resolve_log_trace(std::span<char> trace_buffer, std::span<void* const> raw_trace) noexcept {
if (trace_buffer.empty()) {
return 0;
}

if (auto backtrace_symbols{kphp::diagnostic::backtrace_symbols(raw_trace)}; !backtrace_symbols.empty()) {
const auto [trace_out, _]{std::format_to_n(trace_buffer.data(), trace_buffer.size() - 1, "\n{}", backtrace_symbols)};
*trace_out = '\0';
return std::distance(trace_buffer.data(), trace_out);
} else if (auto backtrace_addresses{kphp::diagnostic::backtrace_addresses(raw_trace)}; !backtrace_addresses.empty()) {
const auto [trace_out, _]{std::format_to_n(trace_buffer.data(), trace_buffer.size() - 1, "{}", backtrace_addresses)};
*trace_out = '\0';
return std::distance(trace_buffer.data(), trace_out);
} else {
static constexpr std::string_view DEFAULT_TRACE = "[]";
const auto [trace_out, _]{std::format_to_n(trace_buffer.data(), trace_buffer.size() - 1, "{}", DEFAULT_TRACE)};
*trace_out = '\0';
return std::distance(trace_buffer.data(), trace_out);
}
}

size_t format_log_message(std::span<char> message_buffer, std::string_view fmt, std::format_args args) noexcept {
auto it{std::vformat_to(truncating_iterator{message_buffer.data(), message_buffer.size() - 1}, fmt, args)};
*it.dst = '\0';
return std::distance(message_buffer.data(), it.dst);
}

} // namespace kphp::log::impl
32 changes: 2 additions & 30 deletions runtime-light/stdlib/diagnostics/detail/logs.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,10 @@
#include <cstddef>
#include <cstring>
#include <format>
#include <iterator>
#include <span>
#include <string_view>
#include <type_traits>

#include "runtime-light/stdlib/diagnostics/backtrace.h"

namespace kphp::log {

namespace impl {
Expand All @@ -35,34 +32,9 @@ constexpr auto wrap_log_argument(T&& t) noexcept -> decltype(auto) {
template<typename T>
using wrapped_arg_t = std::invoke_result_t<decltype(impl::wrap_log_argument<T>), T>;

template<typename... Args>
size_t format_log_message(std::span<char> message_buffer, std::format_string<impl::wrapped_arg_t<Args>...> fmt, Args&&... args) noexcept {
auto [out, _]{std::format_to_n<decltype(message_buffer.data()), impl::wrapped_arg_t<Args>...>(message_buffer.data(), message_buffer.size() - 1, fmt,
impl::wrap_log_argument(std::forward<Args>(args))...)};
*out = '\0';
return std::distance(message_buffer.data(), out);
}

inline size_t resolve_log_trace(std::span<char> trace_buffer, std::span<void* const> raw_trace) noexcept {
if (trace_buffer.empty()) {
return 0;
}
size_t format_log_message(std::span<char> message_buffer, std::string_view fmt, std::format_args args) noexcept;

if (auto backtrace_symbols{kphp::diagnostic::backtrace_symbols(raw_trace)}; !backtrace_symbols.empty()) {
const auto [trace_out, _]{std::format_to_n(trace_buffer.data(), trace_buffer.size() - 1, "\n{}", backtrace_symbols)};
*trace_out = '\0';
return std::distance(trace_buffer.data(), trace_out);
} else if (auto backtrace_addresses{kphp::diagnostic::backtrace_addresses(raw_trace)}; !backtrace_addresses.empty()) {
const auto [trace_out, _]{std::format_to_n(trace_buffer.data(), trace_buffer.size() - 1, "{}", backtrace_addresses)};
*trace_out = '\0';
return std::distance(trace_buffer.data(), trace_out);
} else {
static constexpr std::string_view DEFAULT_TRACE = "[]";
const auto [trace_out, _]{std::format_to_n(trace_buffer.data(), trace_buffer.size() - 1, "{}", DEFAULT_TRACE)};
*trace_out = '\0';
return std::distance(trace_buffer.data(), trace_out);
}
}
size_t resolve_log_trace(std::span<char> trace_buffer, std::span<void* const> raw_trace) noexcept;

} // namespace impl

Expand Down
26 changes: 26 additions & 0 deletions runtime-light/stdlib/diagnostics/logs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Compiler for PHP (aka KPHP)
// Copyright (c) 2026 LLC «V Kontakte»
// Distributed under the GPL v3 License, see LICENSE.notice.txt

#include "runtime-light/stdlib/diagnostics/logs.h"

#include <array>
#include <cstddef>
#include <source_location>
#include <string_view>
#include <utility>

#include "runtime-light/k2-platform/k2-api.h"
#include "runtime-light/stdlib/diagnostics/detail/logs.h"

void kphp::log::assertion(bool condition, const std::source_location& location) noexcept {
if (!condition) [[unlikely]] {
std::array<char, impl::DEFAULT_LOG_BUFFER_SIZE> log_buffer; // NOLINT
size_t message_size{
impl::format_log_message(log_buffer, "assertion failed at {}:{}",
std::make_format_args(kphp::log::impl::unmove(location.file_name()), kphp::log::impl::unmove(location.line())))};
auto message{std::string_view{log_buffer.data(), static_cast<std::string_view::size_type>(message_size)}};
k2::log(std::to_underlying(level::error), message, {});
k2::exit(1);
}
}
18 changes: 8 additions & 10 deletions runtime-light/stdlib/diagnostics/logs.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,16 @@ namespace impl {

static constexpr size_t DEFAULT_LOG_BUFFER_SIZE = 2048UZ;

template<typename T>
const T& unmove(T&& x) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't look good at all. It may lead to dangling references all around. Let's try to get rid of it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

return x;
}

template<typename... Args>
void log(level level, std::optional<std::span<void* const>> trace, std::format_string<impl::wrapped_arg_t<Args>...> fmt, Args&&... args) noexcept {
std::array<char, DEFAULT_LOG_BUFFER_SIZE> log_buffer; // NOLINT
size_t message_size{impl::format_log_message(log_buffer, fmt, std::forward<Args>(args)...)};
size_t message_size{
impl::format_log_message(log_buffer, fmt.get(), std::make_format_args(kphp::log::impl::unmove(impl::wrap_log_argument(std::forward<Args>(args)))...))};
auto message{std::string_view{log_buffer.data(), static_cast<std::string_view::size_type>(message_size)}};

auto opt_tags{
Expand Down Expand Up @@ -66,15 +72,7 @@ void log(level level, std::optional<std::span<void* const>> trace, std::format_s

// The backtrace algorithm relies on the fact that assertion does not call backtrace.
// If assertion is modified, the backtrace algorithm should be updated accordingly
inline void assertion(bool condition, const std::source_location& location = std::source_location::current()) noexcept {
if (!condition) [[unlikely]] {
std::array<char, impl::DEFAULT_LOG_BUFFER_SIZE> log_buffer; // NOLINT
size_t message_size{impl::format_log_message(log_buffer, "assertion failed at {}:{}", location.file_name(), location.line())};
auto message{std::string_view{log_buffer.data(), static_cast<std::string_view::size_type>(message_size)}};
k2::log(std::to_underlying(level::error), message, {});
k2::exit(1);
}
}
void assertion(bool condition, const std::source_location& location = std::source_location::current()) noexcept;

template<typename... Args>
[[noreturn]] void error(std::format_string<impl::wrapped_arg_t<Args>...> fmt, Args&&... args) noexcept {
Expand Down
2 changes: 2 additions & 0 deletions runtime-light/stdlib/stdlib.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ prepend(
crypto/crypto-functions.cpp
curl/curl-state.cpp
web-transfer-lib/web-state.cpp
diagnostics/detail/logs.cpp
diagnostics/backtrace.cpp
diagnostics/contextual-tags.cpp
diagnostics/error-handling-state.cpp
diagnostics/logs.cpp
diagnostics/php-assert.cpp
file/file-system-state.cpp
file/file-system-functions.cpp
Expand Down
Loading