CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
453 lines
13 KiB
C++
453 lines
13 KiB
C++
/*
|
|
* Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#include "top.hpp"
|
|
#include "utils/debug.hpp"
|
|
#include "os/os.hpp"
|
|
|
|
#if !defined(AMD_LOG_LEVEL)
|
|
#include "utils/flags.hpp"
|
|
#endif
|
|
|
|
#include <cstdlib>
|
|
#include <cstdio>
|
|
#include <cstdarg>
|
|
#include <thread>
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
#include <inttypes.h>
|
|
#include <atomic>
|
|
#include <mutex>
|
|
#include <condition_variable>
|
|
#include <vector>
|
|
#include <memory>
|
|
#include <chrono>
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#include <io.h>
|
|
#else
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#endif // _WIN32
|
|
|
|
namespace amd {
|
|
|
|
FILE* outFile = stderr;
|
|
|
|
void truncate_log_file();
|
|
static void TruncateLogFileFlushPath();
|
|
|
|
static void crashFlushCallback();
|
|
|
|
// ================================================================================================
|
|
// Async logging infrastructure
|
|
// ================================================================================================
|
|
|
|
static std::atomic<bool> crashHandlersInstalled_{false};
|
|
|
|
struct LogEntry {
|
|
LogLevel level; //!< Log severity level
|
|
const char* file; //!< Source file name
|
|
int line; //!< Source line number
|
|
std::string message; //!< Formatted log message
|
|
uint64_t timestamp; //!< Timestamp in microseconds
|
|
uint32_t pid; //!< Process ID
|
|
uint32_t tid; //!< Thread ID (hash)
|
|
uint64_t duration; //!< Duration in microseconds (0 if not a duration log)
|
|
bool hasDuration; //!< True if this is a duration log entry
|
|
std::atomic<bool> valid; //!< Valid flag for lock-free synchronization
|
|
|
|
LogEntry()
|
|
: level(LOG_NONE),
|
|
file(""),
|
|
line(0),
|
|
timestamp(0),
|
|
pid(0),
|
|
duration(0),
|
|
hasDuration(false),
|
|
valid(false) {}
|
|
};
|
|
|
|
class AsyncLogger {
|
|
public:
|
|
AsyncLogger() : buffer_(kBufferSize) {
|
|
bool useAsync = !flagIsDefault(AMD_LOG_ASYNC) ? AMD_LOG_ASYNC
|
|
: !flagIsDefault(AMD_LOG_LEVEL_FILE);
|
|
if (useAsync) {
|
|
enable(true);
|
|
}
|
|
}
|
|
|
|
~AsyncLogger() {
|
|
enable(false);
|
|
stop();
|
|
}
|
|
|
|
void start() {
|
|
if (!running_.load(std::memory_order_relaxed)) {
|
|
running_.store(true, std::memory_order_relaxed);
|
|
enabled_.store(true, std::memory_order_relaxed);
|
|
workerThread_ = std::thread(&AsyncLogger::workerLoop, this);
|
|
}
|
|
}
|
|
|
|
void stop() {
|
|
if (running_.load(std::memory_order_relaxed)) {
|
|
running_.store(false, std::memory_order_relaxed);
|
|
flushCV_.notify_all();
|
|
if (workerThread_.joinable()) {
|
|
workerThread_.join();
|
|
}
|
|
}
|
|
}
|
|
|
|
void enable(bool enable) {
|
|
enabled_.store(enable, std::memory_order_relaxed);
|
|
if (enable && !running_.load(std::memory_order_relaxed)) {
|
|
start();
|
|
}
|
|
|
|
if (enable) {
|
|
bool expected = false;
|
|
if (crashHandlersInstalled_.compare_exchange_strong(expected, true,
|
|
std::memory_order_acq_rel)) {
|
|
if (!Os::installExceptionHandlers(crashFlushCallback)) {
|
|
crashHandlersInstalled_.store(false, std::memory_order_release);
|
|
}
|
|
}
|
|
} else {
|
|
bool expected = true;
|
|
if (crashHandlersInstalled_.compare_exchange_strong(expected, false,
|
|
std::memory_order_acq_rel)) {
|
|
Os::uninstallExceptionHandlers();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool isEnabled() const {
|
|
return enabled_.load(std::memory_order_relaxed);
|
|
}
|
|
|
|
void log(LogLevel level, const char* file, int line, const char* message,
|
|
uint64_t timestamp, uint64_t duration = 0, bool hasDuration = false) {
|
|
if (!enabled_.load(std::memory_order_relaxed)) {
|
|
return; // Fall back to sync logging
|
|
}
|
|
|
|
size_t currentWrite = writeIndex_.fetch_add(1, std::memory_order_release);
|
|
size_t currentRead = readIndex_.load(std::memory_order_acquire);
|
|
|
|
// Check if buffer is full
|
|
while (currentWrite - currentRead >= kBufferSize) {
|
|
flush();
|
|
currentRead = readIndex_.load(std::memory_order_acquire);
|
|
}
|
|
// Write to buffer (lock-free)
|
|
LogEntry& entry = buffer_[currentWrite % kBufferSize];
|
|
entry.level = level;
|
|
entry.file = file ? file : "";
|
|
entry.line = line;
|
|
entry.message = message ? message : "";
|
|
entry.timestamp = timestamp;
|
|
entry.pid = Os::getProcessId();
|
|
entry.tid = static_cast<uint32_t>(
|
|
std::hash<std::thread::id>{}(std::this_thread::get_id()) & 0xFFFFF);
|
|
entry.duration = duration;
|
|
entry.hasDuration = hasDuration;
|
|
entry.valid.store(true, std::memory_order_release);
|
|
}
|
|
|
|
void flush() {
|
|
if (enabled_.load(std::memory_order_relaxed)) {
|
|
flushCV_.notify_all();
|
|
}
|
|
}
|
|
|
|
void flushInCurrentThread() {
|
|
if (enabled_.load(std::memory_order_relaxed)) {
|
|
flushPending();
|
|
}
|
|
}
|
|
|
|
void FlushOnCrash() noexcept {
|
|
if (!enabled_.load(std::memory_order_relaxed)) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
flushPending();
|
|
} catch (...) {
|
|
}
|
|
}
|
|
|
|
private:
|
|
static constexpr size_t kBufferSize = 16 * 1024;//!< Circular buffer size
|
|
static constexpr size_t kFlushIntervalMs = 1; //!< Flush interval in milliseconds
|
|
|
|
std::vector<LogEntry> buffer_; //!< Circular buffer of log entries
|
|
std::atomic<size_t> writeIndex_{0}; //!< Write position in circular buffer
|
|
std::atomic<size_t> readIndex_{0}; //!< Read position in circular buffer
|
|
std::atomic<bool> running_{false}; //!< Worker thread running flag
|
|
std::atomic<bool> enabled_{false}; //!< Async logging enabled flag
|
|
|
|
std::thread workerThread_; //!< Background worker thread for flushing
|
|
std::mutex flushMutex_; //!< Mutex for flush condition variable
|
|
std::condition_variable flushCV_; //!< Condition variable for worker wakeup
|
|
|
|
void workerLoop() {
|
|
while (running_.load(std::memory_order_relaxed)) {
|
|
std::unique_lock<std::mutex> lock(flushMutex_);
|
|
flushCV_.wait_for(lock, std::chrono::milliseconds(kFlushIntervalMs),
|
|
[this] { return !running_.load(std::memory_order_relaxed); });
|
|
|
|
if (running_.load(std::memory_order_relaxed)) {
|
|
flushPending();
|
|
}
|
|
}
|
|
// Final flush on shutdown
|
|
flushPending();
|
|
}
|
|
|
|
void flushPending() {
|
|
TruncateLogFileFlushPath();
|
|
|
|
size_t currentRead = readIndex_.load(std::memory_order_acquire);
|
|
size_t currentWrite = writeIndex_.load(std::memory_order_acquire);
|
|
size_t writeCount = 0;
|
|
|
|
while (currentRead != currentWrite) {
|
|
LogEntry& entry = buffer_[currentRead % kBufferSize];
|
|
|
|
// Wait for valid flag
|
|
while (!entry.valid.load(std::memory_order_acquire)) {
|
|
std::this_thread::yield();
|
|
}
|
|
|
|
writeToFile(entry);
|
|
entry.valid.store(false, std::memory_order_release);
|
|
|
|
currentRead++;
|
|
readIndex_.store(currentRead, std::memory_order_release);
|
|
writeCount++;
|
|
if (writeCount % 1024 == 0) {
|
|
fflush(outFile);
|
|
}
|
|
currentWrite = writeIndex_.load(std::memory_order_acquire);
|
|
}
|
|
fflush(outFile);
|
|
}
|
|
|
|
void writeToFile(const LogEntry& entry) {
|
|
char pidtid[64] = "";
|
|
if (AMD_LOG_LEVEL >= 4) {
|
|
snprintf(pidtid, sizeof(pidtid), "[pid:%u tid: 0x%05x]", entry.pid, entry.tid);
|
|
}
|
|
|
|
if (entry.hasDuration) {
|
|
fprintf(outFile, ":%d:%-25s:%-4d: %010" PRIu64 " us: %s %s: duration: %" PRIu64 " us\n",
|
|
entry.level, entry.file, entry.line, entry.timestamp,
|
|
pidtid, entry.message.c_str(), entry.duration);
|
|
} else {
|
|
fprintf(outFile, ":%d:%-25s:%-4d: %010" PRIu64 " us: %s %s\n",
|
|
entry.level, entry.file, entry.line, entry.timestamp,
|
|
pidtid, entry.message.c_str());
|
|
}
|
|
}
|
|
};
|
|
|
|
// ================================================================================================
|
|
static AsyncLogger& getAsyncLogger();
|
|
|
|
static void crashFlushCallback() {
|
|
getAsyncLogger().FlushOnCrash();
|
|
}
|
|
|
|
static AsyncLogger& getAsyncLogger() {
|
|
static AsyncLogger instance;
|
|
return instance;
|
|
}
|
|
|
|
// ================================================================================================
|
|
static void TruncateLogFileFlushPath() {
|
|
if (outFile == stderr) {
|
|
return;
|
|
}
|
|
|
|
const size_t maxLogSize = AMD_LOG_LEVEL_SIZE * Mi;
|
|
|
|
fflush(outFile);
|
|
if (fseek(outFile, 0, SEEK_END) != 0) {
|
|
return;
|
|
}
|
|
|
|
long size = ftell(outFile);
|
|
if (size < 0 || static_cast<size_t>(size) <= maxLogSize) {
|
|
return;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
int fd = _fileno(outFile);
|
|
if (fd < 0 || _chsize_s(fd, 0) != 0) {
|
|
#else
|
|
int fd = fileno(outFile);
|
|
if (fd < 0 || ftruncate(fd, 0) != 0) {
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
fseek(outFile, 0, SEEK_SET);
|
|
}
|
|
|
|
// ================================================================================================
|
|
void truncate_log_file() {
|
|
if (outFile != stderr) {
|
|
fseek(outFile, 0, SEEK_END);
|
|
long size = ftell(outFile);
|
|
|
|
const size_t maxLogSize = AMD_LOG_LEVEL_SIZE * Mi;
|
|
if (size > maxLogSize) {
|
|
if (nullptr == freopen(NULL, "w", outFile)) {
|
|
outFile = stderr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ================================================================================================
|
|
void report_warning(const char* message) {
|
|
truncate_log_file();
|
|
fprintf(outFile, "Warning: %s\n", message);
|
|
}
|
|
|
|
// ================================================================================================
|
|
void log_entry(LogLevel level, const char* file, int line, const char* message) {
|
|
if (level == LOG_NONE) {
|
|
return;
|
|
}
|
|
truncate_log_file();
|
|
fprintf(outFile, ":%d:%s:%d: %s\n", level, file, line, message);
|
|
fflush(outFile);
|
|
}
|
|
|
|
// ================================================================================================
|
|
void log_timestamped(LogLevel level, const char* file, int line, const char* message) {
|
|
static bool gotstart = false; // not thread-safe, but not scary if fails
|
|
static uint64_t start;
|
|
|
|
if (!gotstart) {
|
|
start = Os::timeNanos();
|
|
gotstart = true;
|
|
}
|
|
|
|
uint64_t time = Os::timeNanos() - start;
|
|
if (level == LOG_NONE) {
|
|
return;
|
|
}
|
|
|
|
truncate_log_file();
|
|
fprintf(outFile, ":% 2d:%15s:% 5d: (%010lld) us %s\n", level, file, line, time / 1000ULL,
|
|
message);
|
|
fflush(outFile);
|
|
}
|
|
|
|
// ================================================================================================
|
|
void log_printf(LogLevel level, const char* file, int line, const char* format, ...) {
|
|
va_list ap;
|
|
char message[4096];
|
|
va_start(ap, format);
|
|
vsnprintf(message, sizeof(message), format, ap);
|
|
va_end(ap);
|
|
uint64_t timeUs = Os::timeNanos() / 1000ULL;
|
|
|
|
// Try async logging first
|
|
if (getAsyncLogger().isEnabled()) {
|
|
getAsyncLogger().log(level, file, line, message, timeUs);
|
|
return;
|
|
}
|
|
|
|
// Fall back to sync logging
|
|
char pidtid[64] = "";
|
|
if (AMD_LOG_LEVEL >= 4) {
|
|
size_t tid_hash = std::hash<std::thread::id>{}(std::this_thread::get_id());
|
|
snprintf(pidtid, sizeof(pidtid), "[pid:%u tid: 0x%05zx]",
|
|
Os::getProcessId(), tid_hash & 0xFFFFF);
|
|
}
|
|
|
|
truncate_log_file();
|
|
|
|
fprintf(outFile, ":%d:%-25s:%-4d: %010" PRIu64 " us: %s %s\n", level, file, line, timeUs,
|
|
pidtid, message);
|
|
|
|
fflush(outFile);
|
|
}
|
|
|
|
// ================================================================================================
|
|
void log_printf(LogLevel level, const char* file, int line, uint64_t* start, const char* format,
|
|
...) {
|
|
va_list ap;
|
|
char message[4096];
|
|
va_start(ap, format);
|
|
vsnprintf(message, sizeof(message), format, ap);
|
|
va_end(ap);
|
|
uint64_t timeUs = Os::timeNanos() / 1000ULL;
|
|
|
|
bool isStartLog = (start == 0 || *start == 0);
|
|
uint64_t duration = isStartLog ? 0 : (timeUs - *start);
|
|
|
|
// Try async logging first
|
|
if (getAsyncLogger().isEnabled()) {
|
|
getAsyncLogger().log(level, file, line, message, timeUs, duration, !isStartLog);
|
|
if (start != 0 && *start == 0) {
|
|
*start = timeUs;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Fall back to sync logging
|
|
char pidtid[64] = "";
|
|
if (AMD_LOG_LEVEL >= 4) {
|
|
size_t tid_hash = std::hash<std::thread::id>{}(std::this_thread::get_id());
|
|
snprintf(pidtid, sizeof(pidtid), "[pid:%u tid: 0x%05zx]",
|
|
Os::getProcessId(), tid_hash & 0xFFFFF);
|
|
}
|
|
|
|
truncate_log_file();
|
|
|
|
if (isStartLog) {
|
|
fprintf(outFile, ":%d:%-25s:%-4d: %010" PRIu64 " us: %s %s\n", level, file, line, timeUs,
|
|
pidtid, message);
|
|
} else {
|
|
fprintf(outFile, ":%d:%-25s:%-4d: %010" PRIu64 " us: %s %s: duration: %" PRIu64 " us\n", level,
|
|
file, line, timeUs, pidtid, message, duration);
|
|
}
|
|
fflush(outFile);
|
|
if (start != 0 && *start == 0) {
|
|
*start = timeUs;
|
|
}
|
|
}
|
|
|
|
// ================================================================================================
|
|
void EnableAsyncLogging(bool enable) {
|
|
getAsyncLogger().enable(enable);
|
|
}
|
|
|
|
// ================================================================================================
|
|
bool IsAsyncLoggingEnabled() {
|
|
return getAsyncLogger().isEnabled();
|
|
}
|
|
|
|
// ================================================================================================
|
|
void FlushAsyncLogs() {
|
|
getAsyncLogger().flush();
|
|
}
|
|
|
|
// ================================================================================================
|
|
void FlushAsyncLogsInCurrentThread() {
|
|
getAsyncLogger().flushInCurrentThread();
|
|
}
|
|
|
|
} // namespace amd
|