Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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"

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.

Follow the style guide in context of include order

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.

I think I do. What's wrong here?


namespace kphp::log::impl {

namespace {

struct truncating_iterator {
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;

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.

nit: brace init


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
40 changes: 40 additions & 0 deletions runtime-light/stdlib/diagnostics/error-handling-functions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// 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/error-handling-functions.h"

#include <array>
#include <cstddef>
#include <format>
#include <iterator>
#include <span>
#include <utility>

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

array<array<string>> error_handling_impl_::format_backtrace_addresses(std::span<void* const> backtrace) noexcept {
static constexpr size_t LOG_BUFFER_SIZE = 32;

auto resolved_backtrace{kphp::diagnostic::backtrace_addresses(backtrace)};
if (resolved_backtrace.empty()) {
return {};
}

array<array<string>> backtrace_addresses{array_size{static_cast<int64_t>(backtrace.size()), true}};
const string function_key{FUNCTION_KEY.data(), FUNCTION_KEY.size()};

for (const auto& address : resolved_backtrace) {
std::array<char, LOG_BUFFER_SIZE> log_buffer{};
const auto [out, _]{std::format_to_n(log_buffer.data(), log_buffer.size() - 1, "{}", address)};
*out = '\0';
const auto recorded{std::distance(log_buffer.data(), out)};
array<string> frame_info{array_size{1, false}};
frame_info.set_value(function_key, string{log_buffer.data(), static_cast<string::size_type>(recorded)});

backtrace_addresses.emplace_back(std::move(frame_info));
}

return backtrace_addresses;
}
27 changes: 1 addition & 26 deletions runtime-light/stdlib/diagnostics/error-handling-functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
#include <array>
#include <cstddef>
#include <cstdint>
#include <format>
#include <iterator>
#include <span>
#include <string_view>
#include <utility>
Expand Down Expand Up @@ -48,30 +46,7 @@ inline array<array<string>> format_backtrace_symbols(std::span<void* const> back
return backtrace_symbols;
}

inline array<array<string>> format_backtrace_addresses(std::span<void* const> backtrace) noexcept {
static constexpr size_t LOG_BUFFER_SIZE = 32;

auto resolved_backtrace{kphp::diagnostic::backtrace_addresses(backtrace)};
if (resolved_backtrace.empty()) {
return {};
}

array<array<string>> backtrace_addresses{array_size{static_cast<int64_t>(backtrace.size()), true}};
const string function_key{FUNCTION_KEY.data(), FUNCTION_KEY.size()};

for (const auto& address : resolved_backtrace) {
std::array<char, LOG_BUFFER_SIZE> log_buffer{};
const auto [out, _]{std::format_to_n(log_buffer.data(), log_buffer.size() - 1, "{}", address)};
*out = '\0';
const auto recorded{std::distance(log_buffer.data(), out)};
array<string> frame_info{array_size{1, false}};
frame_info.set_value(function_key, string{log_buffer.data(), static_cast<string::size_type>(recorded)});

backtrace_addresses.emplace_back(std::move(frame_info));
}

return backtrace_addresses;
}
array<array<string>> format_backtrace_addresses(std::span<void* const> backtrace) noexcept;
} // namespace error_handling_impl_

inline array<array<string>> f$debug_backtrace() noexcept {
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
const auto* file_name{location.file_name()};
auto line{location.line()};
size_t message_size{impl::format_log_message(log_buffer, "assertion failed at {}:{}", std::make_format_args(file_name, 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);
}
}
31 changes: 14 additions & 17 deletions runtime-light/stdlib/diagnostics/logs.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,9 @@ namespace impl {

static constexpr size_t DEFAULT_LOG_BUFFER_SIZE = 2048UZ;

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 {
inline void log(level level, std::optional<std::span<void* const>> trace, std::string_view fmt, std::format_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, 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 @@ -62,19 +61,17 @@ void log(level level, std::optional<std::span<void* const>> trace, std::format_s
k2::log(std::to_underlying(level), message, tagged_entries);
}

template<typename... Args>
void log_wrapped(level level, std::optional<std::span<void* const>> trace, std::format_string<impl::wrapped_arg_t<Args>...> fmt,
impl::wrapped_arg_t<Args>... args) noexcept {
kphp::log::impl::log(level, trace, fmt.get(), std::make_format_args(args...));
}

} // namespace impl

// 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 All @@ -84,7 +81,7 @@ template<typename... Args>
std::array<void*, kphp::diagnostic::DEFAULT_BACKTRACE_MAX_SIZE> backtrace{};
const size_t num_frames{kphp::diagnostic::backtrace(backtrace)};
const std::span<void* const> backtrace_view{backtrace.data(), num_frames};
impl::log(level::error, backtrace_view, fmt, std::forward<Args>(args)...);
impl::log_wrapped<Args...>(level::error, backtrace_view, fmt, impl::wrap_log_argument(std::forward<Args>(args))...);
}
k2::exit(1);
}
Expand All @@ -99,7 +96,7 @@ void warning(std::format_string<impl::wrapped_arg_t<Args>...> fmt, Args&&... arg
std::array<void*, kphp::diagnostic::DEFAULT_BACKTRACE_MAX_SIZE> backtrace{};
const size_t num_frames{kphp::diagnostic::backtrace(backtrace)};
const std::span<void* const> backtrace_view{backtrace.data(), num_frames};
impl::log(level::warn, backtrace_view, fmt, std::forward<Args>(args)...);
impl::log_wrapped<Args...>(level::warn, backtrace_view, fmt, impl::wrap_log_argument(std::forward<Args>(args))...);
}
}

Expand All @@ -110,21 +107,21 @@ void info(std::format_string<impl::wrapped_arg_t<Args>...> fmt, Args&&... args)
return;
}
if (std::to_underlying(level::info) <= k2::log_level_enabled()) {
impl::log(level::info, std::nullopt, fmt, std::forward<Args>(args)...);
impl::log_wrapped<Args...>(level::info, std::nullopt, fmt, impl::wrap_log_argument(std::forward<Args>(args))...);
}
}

