diff --git a/CMakeLists.txt b/CMakeLists.txt index 6391e01..db1a3d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,7 @@ add_library(hiload SHARED src/files.c src/memory.c src/string/string.c + src/logger/logger.c src/filewatcher/filewatcher.c src/filewatcher/filewatch.c diff --git a/include/hiload/hiload.h b/include/hiload/hiload.h index 9b8475f..df919dd 100644 --- a/include/hiload/hiload.h +++ b/include/hiload/hiload.h @@ -1,6 +1,7 @@ #ifndef HILOAD_H_ #define HILOAD_H_ +#include "types.h" #ifdef __cplusplus extern "C" { #endif @@ -8,14 +9,6 @@ extern "C" { // Allows using -fvisibility=hidden by default, decreasing visible symbols #pragma GCC visibility push(default) -// Return codes for reload_module -typedef enum { - HI_RELOAD_SUCCESS = 0, - HI_RELOAD_NOT_FOUND, - HI_RELOAD_CLOSE_ERROR, - HI_RELOAD_OPEN_ERROR -} ReloadResult; - /** * Initialiez the module. Must be called before anything else */ @@ -34,8 +27,12 @@ void hi_print_loaded_modules(void); /** * Reload shared library if it has changed since last reload or init was called */ -ReloadResult hi_reload_solib(const char *module_name); +HiloadResult hi_reload_solib(const char *module_name); +/** + * Reload all watched modules + */ +HiloadResult hi_reload(); #pragma GCC visibility pop diff --git a/src/filewatcher/filewatcher.c b/src/filewatcher/filewatcher.c index 9999009..16c82e0 100644 --- a/src/filewatcher/filewatcher.c +++ b/src/filewatcher/filewatcher.c @@ -11,6 +11,7 @@ #include #include "array/array.h" +#include "array/sc_array.h" #include "common.h" #include "filewatch.h" #include "filewatch_context.h" @@ -85,8 +86,8 @@ static hiFileEvent file_event_create(const struct inotify_event *inevent, size_t fullpath_size = parent_name_size + event_path_size; if (fullpath_size > BUFSIZE - 1) { sc_log_error("Pathname length over %u, please compile with smaller " - "paths. Event discarded for %s\n.", BUFSIZE - 1, - inevent->name); + "paths. Event discarded for %s\n.", + BUFSIZE - 1, inevent->name); return NULL_EVENT; } #undef BUFSIZE @@ -124,7 +125,8 @@ static hiFileEvent file_event_create(const struct inotify_event *inevent, return NULL_EVENT; } - event.pathname = watched_file; + // Use params path pointer, because that's what user has control over. + event.pathname = params->path; // Event matched and we're watching for it return event; @@ -137,14 +139,16 @@ static hiFileEvent file_event_create(const struct inotify_event *inevent, return NULL_EVENT; } -hiFileEvent hi_file_event_pop(hiFileWatcher *ctx) { - mtx_lock(&ctx->mutex); - hiFileEvent *last = &sc_array_last(&ctx->events); - hiFileEvent e = *last; +hiFileEvent hi_file_event_pop(hiFileWatcher *fw) { + mtx_lock(&fw->mutex); - sc_array_del_last(&ctx->events); + hiFileEvent e = NULL_EVENT; + if (sc_array_size(&fw->events) > 0) { + e = sc_array_last(&fw->events); + sc_array_del_last(&fw->events); + } - mtx_unlock(&ctx->mutex); + mtx_unlock(&fw->mutex); return e; } diff --git a/src/filewatcher/filewatcher.h b/src/filewatcher/filewatcher.h index 2bc961a..d76cdce 100644 --- a/src/filewatcher/filewatcher.h +++ b/src/filewatcher/filewatcher.h @@ -6,9 +6,13 @@ /** * File event watcher using inotify. + * + * Paths are stored as absolute, and this doesn't handle symlinks in watched + * files. So if you have e.g. a symlink 'my-proj' to your project folder in + * '/workspace/code/my-proj' paths might exist from either side and they are + * not interpreted as equal. */ - /** * File Event produced by FileWatcher. * diff --git a/src/hiload.c b/src/hiload.c index 2ac1331..7b2459f 100644 --- a/src/hiload.c +++ b/src/hiload.c @@ -1,9 +1,8 @@ #include "hiload/hiload.h" -#include "array/sc_array.h" +#include "array/array.h" #include "filewatcher/filewatcher.h" #include "logger/logger.h" -#include "logger/sc_log.h" #include "memory.h" #include "string/string.h" #include "symbols.h" @@ -25,6 +24,7 @@ typedef struct { struct sc_array_ptr handles; // Array of library handles struct sc_array_uptr addresses; // Array of library base addresses struct sc_array_syms symbols; // Symbol info for modules + bool dirty[256]; // Flag for when module needs to be changed size_t count; // Number of modules } ModuleInfos; @@ -47,7 +47,7 @@ static int gather_module_infos_callback(struct dl_phdr_info *info, size_t size, const char *modname = info->dlpi_name; sc_array_add(&mod_infos->names, strdup(modname)); - sc_log_info("Processing: '%s'\n", info->dlpi_name); + log_info("Processing: '%s'\n", info->dlpi_name); // Try to get the handle void *handle = dlopen(info->dlpi_name, RTLD_LAZY | RTLD_NOLOAD); @@ -56,11 +56,11 @@ static int gather_module_infos_callback(struct dl_phdr_info *info, size_t size, sc_array_add(&mod_infos->handles, handle); sc_array_add(&mod_infos->addresses, info->dlpi_addr); - sc_log_debug(" size: %u\n", size); - sc_log_debug(" handle: %p\n", sc_array_last(&mod_infos->handles)); - sc_log_debug(" dlpi_addr: %p\n", info->dlpi_addr); - sc_log_debug(" dlpi_tls_modid: %zu\n", info->dlpi_tls_modid); - sc_log_debug(" dlpi_tls_data: %p\n", info->dlpi_tls_data); + log_debug_v(" size: %u\n", size); + log_debug_v(" handle: %p\n", sc_array_last(&mod_infos->handles)); + log_debug_v(" dlpi_addr: %p\n", info->dlpi_addr); + log_debug_v(" dlpi_tls_modid: %zu\n", info->dlpi_tls_modid); + log_debug_v(" dlpi_tls_data: %p\n", info->dlpi_tls_data); Dl_info dl_info = {0}; if (dladdr((void *)sc_array_last(&mod_infos->addresses), &dl_info)) { @@ -78,15 +78,15 @@ static int gather_module_infos_callback(struct dl_phdr_info *info, size_t size, } if (!HILOADRES(hi_file_watcher_add(context.filewatcher, filename, HI_FILE_ALL_EVENTS))) { - sc_log_error("Failed to set filewatcher for: '%s'\n", filename); + log_error("Failed to set filewatcher for: '%s'\n", filename); } else { - sc_log_info("Watching file: %s\n", filename); + log_info("Watching file: %s\n", filename); } } } } else { - sc_log_error("Couldn't load dlinfo for %s: %s\n", - (sc_array_last(&mod_infos->names)), dlerror()); + log_error("Couldn't load dlinfo for %s: %s\n", + (sc_array_last(&mod_infos->names)), dlerror()); } return 0; // Continue iteration @@ -131,137 +131,60 @@ static ModuleInfos *gather_module_infos(void) { return result; } -static ReloadResult reload_solib(ModuleInfos *modules, const char *filename, - void **updated_handle) { - if (!modules || !filename) { - return HI_RELOAD_NOT_FOUND; - } +i32 get_module_index(const char *path, HiloadContext *ctx) { - // Find the module by filename - int found = 0; - size_t index = 0; - - for (size_t i = 0; i < modules->count; i++) { - // Check if this is the module we're looking for - const char *pathname = sc_array_at(&modules->names, i); - - if (hi_path_has_filename(pathname, filename)) { - found = 1; - index = i; - break; + for (size_t i = 0; i < ctx->loaded_modules->count; i++) { + const char *name = sc_array_at(&ctx->loaded_modules->names, i); + if (strcmp(name, path) == 0) { + return i; } } - - if (!found) { - return HI_RELOAD_NOT_FOUND; - } - - // Save the full path - const char *fullpath = sc_array_at(&modules->names, index); - if (!fullpath) { - return HI_RELOAD_OPEN_ERROR; - } - - // Close the old handle - void *handle = sc_array_at(&modules->handles, index); - if (handle) { - if (dlclose(handle) != 0) { - return HI_RELOAD_CLOSE_ERROR; - } - } - - // Open the module with RTLD_NOW - void *new_handle = dlopen(fullpath, RTLD_LAZY); - if (!new_handle) { - sc_log_error("Error reloading module: %s\n", dlerror()); - return HI_RELOAD_OPEN_ERROR; - } - - if (new_handle != handle) { - sc_log_info("%s: changed\n", fullpath); - } - - // Update the handle in our structure - modules->handles.elems[index] = new_handle; - - // If the caller wants the new handle, provide it - if (updated_handle) { - *updated_handle = new_handle; - } - - return HI_RELOAD_SUCCESS; + return -1; } -ReloadResult hi_reload_solib(const char *module_name) { +HiloadResult hi_reload() { - void *new_handle = NULL; - ReloadResult result = - reload_solib(context.loaded_modules, module_name, &new_handle); + hiFileEvent event = hi_file_event_pop(context.filewatcher); + while (event.type != HI_FILE_NONE) { + log_debug("Event pop: %s – %s\n", event.pathname, + hi_file_watch_type_to_str(event.type)); - return result; -} - -void hi_print_loaded_modules(void) { - - sc_log_info("Loaded module info:\n"); - sc_log_info("-------------------\n"); - - const ModuleInfos *mod_infos = context.loaded_modules; - if (!mod_infos) { - sc_log_error("No module information available.\n"); - return; - } - - sc_log_info("Found %zu loaded modules:\n", mod_infos->count); - - for (size_t i = 0; i < mod_infos->count; i++) { - - sc_log_info("'%s'\n", sc_array_at(&mod_infos->names, i)); - sc_log_info(" handle: %p\n", sc_array_at(&mod_infos->handles, i)); - sc_log_info(" address: %p\n", sc_array_at(&mod_infos->addresses, i)); - - // If we have loaded symbols, print those - if (sc_array_size(&mod_infos->symbols) > 0) { - const SymbolInfos *symbols = &sc_array_at(&mod_infos->symbols, i); - if (symbols) { - for (size_t j = 0; j < sc_array_size(&symbols->names); j++) { - const void *addr = sc_array_at(&symbols->addresses, j); - const char *name = sc_array_at(&symbols->names, j); - sc_log_info(" %p: %s\n", addr, name); - } - } + i32 index = get_module_index(event.pathname, &context); + if (index < 0) { + log_warn("Watched module: %s not found.\n", event.pathname); } - - sc_log_info("\n"); + event = hi_file_event_pop(context.filewatcher); } + + return HILOAD_OK; } int hi_init(unsigned n, const char **enabled_modules) { - if (sc_log_init() != 0) { + if (log_init() != 0) { fprintf(stderr, "Failed to init logger.\n"); return 1; } - sc_log_set_level("DEBUG"); - sc_log_set_thread_name("Main"); + log_set_level("DEBUG"); + log_set_thread_name("Main"); // Start the filewatcher context.filewatcher = hi_file_watcher_create(); for (unsigned i = 0; i < n; i++) { const char *module_name = enabled_modules[i]; - sc_log_info("Enabling module: '%s'\n", module_name); + log_info("Enabling module: '%s'\n", module_name); sc_array_add(&context.enabled_modules, module_name); } if (read_memory_maps_self(&context.memory_regions) != HILOAD_OK) { - sc_log_error("Could not populate program memory maps.\n"); + log_error("Could not populate program memory maps.\n"); return HILOAD_FAIL; } ModuleInfos *infos = gather_module_infos(); if (!infos) { - sc_log_error("Failed to gather module infos.\n"); + log_error("Failed to gather module infos.\n"); return 1; } @@ -271,14 +194,12 @@ int hi_init(unsigned n, const char **enabled_modules) { } context.loaded_modules = infos; - hi_print_loaded_modules(); - return 0; } void hi_deinit() { module_infos_free(context.loaded_modules); hi_file_watcher_destroy(context.filewatcher); - sc_log_term(); + log_term(); memset(&context, 0, sizeof(context)); } diff --git a/src/logger/logger.c b/src/logger/logger.c new file mode 100644 index 0000000..2f90021 --- /dev/null +++ b/src/logger/logger.c @@ -0,0 +1,13 @@ +#include "logger.h" + +#include + +_Atomic bool hiload_verbose_log = false; + +void log_set_verbose(bool value) { + atomic_store_explicit(&hiload_verbose_log, value, memory_order_relaxed); +} + +bool log_get_verbose() { + return atomic_load_explicit(&hiload_verbose_log, memory_order_relaxed); +} diff --git a/src/logger/logger.h b/src/logger/logger.h index a4735ac..44a7e7d 100644 --- a/src/logger/logger.h +++ b/src/logger/logger.h @@ -3,4 +3,26 @@ #include "logger/sc_log.h" + +void log_set_verbose(bool value); +bool log_get_verbose(); + +#define log_init() (sc_log_init()) +#define log_set_level(level) (sc_log_set_level((level))) +#define log_set_thread_name(name) (sc_log_set_thread_name((name))) +#define log_term() (sc_log_term()) +#define log_debug(...) (sc_log_debug(__VA_ARGS__)) +#define log_info(...) (sc_log_info(__VA_ARGS__)) +#define log_warn(...) (sc_log_warn(__VA_ARGS__)) +#define log_error(...) (sc_log_error(__VA_ARGS__)) + +#define log_debug_v(...) do { if (log_get_verbose()) { \ + log_debug(__VA_ARGS__); } } while(0) +#define log_info_v(...) do { if (log_get_verbose()) { \ + log_info(__VA_ARGS__); } } while(0) +#define log_warn_v(...) do { if (log_get_verbose()) { \ + log_warn(__VA_ARGS__); } } while(0) +#define log_error_v(...) do { if (log_get_verbose()) { \ + log_error(__VA_ARGS__); } } while(0) + #endif // LOGGER_H_ diff --git a/src/memory.c b/src/memory.c index 7e1d7d3..d6aca24 100644 --- a/src/memory.c +++ b/src/memory.c @@ -39,7 +39,7 @@ HiloadResult read_memory_maps_self(struct sc_array_memreg *regions) { if (!maps_str) return HILOAD_FAIL; - sc_log_debug("/proc/self/maps:\n%s", maps_str); + log_debug_v("/proc/self/maps:\n%s", maps_str); char *strptr = maps_str; @@ -47,10 +47,10 @@ HiloadResult read_memory_maps_self(struct sc_array_memreg *regions) { while (line) { MemoryRegion reg = {0}; + char pathname[256]; char perm_str[5]; u32 dev_major; u32 dev_minor; - char pathname[256]; // clang-format off // Format example from the file: diff --git a/test/manual/minimal.cpp b/test/manual/minimal.cpp index e518576..8417994 100644 --- a/test/manual/minimal.cpp +++ b/test/manual/minimal.cpp @@ -2,24 +2,27 @@ #include "../../include/hiload/hiload.h" +#include #include #include #include +#define ARRLEN(A) (sizeof((A)) / (sizeof((A)[0]))) + int main(int argc, char *argv[]) { const char *reloadable_modules[] = {"", "libmini.so"}; - hi_init(2, reloadable_modules); + hi_init(ARRLEN(reloadable_modules), reloadable_modules); int modified = -1; while (modified != 0) { - printf("==========================\n"); - printf("main address: %p\n", main); - printf("minimal_lib::getNewValue address: %p\n", minimal_lib::getNewValue); - printf("minimal_lib::otherValue address: %p\n", &minimal_lib::otherValue); - printf("==========================\n"); - printf(""); + // printf("==========================\n"); + // printf("main address: %p\n", main); + // printf("minimal_lib::getNewValue address: %p\n", minimal_lib::getNewValue); + // printf("minimal_lib::otherValue address: %p\n", &minimal_lib::otherValue); + // printf("==========================\n"); + // printf(""); modified = minimal_lib::getNewValue(5); printf("getModified(5): %d\n", modified); @@ -27,10 +30,9 @@ int main(int argc, char *argv[]) { printf("otherValue: %d\n", minimal_lib::otherValue++); std::this_thread::sleep_for(std::chrono::seconds(1)); - printf("\n"); - if (hi_reload_solib("libmini.so") != HI_RELOAD_SUCCESS) { - fprintf(stderr, "Failed to load solib\n"); + if (hi_reload() != HILOAD_OK) { + fprintf(stderr, "Failed to load modules\n"); } }