summaryrefslogtreecommitdiff
path: root/llama.cpp/common/log.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llama.cpp/common/log.cpp')
-rw-r--r--llama.cpp/common/log.cpp446
1 files changed, 446 insertions, 0 deletions
diff --git a/llama.cpp/common/log.cpp b/llama.cpp/common/log.cpp
new file mode 100644
index 0000000..b17d2b6
--- /dev/null
+++ b/llama.cpp/common/log.cpp
@@ -0,0 +1,446 @@
+#include "common.h"
+#include "log.h"
+
+#include <chrono>
+#include <condition_variable>
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <mutex>
+#include <sstream>
+#include <thread>
+#include <vector>
+
+#if defined(_WIN32)
+# include <io.h>
+# include <windows.h>
+# define isatty _isatty
+# define fileno _fileno
+#else
+# include <unistd.h>
+#endif // defined(_WIN32)
+
+int common_log_verbosity_thold = LOG_DEFAULT_LLAMA;
+
+void common_log_set_verbosity_thold(int verbosity) {
+ common_log_verbosity_thold = verbosity;
+}
+
+static int64_t t_us() {
+ return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
+}
+
+// colors
+enum common_log_col : int {
+ COMMON_LOG_COL_DEFAULT = 0,
+ COMMON_LOG_COL_BOLD,
+ COMMON_LOG_COL_RED,
+ COMMON_LOG_COL_GREEN,
+ COMMON_LOG_COL_YELLOW,
+ COMMON_LOG_COL_BLUE,
+ COMMON_LOG_COL_MAGENTA,
+ COMMON_LOG_COL_CYAN,
+ COMMON_LOG_COL_WHITE,
+};
+
+// disable colors by default
+static std::vector<const char *> g_col = {
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+};
+
+struct common_log_entry {
+ enum ggml_log_level level;
+
+ bool prefix;
+
+ int64_t timestamp;
+
+ std::vector<char> msg;
+
+ // signals the worker thread to stop
+ bool is_end;
+
+ void print(FILE * file = nullptr) const {
+ FILE * fcur = file;
+ if (!fcur) {
+ // stderr displays DBG messages only when their verbosity level is not higher than the threshold
+ // these messages will still be logged to a file
+ if (level == GGML_LOG_LEVEL_DEBUG && common_log_verbosity_thold < LOG_DEFAULT_DEBUG) {
+ return;
+ }
+
+ fcur = stdout;
+
+ if (level != GGML_LOG_LEVEL_NONE) {
+ fcur = stderr;
+ }
+ }
+
+ if (level != GGML_LOG_LEVEL_NONE && level != GGML_LOG_LEVEL_CONT && prefix) {
+ if (timestamp) {
+ // [M.s.ms.us]
+ fprintf(fcur, "%s%d.%02d.%03d.%03d%s ",
+ g_col[COMMON_LOG_COL_BLUE],
+ (int) (timestamp / 1000000 / 60),
+ (int) (timestamp / 1000000 % 60),
+ (int) (timestamp / 1000 % 1000),
+ (int) (timestamp % 1000),
+ g_col[COMMON_LOG_COL_DEFAULT]);
+ }
+
+ switch (level) {
+ case GGML_LOG_LEVEL_INFO: fprintf(fcur, "%sI %s", g_col[COMMON_LOG_COL_GREEN], g_col[COMMON_LOG_COL_DEFAULT]); break;
+ case GGML_LOG_LEVEL_WARN: fprintf(fcur, "%sW %s", g_col[COMMON_LOG_COL_MAGENTA], "" ); break;
+ case GGML_LOG_LEVEL_ERROR: fprintf(fcur, "%sE %s", g_col[COMMON_LOG_COL_RED], "" ); break;
+ case GGML_LOG_LEVEL_DEBUG: fprintf(fcur, "%sD %s", g_col[COMMON_LOG_COL_YELLOW], "" ); break;
+ default:
+ break;
+ }
+ }
+
+ fprintf(fcur, "%s", msg.data());
+
+ if (level == GGML_LOG_LEVEL_WARN || level == GGML_LOG_LEVEL_ERROR || level == GGML_LOG_LEVEL_DEBUG) {
+ fprintf(fcur, "%s", g_col[COMMON_LOG_COL_DEFAULT]);
+ }
+
+ fflush(fcur);
+ }
+};
+
+struct common_log {
+ // default capacity - will be expanded if needed
+ common_log() : common_log(256) {}
+
+ common_log(size_t capacity) {
+ file = nullptr;
+ prefix = false;
+ timestamps = false;
+ running = false;
+ t_start = t_us();
+
+ // initial message size - will be expanded if longer messages arrive
+ entries.resize(capacity);
+ for (auto & entry : entries) {
+ entry.msg.resize(256);
+ }
+
+ head = 0;
+ tail = 0;
+
+ resume();
+ }
+
+ ~common_log() {
+ pause();
+ if (file) {
+ fclose(file);
+ }
+ }
+
+private:
+ std::mutex mtx;
+ std::thread thrd;
+ std::condition_variable cv;
+
+ FILE * file;
+
+ bool prefix;
+ bool timestamps;
+ bool running;
+
+ int64_t t_start;
+
+ // ring buffer of entries
+ std::vector<common_log_entry> entries;
+ size_t head;
+ size_t tail;
+
+ // worker thread copies into this
+ common_log_entry cur;
+
+public:
+ void add(enum ggml_log_level level, const char * fmt, va_list args) {
+ std::lock_guard<std::mutex> lock(mtx);
+
+ if (!running) {
+ // discard messages while the worker thread is paused
+ return;
+ }
+
+ auto & entry = entries[tail];
+
+ {
+ // cannot use args twice, so make a copy in case we need to expand the buffer
+ va_list args_copy;
+ va_copy(args_copy, args);
+
+#if 1
+ const size_t n = vsnprintf(entry.msg.data(), entry.msg.size(), fmt, args);
+ if (n >= entry.msg.size()) {
+ entry.msg.resize(n + 1);
+ vsnprintf(entry.msg.data(), entry.msg.size(), fmt, args_copy);
+ }
+#else
+ // hack for bolding arguments
+
+ std::stringstream ss;
+ for (int i = 0; fmt[i] != 0; i++) {
+ if (fmt[i] == '%') {
+ ss << LOG_COL_BOLD;
+ while (fmt[i] != ' ' && fmt[i] != ')' && fmt[i] != ']' && fmt[i] != 0) ss << fmt[i++];
+ ss << LOG_COL_DEFAULT;
+ if (fmt[i] == 0) break;
+ }
+ ss << fmt[i];
+ }
+ const size_t n = vsnprintf(entry.msg.data(), entry.msg.size(), ss.str().c_str(), args);
+ if (n >= entry.msg.size()) {
+ entry.msg.resize(n + 1);
+ vsnprintf(entry.msg.data(), entry.msg.size(), ss.str().c_str(), args_copy);
+ }
+#endif
+ va_end(args_copy);
+ }
+
+ entry.level = level;
+ entry.prefix = prefix;
+ entry.timestamp = 0;
+ if (timestamps) {
+ entry.timestamp = t_us() - t_start;
+ }
+ entry.is_end = false;
+
+ tail = (tail + 1) % entries.size();
+ if (tail == head) {
+ // expand the buffer
+ std::vector<common_log_entry> new_entries(2*entries.size());
+
+ size_t new_tail = 0;
+
+ do {
+ new_entries[new_tail] = std::move(entries[head]);
+
+ head = (head + 1) % entries.size();
+ new_tail = (new_tail + 1);
+ } while (head != tail);
+
+ head = 0;
+ tail = new_tail;
+
+ for (size_t i = tail; i < new_entries.size(); i++) {
+ new_entries[i].msg.resize(256);
+ }
+
+ entries = std::move(new_entries);
+ }
+
+ cv.notify_one();
+ }
+
+ void resume() {
+ std::lock_guard<std::mutex> lock(mtx);
+
+ if (running) {
+ return;
+ }
+
+ running = true;
+
+ thrd = std::thread([this]() {
+ while (true) {
+ {
+ std::unique_lock<std::mutex> lock(mtx);
+ cv.wait(lock, [this]() { return head != tail; });
+
+ cur = entries[head];
+
+ head = (head + 1) % entries.size();
+ }
+
+ if (cur.is_end) {
+ break;
+ }
+
+ cur.print(); // stdout and stderr
+
+ if (file) {
+ cur.print(file);
+ }
+ }
+ });
+ }
+
+ void pause() {
+ {
+ std::lock_guard<std::mutex> lock(mtx);
+
+ if (!running) {
+ return;
+ }
+
+ running = false;
+
+ // push an entry to signal the worker thread to stop
+ {
+ auto & entry = entries[tail];
+ entry.is_end = true;
+
+ tail = (tail + 1) % entries.size();
+ }
+
+ cv.notify_one();
+ }
+
+ thrd.join();
+ }
+
+ void set_file(const char * path) {
+ pause();
+
+ if (file) {
+ fclose(file);
+ }
+
+ if (path) {
+ file = fopen(path, "w");
+ } else {
+ file = nullptr;
+ }
+
+ resume();
+ }
+
+ void set_colors(bool colors) {
+ pause();
+
+ if (colors) {
+ g_col[COMMON_LOG_COL_DEFAULT] = LOG_COL_DEFAULT;
+ g_col[COMMON_LOG_COL_BOLD] = LOG_COL_BOLD;
+ g_col[COMMON_LOG_COL_RED] = LOG_COL_RED;
+ g_col[COMMON_LOG_COL_GREEN] = LOG_COL_GREEN;
+ g_col[COMMON_LOG_COL_YELLOW] = LOG_COL_YELLOW;
+ g_col[COMMON_LOG_COL_BLUE] = LOG_COL_BLUE;
+ g_col[COMMON_LOG_COL_MAGENTA] = LOG_COL_MAGENTA;
+ g_col[COMMON_LOG_COL_CYAN] = LOG_COL_CYAN;
+ g_col[COMMON_LOG_COL_WHITE] = LOG_COL_WHITE;
+ } else {
+ for (size_t i = 0; i < g_col.size(); i++) {
+ g_col[i] = "";
+ }
+ }
+
+ resume();
+ }
+
+ void set_prefix(bool prefix) {
+ std::lock_guard<std::mutex> lock(mtx);
+
+ this->prefix = prefix;
+ }
+
+ void set_timestamps(bool timestamps) {
+ std::lock_guard<std::mutex> lock(mtx);
+
+ this->timestamps = timestamps;
+ }
+};
+
+//
+// public API
+//
+
+struct common_log * common_log_init() {
+ return new common_log;
+}
+
+struct common_log * common_log_main() {
+ static struct common_log log;
+ static std::once_flag init_flag;
+ std::call_once(init_flag, [&]() {
+ // Set default to auto-detect colors
+ log.set_colors(tty_can_use_colors());
+ });
+
+ return &log;
+}
+
+void common_log_pause(struct common_log * log) {
+ log->pause();
+}
+
+void common_log_resume(struct common_log * log) {
+ log->resume();
+}
+
+void common_log_free(struct common_log * log) {
+ delete log;
+}
+
+void common_log_add(struct common_log * log, enum ggml_log_level level, const char * fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ log->add(level, fmt, args);
+ va_end(args);
+}
+
+void common_log_set_file(struct common_log * log, const char * file) {
+ log->set_file(file);
+}
+
+void common_log_set_colors(struct common_log * log, log_colors colors) {
+ if (colors == LOG_COLORS_AUTO) {
+ log->set_colors(tty_can_use_colors());
+ return;
+ }
+
+ if (colors == LOG_COLORS_DISABLED) {
+ log->set_colors(false);
+ return;
+ }
+
+ GGML_ASSERT(colors == LOG_COLORS_ENABLED);
+ log->set_colors(true);
+}
+
+void common_log_set_prefix(struct common_log * log, bool prefix) {
+ log->set_prefix(prefix);
+}
+
+void common_log_set_timestamps(struct common_log * log, bool timestamps) {
+ log->set_timestamps(timestamps);
+}
+
+void common_log_flush(struct common_log * log) {
+ log->pause();
+ log->resume();
+}
+
+static int common_get_verbosity(enum ggml_log_level level) {
+ switch (level) {
+ case GGML_LOG_LEVEL_DEBUG: return LOG_LEVEL_DEBUG;
+ case GGML_LOG_LEVEL_INFO: return LOG_LEVEL_INFO;
+ case GGML_LOG_LEVEL_WARN: return LOG_LEVEL_WARN;
+ case GGML_LOG_LEVEL_ERROR: return LOG_LEVEL_ERROR;
+ case GGML_LOG_LEVEL_CONT: return LOG_LEVEL_INFO; // same as INFO
+ case GGML_LOG_LEVEL_NONE:
+ default:
+ return LOG_LEVEL_OUTPUT;
+ }
+}
+
+void common_log_default_callback(enum ggml_log_level level, const char * text, void * /*user_data*/) {
+ auto verbosity = common_get_verbosity(level);
+ if (verbosity <= common_log_verbosity_thold) {
+ common_log_add(common_log_main(), level, "%s", text);
+ }
+}