template<typename... Args>
void debug(std::format_string<impl::wrapped_arg_t<Args>...> fmt, Args&&... args) noexcept {
if (std::to_underlying(level::debug) <= k2::log_level_enabled()) {
impl::log(level::debug, std::nullopt, fmt, std::forward<Args>(args)...);
impl::log_wrapped<Args...>(level::debug, std::nullopt, fmt, impl::wrap_log_argument(std::forward<Args>(args))...);
}
}

template<typename... Args>
void trace(std::format_string<impl::wrapped_arg_t<Args>...> fmt, Args&&... args) noexcept {
if (std::to_underlying(level::trace) <= k2::log_level_enabled()) {
impl::log(level::trace, std::nullopt, fmt, std::forward<Args>(args)...);
impl::log_wrapped<Args...>(level::trace, std::nullopt, fmt, impl::wrap_log_argument(std::forward<Args>(args))...);
}
}

Expand Down
66 changes: 66 additions & 0 deletions runtime-light/stdlib/math/random-functions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// 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/math/random-functions.h"

#include <array>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <format>
#include <memory>
#include <utility>

#include "runtime-common/core/runtime-core.h"
#include "runtime-light/coroutine/task.h"
#include "runtime-light/k2-platform/k2-api.h"
#include "runtime-light/stdlib/system/system-functions.h"

namespace {

// Analogue of unix's `gettimeofday`
// Returns seconds elapsed since Epoch, and milliseconds elapsed from the last second.
std::pair<std::chrono::seconds, std::chrono::microseconds> system_seconds_and_micros() noexcept {
k2::SystemTime timeval{};
k2::system_time(std::addressof(timeval));
std::chrono::nanoseconds nanos_since_epoch{timeval.since_epoch_ns};
std::chrono::microseconds micros_since_epoch{std::chrono::duration_cast<std::chrono::microseconds>(nanos_since_epoch)};
std::chrono::seconds seconds_since_epoch{std::chrono::duration_cast<std::chrono::seconds>(nanos_since_epoch)};

std::chrono::microseconds micros_since_last_second{micros_since_epoch - std::chrono::duration_cast<std::chrono::microseconds>(seconds_since_epoch)};
return {
seconds_since_epoch,
micros_since_last_second,
};
}

} // namespace

kphp::coro::task<string> f$uniqid(string prefix, bool more_entropy) noexcept {
if (!more_entropy) {
co_await f$usleep(1);
}

auto [sec, susec]{system_seconds_and_micros()};
auto sec_cnt{static_cast<int32_t>(sec.count() & 0xFFFFFFFF)}; // because we'll use only 8 hex digits
auto susec_cnt{static_cast<int32_t>(susec.count() & 0xFFFFF)}; // because we'll use only 5 hex digits
constexpr size_t buf_size = 30;
std::array<char, buf_size> buf{};
auto& runtime_context{RuntimeContext::get()};
runtime_context.static_SB.clean() << prefix;

if (more_entropy) {
// we multiply by 10 to get (0..10) value out of (0..1), because we want random digit before the point.
double lcg_rand_value{f$lcg_value() * 10};
std::format_to_n(buf.data(), buf_size, "{:08x}{:05x}{:.8f}", sec_cnt, susec_cnt, lcg_rand_value);
constexpr size_t rand_len = 23;
runtime_context.static_SB.append(buf.data(), rand_len);
} else {
std::format_to_n(buf.data(), buf_size, "{:08x}{:05x}", sec_cnt, susec_cnt);
constexpr size_t rand_len = 13;
runtime_context.static_SB.append(buf.data(), rand_len);
}

co_return runtime_context.static_SB.str();
}
Loading
Loading