Skip to content
Merged
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
14 changes: 10 additions & 4 deletions include/silk/util/assert.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,26 @@ static constexpr bool DebugAssertionsEnabled = false;
#endif

/**
* Print a symbolized stack trace, the error message, and optional details, then abort.
* Print a symbolized stack trace, the error message, optional printf-formatted
* details, and abort. `fmt` is a printf-style format string; pass nullptr (or
* omit) to skip the details line.
*/
[[noreturn]] void assertFail(const char * message, const char * file, int line, const char * details = nullptr) noexcept;
[[noreturn]] void assertFail(const char * message, const char * file, int line, const char * fmt = nullptr, ...) noexcept
__attribute__((format(printf, 4, 5)));

} // namespace silk

// Unconditional abort.
#define SILK_FAIL(message, ...) silk::assertFail(message, __FILE__, __LINE__ __VA_OPT__(, ) __VA_ARGS__)

// Assertion active in all builds unless DISABLE_ASSERTIONS is defined.
// Use for invariants that must hold in release -- hard internal contract violations.
#define SILK_ASSERT(condition, ...) \
do \
{ \
if (silk::ReleaseAssertionsEnabled && !(condition)) [[unlikely]] \
{ \
silk::assertFail("assertion failed: " #condition, __FILE__, __LINE__ __VA_OPT__(, std::format(__VA_ARGS__).c_str())); \
silk::assertFail("assertion failed: " #condition, __FILE__, __LINE__ __VA_OPT__(, ) __VA_ARGS__); \
} \
} while (0)

Expand All @@ -40,6 +46,6 @@ static constexpr bool DebugAssertionsEnabled = false;
{ \
if (silk::DebugAssertionsEnabled && !(condition)) [[unlikely]] \
{ \
silk::assertFail("assertion failed: " #condition, __FILE__, __LINE__ __VA_OPT__(, std::format(__VA_ARGS__).c_str())); \
silk::assertFail("assertion failed: " #condition, __FILE__, __LINE__ __VA_OPT__(, ) __VA_ARGS__); \
} \
} while (0)
43 changes: 26 additions & 17 deletions include/silk/util/logger.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
#pragma once

#include <atomic>
#include <format>
#include <string_view>

namespace silk
{

// Log severity, ordered low-to-high. A record is emitted when its level is
// >= the logger's configured minimum (default INFO; change via Logger::setLevel).
enum class LogLevel
{
DEBUG,
Expand All @@ -26,30 +26,39 @@ class Logger
{
public:
/** Set the minimum level for messages to be emitted. */
static void setLevel(LogLevel level) noexcept;
static void setLevel(LogLevel level) noexcept { currentLevel.store(level, std::memory_order_relaxed); }

/** Return true if messages at the given level would be emitted. */
static bool isEnabled(LogLevel level) noexcept { return level >= currentLevel.load(std::memory_order_relaxed); }

/** Emit a pre-formatted message. */
static void log(LogLevel level, std::string_view file, int line, std::string_view message) noexcept;
/** Emit a pre-formatted message verbatim (no printf parsing). */
static void log(LogLevel level, const char * file, int line, const char * message) noexcept;

/** Format and emit a message. */
template <typename... Args>
static void log(LogLevel level, std::string_view file, int line, std::format_string<Args...> fmt, Args &&... args) noexcept
{
log(level, file, line, std::format(fmt, std::forward<Args>(args)...));
}
/** Emit a printf-formatted message. */
static void logf(LogLevel level, const char * file, int line, const char * fmt, ...) noexcept __attribute__((format(printf, 4, 5)));

private:
inline static std::atomic<LogLevel> currentLevel{LogLevel::INFO};
};

} // namespace silk

// clang-format off
#define SILK_DEBUG(...) do { if (silk::Logger::isEnabled(silk::LogLevel::DEBUG)) silk::Logger::log(silk::LogLevel::DEBUG, __FILE__, __LINE__, __VA_ARGS__); } while (0)
#define SILK_INFO(...) do { if (silk::Logger::isEnabled(silk::LogLevel::INFO)) silk::Logger::log(silk::LogLevel::INFO, __FILE__, __LINE__, __VA_ARGS__); } while (0)
#define SILK_WARN(...) do { if (silk::Logger::isEnabled(silk::LogLevel::WARN)) silk::Logger::log(silk::LogLevel::WARN, __FILE__, __LINE__, __VA_ARGS__); } while (0)
#define SILK_ERROR(...) do { if (silk::Logger::isEnabled(silk::LogLevel::ERROR)) silk::Logger::log(silk::LogLevel::ERROR, __FILE__, __LINE__, __VA_ARGS__); } while (0)
// clang-format on
// Emit a log record at the given level. Dispatches to Logger::log for plain
// strings (SILK_INFO("text")) and to Logger::logf for printf-style calls
// (SILK_INFO("foo %d", x)) -- the latter is the only path that pays vsnprintf.
// The format string is checked at every call site (-Wformat); formatting only
// runs when the level is enabled, so a disabled-level call site is one relaxed
// atomic load.
#define SILK_LOG(level, fmt, ...) \
do \
{ \
if (silk::Logger::isEnabled(level)) \
{ \
silk::Logger::log##__VA_OPT__(f)(level, __FILE__, __LINE__, fmt __VA_OPT__(, ) __VA_ARGS__); \
} \
} while (0)

#define SILK_DEBUG(fmt, ...) SILK_LOG(silk::LogLevel::DEBUG, fmt __VA_OPT__(, ) __VA_ARGS__)
#define SILK_INFO(fmt, ...) SILK_LOG(silk::LogLevel::INFO, fmt __VA_OPT__(, ) __VA_ARGS__)
#define SILK_WARN(fmt, ...) SILK_LOG(silk::LogLevel::WARN, fmt __VA_OPT__(, ) __VA_ARGS__)
#define SILK_ERROR(fmt, ...) SILK_LOG(silk::LogLevel::ERROR, fmt __VA_OPT__(, ) __VA_ARGS__)
9 changes: 4 additions & 5 deletions src/fibers/fiber.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
#include <atomic>
#include <cerrno>
#include <cstdint>
#include <format>
#include <memory>
#include <mutex>
#include <thread>
Expand Down Expand Up @@ -398,15 +397,15 @@ void Fiber::fiberContextMain(boost::context::detail::transfer_t transfer) noexce
fiber->result = fiber->fiberMain(fiber->parameters);
fiber->changeState(FiberState::RUNNING, FiberState::STOPPED);
fiber->switchToThreadContext(true);
SILK_ASSERT(false, "unreachable");
SILK_FAIL("unreachable");
}

void Fiber::changeState(FiberState expectedState, FiberState newState) noexcept
{
FiberState prevState = state.exchange(newState, std::memory_order_acq_rel);
SILK_ASSERT(
prevState == expectedState,
"invalid fiber state: expected={}, actual={}",
"invalid fiber state: expected=%d, actual=%d",
static_cast<int>(expectedState),
static_cast<int>(prevState));
}
Expand All @@ -430,7 +429,7 @@ bool Fiber::tryChangeStateToSuspended() noexcept
// runFiber will enqueue the fiber after the callback returns.
return false;
default:
SILK_ASSERT(false, "Unexpected fiber state: {}", static_cast<int>(currentState));
SILK_FAIL("unexpected fiber state", "state=%d", static_cast<int>(currentState));
}
}
}
Expand All @@ -457,7 +456,7 @@ bool Fiber::tryChangeStateToReady() noexcept
}
break;
default:
SILK_ASSERT(false, "Unexpected fiber state: {}", static_cast<int>(currentState));
SILK_FAIL("unexpected fiber state", "state=%d", static_cast<int>(currentState));
}
}
}
Expand Down
1 change: 0 additions & 1 deletion src/fibers/mutex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#include <silk/util/spinlock.h>

