From eccb53529c117e1c39bd558e1db7fb801e054fb8 Mon Sep 17 00:00:00 2001 From: compudj Date: Thu, 29 May 2003 13:14:59 +0000 Subject: [PATCH] premilinary add of modular user interface implementation with a few test modules. git-svn-id: http://ltt.polymtl.ca/svn@13 04897980-b3bd-0310-b5e0-8ef037075253 --- ltt/branches/poly/lttv/hook.c | 76 +++++ ltt/branches/poly/lttv/main.c | 144 ++++++++++ ltt/branches/poly/lttv/module.c | 290 ++++++++++++++++++++ ltt/branches/poly/lttv/option.c | 180 ++++++++++++ ltt/branches/poly/lttv/test/sampledep.c | 21 ++ ltt/branches/poly/lttv/test/samplemodule.c | 19 ++ ltt/branches/poly/lttv/test/samplemodule2.c | 19 ++ 7 files changed, 749 insertions(+) create mode 100644 ltt/branches/poly/lttv/hook.c create mode 100644 ltt/branches/poly/lttv/main.c create mode 100644 ltt/branches/poly/lttv/module.c create mode 100644 ltt/branches/poly/lttv/option.c create mode 100644 ltt/branches/poly/lttv/test/sampledep.c create mode 100644 ltt/branches/poly/lttv/test/samplemodule.c create mode 100644 ltt/branches/poly/lttv/test/samplemodule2.c diff --git a/ltt/branches/poly/lttv/hook.c b/ltt/branches/poly/lttv/hook.c new file mode 100644 index 00000000..18ed98ef --- /dev/null +++ b/ltt/branches/poly/lttv/hook.c @@ -0,0 +1,76 @@ +#include + + +typedef struct _lttv_hook_closure { + lttv_hook hook; + void *hook_data; +} lttv_hook_closure; + + +inline lttv_hooks *lttv_hooks_new() { + return g_array_new(FALSE, FALSE, sizeof(lttv_hook_closure)); +} + +/* MD: delete elements of the array also, but don't free pointed addresses + * (functions). + */ +inline void lttv_hooks_destroy(lttv_hooks *h) { + g_array_free(h, TRUE); +} + +inline void lttv_hooks_add(lttv_hooks *h, lttv_hook f, void *hook_data) { + lttv_hook_closure c; + + /* if h is null, do nothing, or popup a warning message */ + if(h == NULL)return; + + c.hook = f; + c.hook_data = hook_data; + g_array_append_val(h,c); +} + + +void lttv_hooks_call(lttv_hooks *h, void *call_data) +{ + int i; + lttv_hook_closure * c; + + for(i = 0 ; i < h->len ; i++) { + c = ((lttv_hook_closure *)h->data) + i; + if(c != NULL) + c->hook(c->hook_data,call_data); + else + g_critical("NULL hook called\n"); + } +} + + +/* Sometimes different hooks need to be called based on the case. The + case is represented by an unsigned integer id and may represent different + event types, for instance. */ + +inline lttv_hooks_by_id *lttv_hooks_by_id_new() { + return g_ptr_array_new(); +} + + +inline void lttv_hooks_by_id_destroy(lttv_hooks_by_id *h) { + /* MD: delete elements of the array also */ + g_ptr_array_free(h, TRUE); +} + + +void lttv_hooks_by_id_add(lttv_hooks_by_id *h, lttv_hook f, void *hook_data, + unsigned int id) +{ + if(h->len < id) g_ptr_array_set_size(h, id); + if(h->pdata[id] == NULL) h->pdata[id] = lttv_hooks_new(); + lttv_hooks_add(h->pdata[id], f, hook_data); +} + +void lttv_hooks_by_id_call(lttv_hooks_by_id *h, void *call_data, unsigned int id) +{ + if(h->len <= id || h->pdata[id] == NULL) return; + lttv_hooks_call(h->pdata[id],call_data); +} + diff --git a/ltt/branches/poly/lttv/main.c b/ltt/branches/poly/lttv/main.c new file mode 100644 index 00000000..fdb44c70 --- /dev/null +++ b/ltt/branches/poly/lttv/main.c @@ -0,0 +1,144 @@ + +#include +#include + + +/* The main program maintains a few central data structures and relies + on modules for the rest. These data structures may be accessed by modules + through an exported API */ + +/* Extensible array of popt command line options. Modules add options as + they are loaded and initialized. */ + + +lttv_attributes *attributes_global; + +static lttv_hooks + *hooks_init_after, + *hooks_program_before, + *hooks_program_main, + *hooks_program_after; + +// trace sets has to be put one in each new window_traceset +static lttv_trace_set *traces; + +static char *aModule, *aPath, *aTrace; + +static int aArgc; + +static char **aArgv; + +static void lttv_module_option(void *hook_data); + +static void lttv_module_path_option(void *hook_data); + +static void lttv_trace_option(void *hook_data); + +#ifdef MEMDEBUG +extern struct GMemVTable *glib_mem_profiler_table; +#endif + +/* Since everything is done in modules, the main program only takes care + of the infrastructure. */ + +int main(int argc, char **argv) { + + aArgc = argc; + aArgv = argv; + +#ifdef MEMDEBUG + g_mem_set_vtable(glib_mem_profiler_table); + g_message("Memory summary before main"); + g_mem_profile(); +#endif + + attributes_global = lttv_attributes_new(); + +// traces = lttv_trace_set_new(); +// lttv_attributes_set_pointer_pathname(attributes_global, "trace_set/default", traces); + + /* Initialize the hooks */ + + hooks_init_after = lttv_hooks_new(); + lttv_attributes_set_pointer_pathname(attributes_global, "hooks/init/after", + hooks_init_after); + + + hooks_program_before = lttv_hooks_new(); + lttv_attributes_set_pointer_pathname(attributes_global, "hooks/program/before", + hooks_program_before); + + hooks_program_main = lttv_hooks_new(); + lttv_attributes_set_pointer_pathname(attributes_global, "hooks/program/main", + hooks_program_main); + + hooks_program_after = lttv_hooks_new(); + lttv_attributes_set_pointer_pathname(attributes_global, "hooks/program/after", + hooks_program_after); + + /* Initialize the command line options processing */ + + lttv_option_init(argc,argv); + lttv_module_init(argc,argv); + // FIXME lttv_analyse_init(argc,argv); + + /* Initialize the module loading */ + + lttv_module_path_add("/usr/lib/lttv/plugins"); + + /* Add some built-in options */ + + lttv_option_add("module",'m', "load a module", "name of module to load", + LTTV_OPT_STRING, &aModule, lttv_module_option, NULL); + + lttv_option_add("modules-path", 'L', + "add a directory to the module search path", + "directory to add to the path", LTTV_OPT_STRING, &aPath, + lttv_module_path_option, NULL); + + lttv_option_add("trace", 't', + "add a trace to the trace set to analyse", + "pathname of the directory containing the trace", + LTTV_OPT_STRING, &aTrace, lttv_trace_option, NULL); + + lttv_hooks_call(hooks_init_after, NULL); + + lttv_hooks_call(hooks_program_before, NULL); + lttv_hooks_call(hooks_program_main, NULL); + lttv_hooks_call(hooks_program_after, NULL); + + /* Finalize the command line options processing */ + lttv_module_destroy(); + lttv_option_destroy(); + // FIXME lttv_analyse_destroy(); + lttv_attributes_destroy(attributes_global); + +#ifdef MEMDEBUG + g_message("Memory summary after main"); + g_mem_profile(); +#endif + + +} + +void lttv_module_option(void *hook_data) +{ + lttv_module_load(aModule,aArgc,aArgv,STANDALONE); +} + + +void lttv_module_path_option(void *hook_data) +{ + lttv_module_path_add(aPath); +} + + +void lttv_trace_option(void *hook_data) +{ +// lttv_trace *trace; + +// trace = lttv_trace_open(aTrace); +// if(trace == NULL) g_critical("cannot open trace %s", aTrace); +// lttv_trace_set_add(traces, trace); +} + diff --git a/ltt/branches/poly/lttv/module.c b/ltt/branches/poly/lttv/module.c new file mode 100644 index 00000000..ae719504 --- /dev/null +++ b/ltt/branches/poly/lttv/module.c @@ -0,0 +1,290 @@ + +/* module.c : Implementation of the module loading/unloading mechanism. + * + */ + +/* Initial draft by Michel Dagenais May 2003 + * Reworked by Mathieu Desnoyers, May 2003 + */ + +#include +#include +#include + +/* Table of loaded modules and paths where to search for modules */ + +static GHashTable *modules = NULL; + +static GPtrArray *modulesStandalone = NULL; + +static GPtrArray *modulesPaths = NULL; + +void lttv_module_init(int argc, char **argv) { + modules = g_hash_table_new(g_str_hash, g_str_equal); + modulesStandalone = g_ptr_array_new(); + modulesPaths = g_ptr_array_new(); +} + +void lttv_module_destroy() { + + int i; + + /* Unload all modules */ + lttv_module_unload_all(); + + /* Free the modules paths pointer array as well as the elements */ + for(i = 0; i< modulesPaths->len; i++) { + g_free(modulesPaths->pdata[i]); + } + g_ptr_array_free(modulesPaths,TRUE) ; + g_ptr_array_free(modulesStandalone,TRUE) ; + modulesPaths = NULL; + modulesStandalone = NULL; + + /* destroy the hash table */ + g_hash_table_destroy(modules) ; + modules = NULL; +} + +/* Add a new pathname to the modules loading search path */ + +void lttv_module_path_add(const char *name) { + g_ptr_array_add(modulesPaths,(char*)g_strdup(name)); +} + + +/* Load (if not already loaded) the named module. Its init function is + called. We pass the options of the command line to it in case it has + preliminary things to get from it. Note that the normal way to add a + command line option for a module is through the options parsing mecanism. + */ + +lttv_module_info *lttv_module_load(const char *name, int argc, char **argv, loadtype load) { + + GModule *gmodule; + + lttv_module_info *moduleInfo; + + int i; + + char *pathname; + + lttv_module_load_init init_Function; + + /* Find and load the module, It will increase the usage counter + * If the module is already loaded, only the reference counter will + * be incremented. It's part of the gmodule architecture. Very useful + * for modules dependencies. + */ + + g_assert(name != NULL); + + for(i = 0 ; i < modulesPaths->len ; i++) { + pathname = g_module_build_path(modulesPaths->pdata[i],name); + gmodule = g_module_open(pathname,0) ; + + + if(gmodule != NULL) { + g_message("Loading module %s ... found!",pathname); + + /* Was the module already opened? */ + moduleInfo = g_hash_table_lookup(modules,g_module_name(gmodule)); + + /* First time the module is opened */ + + if(moduleInfo == NULL ) { + moduleInfo = g_new(lttv_module_info, 1); + moduleInfo->module = gmodule; + moduleInfo->pathname = g_module_name(gmodule); + moduleInfo->directory = modulesPaths->pdata[i]; + moduleInfo->name = (char *)g_strdup(name); + moduleInfo->ref_count = 0; + moduleInfo->index_standalone = -1; + g_hash_table_insert(modules, moduleInfo->pathname, moduleInfo); + if(!g_module_symbol(gmodule, "init", (gpointer) &init_Function)) { + g_critical("module %s (%s) does not have init function", + moduleInfo->pathname,moduleInfo->name); + } + else { + init_Function(argc,argv); + } + } + + /* Add the module in the standalone array if the module is + * standalone and not in the array. Otherwise, set index to + * -1 (dependant only). + */ + if(load == STANDALONE) { + + if(moduleInfo->index_standalone == -1) { + + g_ptr_array_add(modulesStandalone, moduleInfo); + moduleInfo->index_standalone = modulesStandalone->len - 1; + + moduleInfo->ref_count++ ; + } + else { + g_warning("Module %s is already loaded standalone.",pathname); + /* Decrease the gmodule use_count. Has previously been increased in the g_module_open. */ + g_module_close(moduleInfo->module) ; + } + } + else { /* DEPENDANT */ + moduleInfo->ref_count++ ; + } + + return moduleInfo; + } + g_message("Loading module %s ... missing.",pathname); + g_free(pathname); + } + g_critical("module %s not found",name); + return NULL; +} + +/* Unload the named module. */ + +int lttv_module_unload_pathname(const char *pathname, loadtype load) { + + lttv_module_info *moduleInfo; + + moduleInfo = g_hash_table_lookup(modules, pathname); + + /* If no module of that name is loaded, nothing to unload. */ + if(moduleInfo != NULL) { + g_message("Unloading module %s : is loaded.\n", pathname) ; + lttv_module_unload(moduleInfo, load) ; + return 1; + } + else { + g_message("Unloading module %s : is not loaded.\n", pathname) ; + return 0; + } + +} + +int lttv_module_unload_name(const char *name, loadtype load) { + + int i; + + char *pathname; + + /* Find and load the module, It will increase the usage counter + * If the module is already loaded, only the reference counter will + * be incremented. It's part of the gmodule architecture. Very useful + * for modules dependencies. + */ + + g_assert(name != NULL); + + for(i = 0 ; i < modulesPaths->len ; i++) { + + pathname = g_module_build_path(modulesPaths->pdata[i],name); + + if(lttv_module_unload_pathname(pathname, load) == TRUE) + return TRUE ; + } + g_critical("module %s not found",name); + return FALSE; +} + + + +/* Unload the module. We use a call_gclose boolean to keep the g_module_close call + * after the call to the module's destroy function. */ + +int lttv_module_unload(lttv_module_info *moduleInfo, loadtype load) { + + lttv_module_unload_destroy destroy_Function; + + char *moduleName ; + + gboolean call_gclose = FALSE; + + if(moduleInfo == NULL) return FALSE; + + /* Closing the module decrements the usage counter if previously higher than + * 1. If 1, it unloads the module. + */ + + /* Add the module in the standalone array if the module is + * standalone and not in the array. Otherwise, set index to + * -1 (dependant only). + */ + if(load == STANDALONE) { + + if(moduleInfo->index_standalone == -1) { + + g_warning("Module %s is not loaded standalone.",moduleInfo->pathname); + } + else { + /* We do not remove the element of the array, it would change + * the index orders. We will have to check if index is -1 in + * unload all modules. + */ + moduleInfo->index_standalone = -1; + g_message("Unloading module %s, reference count passes from %u to %u", + moduleInfo->pathname,moduleInfo->ref_count, + moduleInfo->ref_count-1); + + moduleInfo->ref_count-- ; + call_gclose = TRUE ; + } + } + else { /* DEPENDANT */ + g_message("Unloading module %s, reference count passes from %u to %u", + moduleInfo->pathname, + moduleInfo->ref_count,moduleInfo->ref_count-1); + + moduleInfo->ref_count-- ; + call_gclose = TRUE ; + } + + /* The module is really closing if ref_count is 0 */ + if(!moduleInfo->ref_count) { + g_message("Unloading module %s : closing module.",moduleInfo->pathname); + + /* Call the destroy function of the module */ + if(!g_module_symbol(moduleInfo->module, "destroy", (gpointer) &destroy_Function)) { + g_critical("module %s (%s) does not have destroy function", + moduleInfo->pathname,moduleInfo->name); + } + else { + destroy_Function(); + } + + /* If the module will effectively be closed, remove the moduleInfo from + * the hash table and free the module name. + */ + g_free(moduleInfo->name) ; + + g_hash_table_remove(modules, moduleInfo->pathname); + } + + if(call_gclose) g_module_close(moduleInfo->module) ; + + return TRUE ; +} + +#define MODULE_I ((lttv_module_info *)modulesStandalone->pdata[i]) + +/* unload all the modules in the hash table, calling module_destroy for + * each of them. + * + * We first take all the moduleInfo in the hash table, put it in an + * array. We use qsort on the array to have the use count of 1 first. + */ +void lttv_module_unload_all() { + + int i = 0; + + /* call the unload for each module. + */ + for(i = 0; i < modulesStandalone->len; i++) { + + if(MODULE_I->index_standalone != -1) { + lttv_module_unload(MODULE_I,STANDALONE) ; + } + } + +} diff --git a/ltt/branches/poly/lttv/option.c b/ltt/branches/poly/lttv/option.c new file mode 100644 index 00000000..aa8656f8 --- /dev/null +++ b/ltt/branches/poly/lttv/option.c @@ -0,0 +1,180 @@ + +#include +#include + +/* Extensible array of popt command line options. Modules add options as + they are loaded and initialized. */ + +typedef struct _lttv_option { + lttv_option_hook hook; + void *hook_data; +} lttv_option; + +extern lttv_attributes *attributes_global; + +static GArray *lttv_options_command; + +static GArray *lttv_options_command_popt; + +// unneeded static lttv_key *key ; + +static int command_argc; + +static char **command_argv; + +/* Lists of hooks to be called at different places */ + +static lttv_hooks + *hooks_options_before, + *hooks_options_after; + +static gboolean init_done = FALSE; + +void lttv_options_command_parse(void *hook_data, void *call_data); + + +void lttv_option_init(int argc, char **argv) { + + lttv_hooks *hooks_init_after; + + if(init_done) return; + else init_done = TRUE; + + command_argc = argc; + command_argv = argv; + + hooks_options_before = lttv_hooks_new(); + hooks_options_after = lttv_hooks_new(); + + lttv_attributes_set_pointer_pathname(attributes_global, + "hooks/options/before", hooks_options_before); + + lttv_attributes_set_pointer_pathname(attributes_global, + "hooks/options/after", hooks_options_after); + + lttv_options_command_popt = g_array_new(0,0,sizeof(struct poptOption)); + lttv_options_command = g_array_new(0,0,sizeof(lttv_option)); + + hooks_init_after = lttv_attributes_get_pointer_pathname(attributes_global, + "hooks/init/after"); + lttv_hooks_add(hooks_init_after, lttv_options_command_parse, NULL); + +} + +void lttv_option_destroy() { + + g_array_free(lttv_options_command_popt,TRUE) ; + g_array_free(lttv_options_command,TRUE) ; + + lttv_attributes_set_pointer_pathname(attributes_global, + "hooks/options/before", NULL); + + lttv_attributes_set_pointer_pathname(attributes_global, + "hooks/options/after", NULL); + + lttv_hooks_destroy(hooks_options_before); + lttv_hooks_destroy(hooks_options_after); + +} + + +static int poptToLTT[] = { + POPT_ARG_NONE, POPT_ARG_STRING, POPT_ARG_INT, POPT_ARG_LONG +}; + + +void lttv_option_add(char *long_name, char char_name, char *description, + char *argDescription, lttv_option_type t, void *p, + lttv_option_hook h, void *hook_data) +{ + struct poptOption poption; + + lttv_option option; + + poption.longName = long_name; + poption.shortName = char_name; + poption.descrip = description; + poption.argDescrip = argDescription; + poption.argInfo = poptToLTT[t]; + poption.arg = p; + poption.val = lttv_options_command->len + 1; + + option.hook = h; + option.hook_data = hook_data; + + g_array_append_val(lttv_options_command_popt,poption); + g_array_append_val(lttv_options_command,option); +} + + +static struct poptOption endOption = { NULL, '\0', 0, NULL, 0}; + +/* As we may load modules in the hooks called for argument processing, + * we have to recreate the argument context each time the + * lttv_options_command_popt is modified. This way we will be able to + * parse arguments defined by the modules + */ + +void lttv_options_command_parse(void *hook_data, void *call_data) +{ + int rc; + int lastrc; + poptContext c; + lttv_option *option; + + lttv_hooks_call(hooks_options_before,NULL); + /* Always add then remove the null option around the get context */ + g_array_append_val(lttv_options_command_popt, endOption); + /* Compiler warning caused by const char ** for command_argv in header */ + /* Nothing we can do about it. Header should not put it const. */ + c = poptGetContext("lttv", command_argc, (const char**)command_argv, + (struct poptOption *)(lttv_options_command_popt->data),0); + + /* We remove the null option here to be able to add options correctly */ + g_array_remove_index(lttv_options_command_popt, + lttv_options_command_popt->len - 1); + + /* There is no last good offset */ + lastrc = -1; + + /* Parse options while not end of options event */ + while((rc = poptGetNextOpt(c)) != -1) { + + if(rc == POPT_ERROR_BADOPT) { + /* We need to redo the context with information added by modules */ + g_array_append_val(lttv_options_command_popt, endOption); + poptFreeContext(c); + c = poptGetContext("lttv", command_argc, (const char**)command_argv, + (struct poptOption *)lttv_options_command_popt->data,0); + g_array_remove_index(lttv_options_command_popt, + lttv_options_command_popt->len); + + /* Cut out the already parsed elements */ + if(lastrc != -1) + while(poptGetNextOpt(c) != lastrc) { } ; + + /* Get the same option once again */ + g_assert(rc = poptGetNextOpt(c) != -1) ; + if(rc == POPT_ERROR_BADOPT) { + /* If here again we have a parsing error with all context info ok, + * then there is a problem in the arguments themself, give up */ + g_critical("option %s: %s", poptBadOption(c,0), poptStrerror(rc)); + break ; + } + } + + /* Remember this offset as the last good option value */ + lastrc = rc; + + /* Execute the hook registered with this option */ + option = ((lttv_option *)lttv_options_command->data) + rc - 1; + if(option->hook != NULL) option->hook(option->hook_data); + + } + + poptFreeContext(c); + + lttv_hooks_call(hooks_options_after,NULL); + +} + diff --git a/ltt/branches/poly/lttv/test/sampledep.c b/ltt/branches/poly/lttv/test/sampledep.c new file mode 100644 index 00000000..8275982c --- /dev/null +++ b/ltt/branches/poly/lttv/test/sampledep.c @@ -0,0 +1,21 @@ +/* Sample module for Linux Trace Toolkit new generation User Interface */ + +/* Created by Mathieu Desnoyers, may 2003 */ + +#include +#include + +/* Include module.h from lttv headers for module loading */ +#include "../../include/lttv/module.h" + +G_MODULE_EXPORT void init() { + g_critical("Sample module dependant init()"); + + lttv_module_load("samplemodule",0,NULL,DEPENDANT); +} + +G_MODULE_EXPORT void destroy() { + g_critical("Sample module dependant destroy()"); + lttv_module_unload_name("samplemodule",DEPENDANT); +} + diff --git a/ltt/branches/poly/lttv/test/samplemodule.c b/ltt/branches/poly/lttv/test/samplemodule.c new file mode 100644 index 00000000..53240aae --- /dev/null +++ b/ltt/branches/poly/lttv/test/samplemodule.c @@ -0,0 +1,19 @@ +/* Sample module for Linux Trace Toolkit new generation User Interface */ + +/* Created by Mathieu Desnoyers, may 2003 */ + +#include +#include + +#include "../../include/lttv/attribute.h" + +extern lttv_attributes *attributes_global; + +G_MODULE_EXPORT void init() { + g_critical("Sample module init()"); +} + +G_MODULE_EXPORT void destroy() { + g_critical("Sample module destroy()"); +} + diff --git a/ltt/branches/poly/lttv/test/samplemodule2.c b/ltt/branches/poly/lttv/test/samplemodule2.c new file mode 100644 index 00000000..03fbfe44 --- /dev/null +++ b/ltt/branches/poly/lttv/test/samplemodule2.c @@ -0,0 +1,19 @@ +/* Sample module for Linux Trace Toolkit new generation User Interface */ + +/* Created by Mathieu Desnoyers, may 2003 */ + +#include +#include + +#include "../../include/lttv/attribute.h" + +extern lttv_attributes *attributes_global; + +G_MODULE_EXPORT void init() { + g_critical("Sample module 2 init()"); +} + +G_MODULE_EXPORT void destroy() { + g_critical("Sample module 2 destroy()"); +} + -- 2.34.1