X-Git-Url: http://git.lttng.org./?a=blobdiff_plain;f=ltt%2Fbranches%2Fpoly%2Flttv%2Fmain%2Fmodule.c;h=c4d3d6b07405c037a444cd11058e42cea2de876a;hb=338d4282832c1e0002ccdeeb36555271959dd57b;hp=38f5a881e9b20dbffd4ceea7491bebda49f5fe4d;hpb=9c3123113f395e7ea634c522e340604331359e7e;p=lttv.git diff --git a/ltt/branches/poly/lttv/main/module.c b/ltt/branches/poly/lttv/main/module.c index 38f5a881..c4d3d6b0 100644 --- a/ltt/branches/poly/lttv/main/module.c +++ b/ltt/branches/poly/lttv/main/module.c @@ -1,3 +1,4 @@ + /* This file is part of the Linux Trace Toolkit viewer * Copyright (C) 2003-2004 Michel Dagenais * @@ -17,304 +18,567 @@ */ -/* module.c : Implementation of the module loading/unloading mechanism. - * - */ +/* module.c : Implementation of the module loading/unloading mechanism. */ #include +#include + + +struct _LttvLibrary +{ + LttvLibraryInfo info; + GPtrArray *modules; + GModule *gm; + guint locked_loaded; +}; -#define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format) -#define g_debug(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format) struct _LttvModule { - GModule *module; - guint ref_count; - guint load_count; - GPtrArray *dependents; + LttvModuleInfo info; + char **prerequisites_names; + GPtrArray *prerequisites; }; -/* Table of loaded modules and paths where to search for modules */ +/* Modules are searched by name. However, a library may be loaded which + provides a module with the same name as an existing one. A stack of + modules is thus maintained for each name. + + Libraries correspond to glib modules. The g_module function is + responsible for loading each library only once. */ + +static GHashTable *modules_by_name = NULL; + +static GPtrArray *libraries = NULL; -static GHashTable *modules = NULL; +static GHashTable *libraries_by_g_module = NULL; -static GPtrArray *modulesPaths = NULL; +static GPtrArray *library_paths = NULL; -static void lttv_module_unload_all(); +static gboolean initialized = FALSE; +static gboolean destroyed = TRUE; -void lttv_module_init(int argc, char **argv) +static struct _LttvModuleDescription *builtin_chain = NULL; + +static struct _LttvModuleDescription *module_chain = NULL; + +static struct _LttvModuleDescription **module_next = &module_chain; + +static GQuark lttv_module_error; + +static void init(); + +static finish_destroy(); + +static void module_release(LttvModule *m); + + +static LttvLibrary *library_add(char *name, char *path, GModule *gm) { - g_info("Init module.c"); - modules = g_hash_table_new(g_str_hash, g_str_equal); - modulesPaths = g_ptr_array_new(); -} + LttvLibrary *l; + LttvModule *m; -void lttv_module_destroy() -{ - int i; + struct _LttvModuleDescription *link; + + GPtrArray *modules; - g_info("Destroy module.c"); + l = g_new(LttvLibrary, 1); + l->modules = g_ptr_array_new(); + l->gm = gm; + l->locked_loaded = 0; + l->info.name = g_strdup(name); + l->info.path = g_strdup(path); + l->info.load_count = 0; - /* Unload all modules */ - lttv_module_unload_all(); + g_ptr_array_add(libraries, l); + g_hash_table_insert(libraries_by_g_module, gm, l); - /* Free the modules paths pointer array as well as the elements */ - for(i = 0; i < modulesPaths->len ; i++) { - g_free(modulesPaths->pdata[i]); + *module_next = NULL; + for(link = module_chain; link != NULL; link = link->next) { + m = g_new(LttvModule, 1); + g_ptr_array_add(l->modules, m); + + modules = g_hash_table_lookup(modules_by_name, link->name); + if(modules == NULL) { + modules = g_ptr_array_new(); + g_hash_table_insert(modules_by_name, g_strdup(link->name), modules); + } + g_ptr_array_add(modules, m); + + m->prerequisites_names = link->prerequisites; + m->prerequisites = g_ptr_array_new(); + m->info.name = link->name; + m->info.short_description = link->short_description; + m->info.description = link->description; + m->info.init = link->init; + m->info.destroy = link->destroy; + m->info.library = l; + m->info.require_count = 0; + m->info.use_count = 0; + m->info.prerequisites_number = link->prerequisites_number; } - g_ptr_array_free(modulesPaths,TRUE) ; - modulesPaths = NULL; - - /* destroy the hash table */ - g_hash_table_destroy(modules) ; - modules = NULL; + return l; } -/* Add a new pathname to the modules loading search path */ - -void lttv_module_path_add(const char *name) +static void library_remove(LttvLibrary *l) { - g_info("Add module path %s", name); - g_ptr_array_add(modulesPaths,(char*)g_strdup(name)); + LttvModule *m; + + GPtrArray *modules; + + guint i; + + char *key; + + for(i = 0 ; i < l->modules->len ; i++) { + m = (LttvModule *)(l->modules->pdata[i]); + + g_hash_table_lookup_extended(modules_by_name, m->info.name, + (gpointer *)&key, (gpointer *)&modules); + g_assert(modules != NULL); + g_ptr_array_remove(modules, m); + if(modules->len == 0) { + g_hash_table_remove(modules_by_name, m->info.name); + g_ptr_array_free(modules, TRUE); + g_free(key); + } + + g_ptr_array_free(m->prerequisites, TRUE); + g_free(m); + } + + g_ptr_array_remove(libraries, l); + g_hash_table_remove(libraries_by_g_module, l->gm); + g_ptr_array_free(l->modules, TRUE); + g_free(l->info.name); + g_free(l->info.path); + g_free(l); } -static LttvModule * -module_load(const char *name, int argc, char **argv) +static LttvLibrary *library_load(char *name, GError **error) { GModule *gm; - LttvModule *m; + int i, nb; + + char *path, *pathname; - int i; + LttvLibrary *l; - char *pathname; + GString *messages = g_string_new(""); - const char *module_name; - - LttvModuleInit init_function; + /* insure that module.c is initialized */ - g_info("Load module %s", name); + init(); - /* Try to find the module along all the user specified paths */ + /* Try to find the library along all the user specified paths */ - for(i = 0 ; i < modulesPaths->len ; i++) { - pathname = g_module_build_path(modulesPaths->pdata[i],name); - g_info("Try path %s", pathname); + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Load library %s", name); + nb = lttv_library_path_number(); + for(i = 0 ; i <= nb ; i++) { + if(i < nb) path = lttv_library_path_get(i); + else path = NULL; + + pathname = g_module_build_path(path ,name); + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Try path %s", pathname); + module_chain = NULL; + module_next = &module_chain; gm = g_module_open(pathname,0); g_free(pathname); if(gm != NULL) break; - g_info("Trial failed, %s", g_module_error()); - } - - /* Try the default system path */ - if(gm == NULL) { - pathname = g_module_build_path(NULL,name); - g_info("Try default path"); - gm = g_module_open(pathname,0); - g_free(pathname); + g_string_append(messages, g_module_error()); + g_string_append(messages, "\n"); + g_log(G_LOG_DOMAIN,G_LOG_LEVEL_INFO,"Trial failed, %s", g_module_error()); } /* Module cannot be found */ + if(gm == NULL) { - g_info("Trial failed, %s", g_module_error()); - g_info("Failed to load %s", name); + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Failed to load %s", name); + g_set_error(error, lttv_module_error, LTTV_MODULE_NOT_FOUND, + "Cannot load library %s: %s", name, messages->str); + g_string_free(messages, TRUE); return NULL; } + g_string_free(messages, TRUE); - /* Check if the module was already opened using the hopefully canonical name - returned by g_module_name. */ + /* Check if the library was already loaded */ - module_name = g_module_name(gm); + l = g_hash_table_lookup(libraries_by_g_module, gm); - m = g_hash_table_lookup(modules, module_name); + /* This library was not already loaded */ - if(m == NULL) { - g_info("Module %s (%s) loaded, call its init function", name, module_name); + if(l == NULL) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Library %s (%s) loaded", name, + g_module_name(gm)); + l = library_add(name, path, gm); + } + return l; +} - /* Module loaded for the first time. Insert it in the table and call the - init function if any. */ - m = g_new(LttvModule, 1); - m->module = gm; - m->ref_count = 0; - m->load_count = 0; - m->dependents = g_ptr_array_new(); - g_hash_table_insert(modules, (gpointer)module_name, m); - - if(!g_module_symbol(gm, "init", (gpointer)&init_function)) { - g_warning("module %s (%s) has no init function", name, pathname); - } - else init_function(m, argc, argv); +LttvLibrary *lttv_library_load(char *name, GError **error) +{ + LttvLibrary *l = library_load(name, error); + l->info.load_count++; + return l; +} + + +static void library_unload(LttvLibrary *l) +{ + guint i, len; + + GModule *gm; + + LttvModule *m; + + if(l->locked_loaded > 0) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Unload library %s: locked loaded", + l->info.name); + return; + } + + if(l->info.load_count > 0) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Unload library %s: load count %d", + l->info.name, l->info.load_count); + return; } - else { - /* Module was already opened, check that it really is the same and - undo the extra g_module_open */ + /* Check if all its modules have been released */ - g_info("Module %s (%s) was already loaded, no need to call init function", - name, module_name); - if(m->module != gm) g_error("Two gmodules with the same pathname"); - g_module_close(gm); + for(i = 0 ; i < l->modules->len ; i++) { + m = (LttvModule *)(l->modules->pdata[i]); + if(m->info.use_count > 0) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO,"Unload library %s: module %s used", + l->info.name, m->info.name); + return; + } } - - m->ref_count++; - return m; + + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Unload library %s: close the GModule", + l->info.name); + gm = l->gm; + library_remove(l); + if(gm != NULL) g_module_close(gm); + + /* insure that module.c will be finalized */ + + finish_destroy(); } -LttvModule * -lttv_module_load(const char *name, int argc, char **argv) +void lttv_library_unload(LttvLibrary *l) { - g_info("Load module %s explicitly", name); - LttvModule *m = module_load(name, argc, argv); - if(m != NULL) m->load_count++; - return m; + l->info.load_count--; + library_unload(l); } -LttvModule * -lttv_module_require(LttvModule *m, const char *name, int argc, char **argv) +static void library_lock_loaded(LttvLibrary *l) { - LttvModule *module; + l->locked_loaded++; +} + - g_info("Load module %s, as %s is a dependent requiring it", name, - g_module_name(m->module)); - module = module_load(name, argc, argv); - if(module != NULL) g_ptr_array_add(module->dependents, m); - return module; +static void library_unlock_loaded(LttvLibrary *l) +{ + l->locked_loaded--; + library_unload(l); } -static void module_unload(LttvModule *m) +static LttvModule *module_require(char *name, GError **error) { - LttvModuleDestroy destroy_function; + GError *tmp_error = NULL; - char *pathname; + guint i, j; - guint i, len; + LttvModule *m, *required; - /* Decrement the reference count */ + LttvLibrary *l = NULL; - g_info("Unload module %s", g_module_name(m->module)); - m->ref_count--; - if(m->ref_count > 0) { - g_info("Module usage count decremented to %d", m->ref_count); - return; - } - /* We really have to unload the module, first unload its dependents */ + GPtrArray *modules; + + /* Insure that module.c is initialized */ + + init(); + + /* Check if the module is already loaded */ - len = m->dependents->len; - g_info("Unload dependent modules"); + modules = g_hash_table_lookup(modules_by_name, name); - for(i = 0 ; i < len ; i++) { - module_unload(m->dependents->pdata[i]); + /* Try to load a library having the module name */ + + if(modules == NULL) { + l = library_load(name, error); + if(l == NULL) return NULL; + else library_lock_loaded(l); + + /* A library was found, does it contain the named module */ + + modules = g_hash_table_lookup(modules_by_name, name); + if(modules == NULL) { + g_set_error(error, lttv_module_error, LTTV_MODULE_NOT_FOUND, + "Module %s not found in library %s", name, l->info.name); + library_unlock_loaded(l); + return NULL; + } } + m = (LttvModule *)(modules->pdata[modules->len - 1]); + + /* We have the module */ - if(len != m->dependents->len) g_error("dependents list modified"); + m->info.use_count++; - /* Unload the module itself */ + /* First use of the module. Initialize after getting the prerequisites */ - g_info("Call the destroy function and unload the module"); - if(!g_module_symbol(m->module, "destroy", (gpointer)&destroy_function)) { - g_warning("module (%s) has no destroy function", pathname); + if(m->info.use_count == 1) { + for(i = 0 ; i < m->info.prerequisites_number ; i++) { + required = module_require(m->prerequisites_names[i], &tmp_error); + + /* A prerequisite could not be found, undo everything and fail */ + + if(required == NULL) { + for(j = 0 ; j < m->prerequisites->len ; j++) { + module_release((LttvModule *)(m->prerequisites->pdata[j])); + } + g_ptr_array_set_size(m->prerequisites, 0); + if(l != NULL) library_unlock_loaded(l); + g_set_error(error, lttv_module_error, LTTV_MODULE_NOT_FOUND, + "Cannot find prerequisite for module %s: %s", name, + tmp_error->message); + g_clear_error(&tmp_error); + return NULL; + } + g_ptr_array_add(m->prerequisites, required); + } + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Module %s: init()", m->info.name); + m->info.init(); } - else destroy_function(); - g_hash_table_remove(modules, g_module_name(m->module)); - g_ptr_array_free(m->dependents, TRUE); - g_module_close(m->module); - g_free(m); + /* Decrement the load count of the library. It will not really be + unloaded since it contains a currently used module. */ + + if(l != NULL) library_unlock_loaded(l); + + return(m); } -void lttv_module_unload(LttvModule *m) +/* The require_count for a module is the number of explicit calls to + lttv_module_require, while the use_count also counts the number of times + a module is needed as a prerequisite. */ + +LttvModule *lttv_module_require(char *name, GError **error) { - g_info("Explicitly unload module %s", g_module_name(m->module)); - if(m->load_count <= 0) { - g_error("more unload than load (%s)", g_module_name(m->module)); - return; + LttvModule *m = module_require(name, error); + if(m != NULL) m->info.require_count++; + return(m); +} + + +static void module_release(LttvModule *m) +{ + guint i; + + library_lock_loaded(m->info.library); + + m->info.use_count--; + if(m->info.use_count == 0) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Module %s: destroy()",m->info.name); + m->info.destroy(); + for(i = 0 ; i < m->prerequisites->len ; i++) { + module_release((LttvModule *)(m->prerequisites->pdata[i])); + } + g_ptr_array_set_size(m->prerequisites, 0); } - m->load_count--; - module_unload(m); + library_unlock_loaded(m->info.library); } -static void -list_modules(gpointer key, gpointer value, gpointer user_data) +void lttv_module_release(LttvModule *m) { - g_ptr_array_add((GPtrArray *)user_data, value); + m->info.require_count--; + module_release(m); } -LttvModule ** -lttv_module_list(guint *nb) +void lttv_module_info(LttvModule *m, LttvModuleInfo *info) { - GPtrArray *list = g_ptr_array_new(); + *info = m->info; +} - LttvModule **array; - g_hash_table_foreach(modules, list_modules, list); - *nb = list->len; - array = (LttvModule **)list->pdata; - g_ptr_array_free(list, FALSE); - return array; +unsigned lttv_module_prerequisite_number(LttvModule *m) +{ + return m->prerequisites->len; } -LttvModule ** -lttv_module_info(LttvModule *m, const char **name, - guint *ref_count, guint *load_count, guint *nb_dependents) +LttvModule *lttv_module_prerequisite_get(LttvModule *m, unsigned i) { - guint i, len = m->dependents->len; + return (LttvModule *)(m->prerequisites->pdata[i]); +} - LttvModule **array = g_new(LttvModule *, len); - *name = g_module_name(m->module); - *ref_count = m->ref_count; - *load_count = m->load_count; - *nb_dependents = len; - for(i = 0 ; i < len ; i++) array[i] = m->dependents->pdata[i]; - return array; +void lttv_library_info(LttvLibrary *l, LttvLibraryInfo *info) +{ + *info = l->info; } -char * -lttv_module_name(LttvModule *m) + +unsigned lttv_library_module_number(LttvLibrary *l) { - return g_module_name(m->module); + return l->modules->len; } -static void -list_independent(gpointer key, gpointer value, gpointer user_data) + +LttvModule *lttv_library_module_get(LttvLibrary *l, unsigned i) { - LttvModule *m = (LttvModule *)value; + return (LttvModule *)(l->modules->pdata[i]); +} + - if(m->load_count > 0) g_ptr_array_add((GPtrArray *)user_data, m); +unsigned lttv_library_number() +{ + return libraries->len; } -void -lttv_module_unload_all() +LttvLibrary *lttv_library_get(unsigned i) +{ + return (LttvLibrary *)(libraries->pdata[i]); +} + + +void lttv_library_path_add(char *name) +{ + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Add library path %s", name); + g_ptr_array_add(library_paths,(char*)g_strdup(name)); +} + + +void lttv_library_path_remove(char *name) { guint i; + for(i = 0 ; i < library_paths->len ; i++) { + if(g_str_equal(name, library_paths->pdata[i])) { + g_free(library_paths->pdata[i]); + g_ptr_array_remove_index(library_paths,i); + return; + } + } +} + + +unsigned lttv_library_path_number() +{ + return library_paths->len; +} + + +char *lttv_library_path_get(unsigned i) +{ + return (char *)(library_paths->pdata[library_paths->len - i - 1]); +} + + +void lttv_module_register(struct _LttvModuleDescription *d) +{ + *module_next = d; + module_next = &(d->next); +} + + +static void init() +{ + if(initialized) return; + g_assert(destroyed); + + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Init module.c"); + + initialized = TRUE; + destroyed = FALSE; + lttv_module_error = g_quark_from_string("LTTV_MODULE_ERROR"); + modules_by_name = g_hash_table_new(g_str_hash, g_str_equal); + libraries = g_ptr_array_new(); + libraries_by_g_module = g_hash_table_new(g_direct_hash, g_direct_equal); + library_paths = g_ptr_array_new(); + + if(builtin_chain == NULL) builtin_chain = module_chain; + module_chain = builtin_chain; + library_add("builtin", NULL, NULL); +} + + +static finish_destroy() +{ + guint i; + + if(initialized) return; + g_assert(!destroyed); + + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Finish destroy module.c"); + g_hash_table_destroy(modules_by_name); + g_ptr_array_free(libraries, TRUE); + g_hash_table_destroy(libraries_by_g_module); + for(i = 0 ; i < library_paths->len ; i++) { + g_free(library_paths->pdata[i]); + } + g_ptr_array_free(library_paths, TRUE); + destroyed = TRUE; +} + + +static void destroy() +{ + guint i, j, nb; + + LttvLibrary *l, **locked_libraries; + LttvModule *m; - GPtrArray *independent_modules = g_ptr_array_new(); + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Destroy module.c"); + + /* Unload all libraries */ + + nb = libraries->len; + locked_libraries = g_new(LttvLibrary *, nb); - g_hash_table_foreach(modules, list_independent, independent_modules); + for(i = 0 ; i < nb ; i++) { + l = (LttvLibrary *)(libraries->pdata[i]); + locked_libraries[i] = l; + library_lock_loaded(l); + for(j = 0 ; j < l->modules->len ; j++) { + m = (LttvModule *)(l->modules->pdata[j]); + while(m->info.require_count > 0) lttv_module_release(m); + } + while(l->info.load_count > 0) lttv_library_unload(l); + } - for(i = 0 ; i < independent_modules->len ; i++) { - m = (LttvModule *)independent_modules->pdata[i]; - while(m->load_count > 0) lttv_module_unload(m); + for(i = 0 ; i < nb ; i++) { + l = locked_libraries[i]; + library_unlock_loaded(l); } + g_free(locked_libraries); - g_ptr_array_free(independent_modules, TRUE); - if(g_hash_table_size(modules) != 0) g_warning("cannot unload all modules"); + /* The library containing module.c may be locked by our caller */ + + g_assert(libraries->len <= 1); + + initialized = FALSE; } + +LTTV_MODULE("module", "Modules in libraries", \ + "Load libraries, list, require and initialize contained modules", \ + init, destroy) +