From 3bf47f3a5c2103e57afc844fb7f9a21769fbd99d Mon Sep 17 00:00:00 2001 From: blankie Date: Thu, 5 Jan 2023 17:27:14 +0700 Subject: [PATCH] Add logging --- Makefile | 11 ++++--- config.cpp | 27 +++++++++------- event_loop.cpp | 84 +++++++++++++++++++++++++++++++++++++------------- log.cpp | 63 +++++++++++++++++++++++++++++++++++++ log.h | 10 ++++++ main.cpp | 6 ++-- 6 files changed, 162 insertions(+), 39 deletions(-) create mode 100644 log.cpp create mode 100644 log.h diff --git a/Makefile b/Makefile index 4569556..29abd24 100644 --- a/Makefile +++ b/Makefile @@ -12,19 +12,20 @@ # #CXX = g++ -#CXX = clang++ +CXX = clang++ EXE = logmeow IMGUI_DIR = imgui -SOURCES = main.cpp config.cpp event_loop.cpp +SOURCES = main.cpp log.cpp config.cpp event_loop.cpp SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp SOURCES += $(IMGUI_DIR)/misc/cpp/imgui_stdlib.cpp $(IMGUI_DIR)/backends/imgui_impl_sdl.cpp $(IMGUI_DIR)/backends/imgui_impl_opengl3.cpp OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) UNAME_S := $(shell uname -s) LINUX_GL_LIBS = -lGL -CXXFLAGS = -std=c++11 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/misc/cpp -I$(IMGUI_DIR)/backends -CXXFLAGS += -g -Wall -Wformat +CXXFLAGS = -std=c++14 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/misc/cpp -I$(IMGUI_DIR)/backends +# https://t.me/NightShadowsHangout/670691 +CXXFLAGS += -g -Werror -Wall -Wextra -Wshadow -Wpedantic -Wno-gnu-anonymous-struct -fno-rtti -fPIC -Wconversion -Wno-unused-parameter -Wimplicit-fallthrough LIBS = ##--------------------------------------------------------------------- @@ -85,7 +86,7 @@ endif $(CXX) $(CXXFLAGS) -c -o $@ $< $(EXE): $(OBJS) - $(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS) + $(CXX) -o $@ $^ $(CXXFLAGS) -fuse-ld=lld $(LIBS) clean: rm -f $(EXE) $(OBJS) diff --git a/config.cpp b/config.cpp index 3f40350..7a27ba1 100644 --- a/config.cpp +++ b/config.cpp @@ -6,6 +6,7 @@ #include #include +#include "log.h" #include "config.h" static FILE* fopen_or_raise(const char* path, const char* mode, bool ignore_enoent) { @@ -13,14 +14,15 @@ static FILE* fopen_or_raise(const char* path, const char* mode, bool ignore_enoe if (file || (ignore_enoent && errno == ENOENT)) { return file; } - throw std::system_error(errno, std::generic_category(), "fopen()"); + throw std::system_error(errno, std::generic_category(), std::string("fopen(") + quote(path) + ')'); } static void fclose_and_log(FILE* file) { if (!fclose(file)) { return; } - // TODO log() when logging exists + std::system_error e = std::system_error(errno, std::generic_category(), "fclose()"); + log("Failed to close a file", std::move(e)); } static bool write(const std::string& ptr, FILE* file) { @@ -52,21 +54,23 @@ std::string get_config_file_path() { // tries to create a directory with an empty string if it's just "/" but who cares void create_config_folders_if_necessary() { - std::string path = get_config_folder(); + std::string full_path = get_config_folder(); + std::string path; size_t pos = 0; while (pos != std::string::npos) { - pos = path.find('/', pos); + pos = full_path.find('/', pos); if (pos != std::string::npos) { pos++; } - if (!mkdir(path.substr(0, pos).c_str(), 0600)) { + path = full_path.substr(0, pos); + if (!mkdir(path.c_str(), 0600)) { continue; } if (errno == EEXIST) { continue; } - throw std::system_error(errno, std::generic_category(), "mkdir()"); + throw std::system_error(errno, std::generic_category(), std::string("mkdir(") + quote(path) + ')'); } } @@ -87,7 +91,7 @@ static inline Config load_config(FILE* file) { if (errsv == ENOMEM) { throw std::bad_alloc(); } else if (errsv != 0) { - throw std::system_error(errsv, std::generic_category(), "reading config line"); + throw std::system_error(errsv, std::generic_category(), "getline()"); } else { break; } @@ -129,13 +133,13 @@ static inline void write_config(FILE* file, const Config& config) { "# # a comment, only available at the start of a line\n" "# (an empty line, no whitespace)\n" "# key=value pairs, no spaces around the delimiter, and no unknown keys\n\n", file)) { - throw std::runtime_error("failed to write info comment"); + throw std::runtime_error("Failed to write info comment"); } if (!write("logcat_command=", file)) { - throw std::runtime_error("failed to write logcat command key"); + throw std::runtime_error("Failed to write logcat command key"); } if (!write(config.logcat_command, file)) { - throw std::runtime_error("failed to write logcat command value"); + throw std::runtime_error("Failed to write logcat command value"); } } @@ -150,5 +154,6 @@ void write_config(const Config& config) { if (!rename(tmp_config_file_path.c_str(), config_file_path.c_str())) { return; } - throw std::system_error(errno, std::generic_category(), "rename()"); + throw std::system_error(errno, std::generic_category(), + std::string("rename(") + quote(tmp_config_file_path) + ", " + quote(config_file_path) + ')'); } diff --git a/event_loop.cpp b/event_loop.cpp index aceb9b8..b42ee9e 100644 --- a/event_loop.cpp +++ b/event_loop.cpp @@ -1,31 +1,57 @@ #include #include +#include "log.h" #include "config.h" static inline void write_config_and_update_structures(const Config& config) { try { write_config(config); } catch (const std::exception& e) { - // TODO log() when logging exists - throw; + log("Failed to write config", e); + return; } } + static inline void settings_window(Config& config, float* config_write_timer, bool* show_settings_window) { if (!ImGui::Begin("Settings", show_settings_window)) { ImGui::End(); return; } // TODO actually have process control - ImGui::Text("Logcat command is only modifiable when logcat is stopped"); + ImGui::Text("Logcat command only takes effect when logcat is not running"); if (ImGui::InputTextWithHint("Logcat command", "adb logcat -Dv 'threadtime UTC epoch usec'", &config.logcat_command)) { *config_write_timer = *config_write_timer > 0.0f ? *config_write_timer : 5.0f; } ImGui::End(); } -static inline void main_window(Config& active_config, bool* show_settings_window, bool* exit_requested_rev) { +static inline void logs_window(bool* show_logs_window) { + if (!ImGui::Begin("LogMeow Logs", show_logs_window)) { + ImGui::End(); + return; + } + + if (ImGui::Button("Clear")) { + log_entries.clear(); + } + ImGui::SameLine(); + if (ImGui::Button("Copy")) { + ImGui::SetClipboardText(log_entries.c_str()); + } + + // copied from imgui/imgui_demo.cpp: [SECTION] Example App: Debug Console / ShowExampleAppConsole() + + ImGui::Separator(); + if (ImGui::BeginChild("ScrollingRegion", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar)) { + ImGui::TextUnformatted(log_entries.data(), &log_entries[log_entries.size()]); + } + ImGui::EndChild(); + ImGui::End(); +} + +static inline void main_window(Config& active_config, bool* show_settings_window, bool* show_logs_window, bool* exit_requested_rev) { if (!ImGui::Begin("LogMeow", exit_requested_rev)) { ImGui::End(); return; @@ -33,6 +59,31 @@ static inline void main_window(Config& active_config, bool* show_settings_window if (ImGui::Button("Settings")) { *show_settings_window = true; } + ImGui::SameLine(); + if (ImGui::Button("Logs")) { + *show_logs_window = true; + } + ImGui::End(); +} + +static inline void debug_window() { + static bool show_demo_window = false; + static size_t add_log_entry_presses = 1; + + if (show_demo_window) { + ImGui::ShowDemoWindow(&show_demo_window); + } + if (!ImGui::Begin("LogMeow Debug")) { + ImGui::End(); + return; + } + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); + ImGui::Checkbox("Show Dear ImGui Demo Window", &show_demo_window); + ImGui::Separator(); + if (ImGui::Button("Add Log Entry")) { + std::exception e; + log(std::string("Add log entry button pressed ") + std::to_string(add_log_entry_presses++) + " time(s)", e); + } ImGui::End(); } @@ -53,25 +104,13 @@ static inline void exit_modal_if_necessary(bool* run_event_loop) { ImGui::EndPopup(); } -static inline void debug_window() { - static bool show_demo_window = false; - if (show_demo_window) { - ImGui::ShowDemoWindow(&show_demo_window); - } - - if (!ImGui::Begin("LogMeow Debug")) { - ImGui::End(); - return; - } - ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); - ImGui::Checkbox("Show Dear ImGui Demo Window", &show_demo_window); - ImGui::End(); -} - void event_loop(Config& config, float* config_write_timer, bool* run_event_loop) { static bool show_settings_window = false; + static bool show_logs_window = false; static bool exit_requested_rev = true; + debug_window(); + if (show_settings_window) { settings_window(config, config_write_timer, &show_settings_window); } @@ -82,11 +121,14 @@ void event_loop(Config& config, float* config_write_timer, bool* run_event_loop) } } + if (show_logs_window) { + logs_window(&show_logs_window); + } + if (!exit_requested_rev) { ImGui::OpenPopup("Exit?"); exit_requested_rev = true; } exit_modal_if_necessary(run_event_loop); - main_window(config, &show_settings_window, &exit_requested_rev); - debug_window(); + main_window(config, &show_settings_window, &show_logs_window, &exit_requested_rev); } diff --git a/log.cpp b/log.cpp new file mode 100644 index 0000000..9dcfc58 --- /dev/null +++ b/log.cpp @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" + +static time_t current_time() { + static bool no_time_warned = false; + struct timespec tp; + + if (!clock_gettime(CLOCK_REALTIME, &tp)) { + return tp.tv_sec; + } + + if (no_time_warned) { + return 0; + } + no_time_warned = true; + + std::system_error e = std::system_error(errno, std::generic_category(), "clock_gettime()"); + log("Failed to get current time", std::move(e)); + return 0; +} + + +std::string log_entries; + +void log(std::string entry, time_t time) { + size_t last_newline_pos = 0, newline_pos; + std::string line; + char time_as_str[128] = {0}; + + strftime(time_as_str, 127 * sizeof(char), "%c", localtime(&time)); + do { + newline_pos = entry.find('\n', last_newline_pos); + line = '['; + line += time_as_str; + line += "] "; + line += entry.substr(last_newline_pos, newline_pos); + + printf("%s\n", line.c_str()); + if (!log_entries.empty()) { + log_entries += '\n'; + } + log_entries += std::move(line); + last_newline_pos = newline_pos + 1; + } while (last_newline_pos); +} + +void log(std::string action, const std::exception& e) { + log(action + ": " + e.what(), current_time()); +} + +std::string quote(const std::string& str) { + std::stringstream ss; + ss << std::quoted(str); + return ss.str(); +} diff --git a/log.h b/log.h new file mode 100644 index 0000000..8ac289c --- /dev/null +++ b/log.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include +#include + +extern std::string log_entries; + +void log(std::string action, const std::exception& e); +std::string quote(const std::string& str); diff --git a/main.cpp b/main.cpp index 900ab14..b205b1f 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -14,8 +15,9 @@ #include "config.h" #include "event_loop.h" -int main(int, char**) -{ +int main(int, char**) { + setlocale(LC_ALL, ""); + Config config; try { config = load_config();