Add logging
This commit is contained in:
parent
f1c2dc7f63
commit
3bf47f3a5c
11
Makefile
11
Makefile
|
@ -12,19 +12,20 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
#CXX = g++
|
#CXX = g++
|
||||||
#CXX = clang++
|
CXX = clang++
|
||||||
|
|
||||||
EXE = logmeow
|
EXE = logmeow
|
||||||
IMGUI_DIR = imgui
|
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)/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
|
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))))
|
OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES))))
|
||||||
UNAME_S := $(shell uname -s)
|
UNAME_S := $(shell uname -s)
|
||||||
LINUX_GL_LIBS = -lGL
|
LINUX_GL_LIBS = -lGL
|
||||||
|
|
||||||
CXXFLAGS = -std=c++11 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/misc/cpp -I$(IMGUI_DIR)/backends
|
CXXFLAGS = -std=c++14 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/misc/cpp -I$(IMGUI_DIR)/backends
|
||||||
CXXFLAGS += -g -Wall -Wformat
|
# 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 =
|
LIBS =
|
||||||
|
|
||||||
##---------------------------------------------------------------------
|
##---------------------------------------------------------------------
|
||||||
|
@ -85,7 +86,7 @@ endif
|
||||||
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
$(EXE): $(OBJS)
|
$(EXE): $(OBJS)
|
||||||
$(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS)
|
$(CXX) -o $@ $^ $(CXXFLAGS) -fuse-ld=lld $(LIBS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(EXE) $(OBJS)
|
rm -f $(EXE) $(OBJS)
|
||||||
|
|
27
config.cpp
27
config.cpp
|
@ -6,6 +6,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
static FILE* fopen_or_raise(const char* path, const char* mode, bool ignore_enoent) {
|
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)) {
|
if (file || (ignore_enoent && errno == ENOENT)) {
|
||||||
return file;
|
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) {
|
static void fclose_and_log(FILE* file) {
|
||||||
if (!fclose(file)) {
|
if (!fclose(file)) {
|
||||||
return;
|
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) {
|
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
|
// tries to create a directory with an empty string if it's just "/" but who cares
|
||||||
void create_config_folders_if_necessary() {
|
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;
|
size_t pos = 0;
|
||||||
|
|
||||||
while (pos != std::string::npos) {
|
while (pos != std::string::npos) {
|
||||||
pos = path.find('/', pos);
|
pos = full_path.find('/', pos);
|
||||||
if (pos != std::string::npos) {
|
if (pos != std::string::npos) {
|
||||||
pos++;
|
pos++;
|
||||||
}
|
}
|
||||||
if (!mkdir(path.substr(0, pos).c_str(), 0600)) {
|
path = full_path.substr(0, pos);
|
||||||
|
if (!mkdir(path.c_str(), 0600)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (errno == EEXIST) {
|
if (errno == EEXIST) {
|
||||||
continue;
|
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) {
|
if (errsv == ENOMEM) {
|
||||||
throw std::bad_alloc();
|
throw std::bad_alloc();
|
||||||
} else if (errsv != 0) {
|
} 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 {
|
} else {
|
||||||
break;
|
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"
|
"# # a comment, only available at the start of a line\n"
|
||||||
"# (an empty line, no whitespace)\n"
|
"# (an empty line, no whitespace)\n"
|
||||||
"# key=value pairs, no spaces around the delimiter, and no unknown keys\n\n", file)) {
|
"# 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)) {
|
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)) {
|
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())) {
|
if (!rename(tmp_config_file_path.c_str(), config_file_path.c_str())) {
|
||||||
return;
|
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) + ')');
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,57 @@
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <imgui_stdlib.h>
|
#include <imgui_stdlib.h>
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
static inline void write_config_and_update_structures(const Config& config) {
|
static inline void write_config_and_update_structures(const Config& config) {
|
||||||
try {
|
try {
|
||||||
write_config(config);
|
write_config(config);
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
// TODO log() when logging exists
|
log("Failed to write config", e);
|
||||||
throw;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline void settings_window(Config& config, float* config_write_timer, bool* show_settings_window) {
|
static inline void settings_window(Config& config, float* config_write_timer, bool* show_settings_window) {
|
||||||
if (!ImGui::Begin("Settings", show_settings_window)) {
|
if (!ImGui::Begin("Settings", show_settings_window)) {
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// TODO actually have process control
|
// 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)) {
|
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;
|
*config_write_timer = *config_write_timer > 0.0f ? *config_write_timer : 5.0f;
|
||||||
}
|
}
|
||||||
ImGui::End();
|
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)) {
|
if (!ImGui::Begin("LogMeow", exit_requested_rev)) {
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
return;
|
return;
|
||||||
|
@ -33,6 +59,31 @@ static inline void main_window(Config& active_config, bool* show_settings_window
|
||||||
if (ImGui::Button("Settings")) {
|
if (ImGui::Button("Settings")) {
|
||||||
*show_settings_window = true;
|
*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();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,25 +104,13 @@ static inline void exit_modal_if_necessary(bool* run_event_loop) {
|
||||||
ImGui::EndPopup();
|
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) {
|
void event_loop(Config& config, float* config_write_timer, bool* run_event_loop) {
|
||||||
static bool show_settings_window = false;
|
static bool show_settings_window = false;
|
||||||
|
static bool show_logs_window = false;
|
||||||
static bool exit_requested_rev = true;
|
static bool exit_requested_rev = true;
|
||||||
|
|
||||||
|
debug_window();
|
||||||
|
|
||||||
if (show_settings_window) {
|
if (show_settings_window) {
|
||||||
settings_window(config, config_write_timer, &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) {
|
if (!exit_requested_rev) {
|
||||||
ImGui::OpenPopup("Exit?");
|
ImGui::OpenPopup("Exit?");
|
||||||
exit_requested_rev = true;
|
exit_requested_rev = true;
|
||||||
}
|
}
|
||||||
exit_modal_if_necessary(run_event_loop);
|
exit_modal_if_necessary(run_event_loop);
|
||||||
main_window(config, &show_settings_window, &exit_requested_rev);
|
main_window(config, &show_settings_window, &show_logs_window, &exit_requested_rev);
|
||||||
debug_window();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
#include <ctime>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <exception>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
#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();
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
extern std::string log_entries;
|
||||||
|
|
||||||
|
void log(std::string action, const std::exception& e);
|
||||||
|
std::string quote(const std::string& str);
|
6
main.cpp
6
main.cpp
|
@ -1,4 +1,5 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <locale.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
@ -14,8 +15,9 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "event_loop.h"
|
#include "event_loop.h"
|
||||||
|
|
||||||
int main(int, char**)
|
int main(int, char**) {
|
||||||
{
|
setlocale(LC_ALL, "");
|
||||||
|
|
||||||
Config config;
|
Config config;
|
||||||
try {
|
try {
|
||||||
config = load_config();
|
config = load_config();
|
||||||
|
|
Loading…
Reference in New Issue