#include <atomic>
#include <format>

namespace silk
{
Expand Down
5 changes: 2 additions & 3 deletions src/perf/fiber-http.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#include <Poco/Net/StreamSocket.h>

#include <cerrno>
#include <format>

#include <poll.h>
#include <unistd.h>
Expand Down Expand Up @@ -130,7 +129,7 @@ bool FiberSocketImpl::poll(const Poco::Timespan & timeout, int mode)
int FiberSocketImpl::sendBytes(const void * buffer, int length, int flags)
{
// Poco flags (MSG_NOSIGNAL, MSG_PEEK, etc.) are not plumbed through to io_uring read/write.
SILK_ASSERT(flags == 0, "FiberSocketImpl::sendBytes does not support flags (got {})", flags);
SILK_ASSERT(flags == 0, "FiberSocketImpl::sendBytes does not support flags (got %d)", flags);

int total = 0;
const char * ptr = static_cast<const char *>(buffer);
Expand Down Expand Up @@ -170,7 +169,7 @@ int FiberSocketImpl::sendBytes(const void * buffer, int length, int flags)
int FiberSocketImpl::receiveBytes(void * buffer, int length, int flags)
{
// Poco flags (MSG_NOSIGNAL, MSG_PEEK, etc.) are not plumbed through to io_uring read/write.
SILK_ASSERT(flags == 0, "FiberSocketImpl::receiveBytes does not support flags (got {})", flags);
SILK_ASSERT(flags == 0, "FiberSocketImpl::receiveBytes does not support flags (got %d)", flags);

#if defined(USE_IO_URING_RW)
uint64_t bytesRead = 0;
Expand Down
21 changes: 10 additions & 11 deletions src/perf/file-perf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <format>
#include <iostream>
#include <memory>
#include <string>
Expand Down Expand Up @@ -221,7 +220,7 @@ void Benchmark::start()
for (Job & job : jobs)
{
int r = silk::FiberScheduler::run(workerFiberMain, {this, &job}, &job.future);
SILK_ASSERT(!r, "cannot start fiber: {}", std::strerror(r));
SILK_ASSERT(!r, "cannot start fiber: %s", std::strerror(r));
}

warmupEndCycles.store(silk::Tsc::getCycles() + silk::Tsc::nanosecondsToCycles(cfg.warmupNs), std::memory_order_relaxed);
Expand Down Expand Up @@ -280,7 +279,7 @@ int Benchmark::workerFiberMain(WorkerFiberParams * params) noexcept
int r = slot->future.wait();
if (r)
{
SILK_ERROR("request failed: {}", std::strerror(r));
SILK_ERROR("request failed: %s", std::strerror(r));
break;
}

Expand All @@ -300,7 +299,7 @@ int Benchmark::workerFiberMain(WorkerFiberParams * params) noexcept
for (uint32_t i = 0; i < benchmark->cfg.iodepth; ++i)
{
int r = job->slots[i].future.wait();
SILK_ERROR("request failed: {}", std::strerror(r));
SILK_ERROR("request failed: %s", std::strerror(r));
}

return 0;
Expand Down Expand Up @@ -403,14 +402,14 @@ int main(int argc, char ** argv)

int openFlags = O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC | (cfg.direct ? O_DIRECT : 0);
int fd = ::open(cfg.filename.c_str(), openFlags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
SILK_ASSERT(fd >= 0, "open failed: {}", std::strerror(errno));
SILK_ASSERT(fd >= 0, "open failed: %s", std::strerror(errno));

struct stat st;
int r = ::fstat(fd, &st);
SILK_ASSERT(!r, "fstat failed: {}", std::strerror(errno));
SILK_ASSERT(!r, "fstat failed: %s", std::strerror(errno));

r = ::fallocate(fd, 0, 0, static_cast<off_t>(cfg.fileSize));
SILK_ASSERT(!r, "fallocate failed: {}", std::strerror(errno));
SILK_ASSERT(!r, "fallocate failed: %s", std::strerror(errno));

sigset_t mask = blockSignals();

Expand All @@ -426,8 +425,8 @@ int main(int argc, char ** argv)
silk::FiberScheduler::initialize(&options);

SILK_INFO(
"starting benchmark on: {} size={:.1f}GiB{}",
cfg.filename,
"starting benchmark on: %s size=%.1fGiB%s",
cfg.filename.c_str(),
static_cast<double>(cfg.fileSize) / (1024.0 * 1024 * 1024),
cfg.direct ? " direct" : "");

Expand All @@ -440,13 +439,13 @@ int main(int argc, char ** argv)

if (cfg.warmupNs > 0)
{
SILK_INFO("warming up for {}...", formatDuration(cfg.warmupNs));
SILK_INFO("warming up for %s...", formatDuration(cfg.warmupNs).c_str());
signalled = sigwaitFor(mask, cfg.warmupNs);
}

if (!signalled)
{
SILK_INFO("measuring for {}...", formatDuration(cfg.durationNs));
SILK_INFO("measuring for %s...", formatDuration(cfg.durationNs).c_str());
sigwaitFor(mask, cfg.durationNs);
}

Expand Down
Loading
Loading