X-Git-Url: http://info.iut-bm.univ-fcomte.fr/pub/gitweb/simgrid.git/blobdiff_plain/745b5fc8d531eb1a573d9d47a9217811f8753071..576680fc0cbe8a97b0e439c0db295b23f5f8816c:/src/xbt/log.c diff --git a/src/xbt/log.c b/src/xbt/log.c index cd834908c5..767f8835ea 100644 --- a/src/xbt/log.c +++ b/src/xbt/log.c @@ -1,138 +1,64 @@ -/* $Id$ */ - /* log - a generic logging facility in the spirit of log4j */ -/* Copyright (c) 2003, 2004 Martin Quinson. All rights reserved. */ +/* Copyright (c) 2004-2017. The SimGrid Team. + * All rights reserved. */ /* This program is free software; you can redistribute it and/or modify it * under the terms of the license (GNU LGPL) which comes with this package. */ #include #include +#include /* snprintf */ +#include /* snprintf */ + +#include "src/internal_config.h" -#include "xbt_modinter.h" +#include "src/xbt_modinter.h" +#include "src/xbt/log_private.h" +#include "xbt/asserts.h" +#include "xbt/dynar.h" +#include "xbt/ex.h" #include "xbt/misc.h" +#include "xbt/str.h" #include "xbt/sysdep.h" -#include "xbt/log.h" -#include "xbt/error.h" -#include "xbt/dynar.h" +#include "xbt/xbt_os_thread.h" +int xbt_log_no_loc = 0; /* if set to true (with --log=no_loc), file localization will be omitted (for tesh tests) */ +static xbt_os_mutex_t log_cat_init_mutex = NULL; -/* -FAIRE DES ZOLIS LOGS --------------------- -Dans gras, tu ne te contente pas d'écrire des choses à l'écran, mais tu -écris sur un sujet particulier (notion de canal) des choses d'une gravité -particulière. Il y a 7 niveaux de gravité. - trace: tracer les entrées dans une fonction, retour de fonction - (famille de macros XBT_IN/XBT_OUT) - debug: pour t'aider à mettre au point le module, potentiellement tres bavard - verbose: quelques infos succintes sur les internals du module - info: niveau normal, ton de la conversation - warning: problème potentiel, mais auquel on a su faire face - error: problème qui t'as empêché de faire ton job - critical: juste avant de mourir - -Quand on compile avec -DNDEBUG (par défaut dans le paquet Debian), tout ce -qui est '>= verbose' est supprimé au moment de la compilation. Retiré du -binaire, killé. - -Ensuite, tu écris dans un canal particulier. Tous les canaux sont rangés en -arbre. Il faudrait faire un ptit script qui fouille les sources à la -recherche des macros XBT_LOG_NEW_* utilisées pour créer des canaux. Le -dernier argument de ces macros est ignoré dans le source. Il est destiné à -être la documentation de la chose en une ligne. En gros, ca fait: -root - +--xbt - | +--config - | +--dict - | | +--dict_cursor - | | +--dict_elm - | | ... - | +--dynar - | +--set - | +--log - | +--module - +--gras - +--datadesc - | +--ddt_cbps - | +--ddt_convert - | +--ddt_exchange - | +--ddt_parse - | +--lexer - +--msg - +--transport - +--raw_trp (Je devrais tuer ce module, un jour) - +--trp_buf - +--trp_sg - +--trp_file - +--trp_tcp - -Et ensuite les utilisateurs peuvent choisir le niveau de gravité qui les -interresse sur tel ou tel sujet. - -Toute la mécanique de logging repose sur des variables statiques dont le nom -dépend du nom du canal. - => attention aux conflits de nom de canal - => il faut une macro XBT_LOG dans chaque fichier où tu fais des logs. - -XBT_LOG_NEW_CATEGORY: nouveau canal sous "root". Rare, donc. -XBT_LOG_NEW_SUBCATEGORY: nouveau canal dont on précise le père. -XBT_LOG_DEFAULT_CATEGORY: indique quel est le canal par défaut dans ce fichier -XBT_LOG_NEW_DEFAULT_CATEGORY: Crèe un canal et l'utilise par défaut -XBT_LOG_NEW_DEFAULT_SUBCATEGORY: devine -XBT_LOG_EXTERNAL_CATEGORY: quand tu veux utiliser par défaut un canal créé - dans un autre fichier. - -Une fois que ton canal est créé, tu l'utilise avec les macros LOG, DEBUG, -VERB, WARN, ERROR et CRITICAL. Il faut que tu donne le nombre d'arguments -après le nom de macro. Exemple: LOG2("My name is %s %s","Martin","Quinson") -Si tu veux préciser explicitement le canal où écrire, ajoute un C devant le -nom de la macro. Exemple: CCRITICAL0(module, "Cannot initialize GRAS") - -Toutes ces macros (enfin, ce en quoi elles se réécrivent) vérifient leurs -arguments comme printf le fait lorsqu'on compile avec gcc. -LOG1("name: %d","toto"); donne un warning, et donc une erreur en mode -mainteneur. - -Enfin, tu peux tester si un canal est ouvert à une priorité donnée (pour -préparer plus de débug, par exemple. Dans le parseur, je fais du pretty -printing sur ce qu'il faut parser dans ce cas). -XBT_LOG_ISENABLED(catName, priority) Le second argument doit être une valeur -de e_xbt_log_priority_t (log.h). Par exemple: xbt_log_priority_verbose - -Voila sur comment mettre des logs dans ton code. N'hesite pas à faire pleins -de canaux différents pour des aspects différents de ton code. En -particulier, dans les dict, j'ai un canal pour l'ajout, le retrait, le -netoyage du code après suppression et ainsi de suite. De cette façon, je -peux choisir qui m'interresse. - - -Pour utiliser les logs, tu déjà faire, non ? Tu colle sur la ligne de -commande un ou plusieurs arguments de la forme - --gras-log=" [+]" (ou sans " si t'as pas d'espace) -chaque réglage étant de la forme: - .thres= -Les différents réglages sont lus de gauche à droite. -"root.thres=debug root.thres=critical" ferme tout, normalement. - -*/ +/** \addtogroup XBT_log + * + * For more information, please refer to @ref outcomes_logs Section. + */ + +xbt_log_appender_t xbt_log_default_appender = NULL; /* set in log_init */ +xbt_log_layout_t xbt_log_default_layout = NULL; /* set in log_init */ typedef struct { char *catname; + char *fmt; e_xbt_log_priority_t thresh; -} s_xbt_log_setting_t,*xbt_log_setting_t; + int additivity; + xbt_log_appender_t appender; +} s_xbt_log_setting_t; + +typedef s_xbt_log_setting_t* xbt_log_setting_t; + +static xbt_dynar_t xbt_log_settings = NULL; -static xbt_dynar_t xbt_log_settings=NULL; -static void _free_setting(void *s) { - xbt_log_setting_t set=(xbt_log_setting_t)s; +static void _free_setting(void *s) +{ + xbt_log_setting_t set = *(xbt_log_setting_t *) s; if (set) { - xbt_free(set->catname); -/* xbt_free(set); FIXME: uncommenting this leads to segfault when more than one chunk is passed as gras-log */ + free(set->catname); + free(set->fmt); + free(set); } } +static void _xbt_log_cat_apply_set(xbt_log_category_t category, xbt_log_setting_t setting); + const char *xbt_log_priority_names[8] = { "NONE", "TRACE", @@ -145,353 +71,836 @@ const char *xbt_log_priority_names[8] = { }; s_xbt_log_category_t _XBT_LOGV(XBT_LOG_ROOT_CAT) = { - 0, 0, 0, - "root", xbt_log_priority_uninitialized, 0, - NULL, 0 + NULL /*parent */ , NULL /* firstChild */ , NULL /* nextSibling */ , + "root", "The common ancestor for all categories", + 0 /*initialized */, xbt_log_priority_uninitialized /* threshold */ , + 0 /* isThreshInherited */ , + NULL /* appender */ , NULL /* layout */ , + 0 /* additivity */ }; -XBT_LOG_NEW_SUBCATEGORY(xbt,XBT_LOG_ROOT_CAT,"All XBT categories (simgrid toolbox)"); -XBT_LOG_NEW_SUBCATEGORY(surf,XBT_LOG_ROOT_CAT,"All SURF categories"); -XBT_LOG_NEW_DEFAULT_SUBCATEGORY(log,xbt,"Loggings from the logging mecanism itself"); +XBT_LOG_NEW_DEFAULT_SUBCATEGORY(log, xbt, "Loggings from the logging mechanism itself"); + +/* create the default appender and install it in the root category, + which were already created (damnit. Too slow little beetle) */ +void xbt_log_preinit(void) +{ + xbt_log_default_appender = xbt_log_appender_file_new(NULL); + xbt_log_default_layout = xbt_log_layout_simple_new(NULL); + _XBT_LOGV(XBT_LOG_ROOT_CAT).appender = xbt_log_default_appender; + _XBT_LOGV(XBT_LOG_ROOT_CAT).layout = xbt_log_default_layout; + log_cat_init_mutex = xbt_os_mutex_init(); +} -void xbt_log_init(int *argc,char **argv, const char *defaultlog) { - int i,j; - char *opt; - int found=0; +static void xbt_log_connect_categories(void) +{ + /* Connect our log channels: that must be done manually under windows */ + /* Also permit that they are correctly listed by xbt_log_help_categories() */ + + /* xbt */ + XBT_LOG_CONNECT(xbt); + XBT_LOG_CONNECT(log); + XBT_LOG_CONNECT(module); + XBT_LOG_CONNECT(replay); + XBT_LOG_CONNECT(xbt_cfg); + XBT_LOG_CONNECT(xbt_dict); + XBT_LOG_CONNECT(xbt_dict_cursor); + XBT_LOG_CONNECT(xbt_dict_elm); + XBT_LOG_CONNECT(xbt_dyn); + XBT_LOG_CONNECT(xbt_ex); + XBT_LOG_CONNECT(xbt_automaton); + XBT_LOG_CONNECT(xbt_backtrace); + XBT_LOG_CONNECT(xbt_exception); + XBT_LOG_CONNECT(xbt_graph); + XBT_LOG_CONNECT(xbt_mallocator); + XBT_LOG_CONNECT(xbt_memory_map); + XBT_LOG_CONNECT(xbt_parmap); + XBT_LOG_CONNECT(xbt_sync); + XBT_LOG_CONNECT(xbt_sync_os); + +#ifdef simgrid_EXPORTS + /* The following categories are only defined in libsimgrid */ + + /* bindings */ +#if SIMGRID_HAVE_LUA + XBT_LOG_CONNECT(lua); + XBT_LOG_CONNECT(lua_host); + XBT_LOG_CONNECT(lua_platf); + XBT_LOG_CONNECT(lua_debug); +#endif + + /* instr */ + XBT_LOG_CONNECT(instr); + XBT_LOG_CONNECT(instr_api); + XBT_LOG_CONNECT(instr_config); + XBT_LOG_CONNECT(instr_msg); + XBT_LOG_CONNECT(instr_msg_process); + XBT_LOG_CONNECT(instr_paje_containers); + XBT_LOG_CONNECT(instr_paje_header); + XBT_LOG_CONNECT(instr_paje_trace); + XBT_LOG_CONNECT(instr_paje_types); + XBT_LOG_CONNECT(instr_paje_values); + XBT_LOG_CONNECT(instr_resource); + XBT_LOG_CONNECT(instr_routing); + XBT_LOG_CONNECT(instr_surf); + + /* jedule */ +#if SIMGRID_HAVE_JEDULE + XBT_LOG_CONNECT(jedule); + XBT_LOG_CONNECT(jed_sd); +#endif + + /* mc */ +#if SIMGRID_HAVE_MC + XBT_LOG_CONNECT(mc); + XBT_LOG_CONNECT(mc_checkpoint); + XBT_LOG_CONNECT(mc_comm_determinism); + XBT_LOG_CONNECT(mc_compare); + XBT_LOG_CONNECT(mc_dwarf); + XBT_LOG_CONNECT(mc_hash); + XBT_LOG_CONNECT(mc_liveness); + XBT_LOG_CONNECT(mc_memory); + XBT_LOG_CONNECT(mc_page_snapshot); + XBT_LOG_CONNECT(mc_request); + XBT_LOG_CONNECT(mc_safety); + XBT_LOG_CONNECT(mc_VisitedState); + XBT_LOG_CONNECT(mc_client); + XBT_LOG_CONNECT(mc_client_api); + XBT_LOG_CONNECT(mc_comm_pattern); + XBT_LOG_CONNECT(mc_process); + XBT_LOG_CONNECT(mc_protocol); + XBT_LOG_CONNECT(mc_Channel); + XBT_LOG_CONNECT(mc_ModelChecker); + XBT_LOG_CONNECT(mc_RegionSnaphot); + XBT_LOG_CONNECT(mc_Session); + XBT_LOG_CONNECT(mc_state); +#endif + XBT_LOG_CONNECT(mc_global); + XBT_LOG_CONNECT(mc_config); + XBT_LOG_CONNECT(mc_record); + + /* msg */ + XBT_LOG_CONNECT(msg); + XBT_LOG_CONNECT(msg_action); + XBT_LOG_CONNECT(msg_gos); + XBT_LOG_CONNECT(msg_io); + XBT_LOG_CONNECT(msg_kernel); + XBT_LOG_CONNECT(msg_mailbox); + XBT_LOG_CONNECT(msg_process); + XBT_LOG_CONNECT(msg_synchro); + XBT_LOG_CONNECT(msg_task); + XBT_LOG_CONNECT(msg_vm); + + /* s4u */ + XBT_LOG_CONNECT(s4u); + XBT_LOG_CONNECT(s4u_activity); + XBT_LOG_CONNECT(s4u_actor); + XBT_LOG_CONNECT(s4u_netzone); + XBT_LOG_CONNECT(s4u_channel); + XBT_LOG_CONNECT(s4u_comm); + XBT_LOG_CONNECT(s4u_file); + XBT_LOG_CONNECT(s4u_link); + XBT_LOG_CONNECT(s4u_vm); + + /* sg */ + XBT_LOG_CONNECT(sg_host); + + /* simdag */ + XBT_LOG_CONNECT(sd); + XBT_LOG_CONNECT(sd_daxparse); +#if HAVE_GRAPHVIZ + XBT_LOG_CONNECT(sd_dotparse); +#endif + XBT_LOG_CONNECT(sd_kernel); + XBT_LOG_CONNECT(sd_task); + + /* simix */ + XBT_LOG_CONNECT(simix); + XBT_LOG_CONNECT(simix_context); + XBT_LOG_CONNECT(simix_deployment); + XBT_LOG_CONNECT(simix_environment); + XBT_LOG_CONNECT(simix_host); + XBT_LOG_CONNECT(simix_io); + XBT_LOG_CONNECT(simix_kernel); + XBT_LOG_CONNECT(simix_mailbox); + XBT_LOG_CONNECT(simix_network); + XBT_LOG_CONNECT(simix_process); + XBT_LOG_CONNECT(simix_popping); + XBT_LOG_CONNECT(simix_synchro); + + /* smpi */ + /* SMPI categories are connected in smpi_global.c */ + + /* surf */ + XBT_LOG_CONNECT(surf); + XBT_LOG_CONNECT(surf_config); + XBT_LOG_CONNECT(surf_cpu); + XBT_LOG_CONNECT(surf_cpu_cas); + XBT_LOG_CONNECT(surf_cpu_ti); + XBT_LOG_CONNECT(surf_energy); + XBT_LOG_CONNECT(surf_kernel); + XBT_LOG_CONNECT(surf_lagrange); + XBT_LOG_CONNECT(surf_lagrange_dichotomy); + XBT_LOG_CONNECT(surf_maxmin); + XBT_LOG_CONNECT(surf_network); +#if SIMGRID_HAVE_NS3 + XBT_LOG_CONNECT(ns3); +#endif + XBT_LOG_CONNECT(surf_parse); + XBT_LOG_CONNECT(surf_plugin_load); + XBT_LOG_CONNECT(surf_route); + XBT_LOG_CONNECT(surf_routing_generic); + XBT_LOG_CONNECT(surf_route_cluster); + XBT_LOG_CONNECT(surf_route_cluster_torus); + XBT_LOG_CONNECT(surf_route_cluster_dragonfly); + XBT_LOG_CONNECT(surf_route_dijkstra); + XBT_LOG_CONNECT(surf_route_fat_tree); + XBT_LOG_CONNECT(surf_route_floyd); + XBT_LOG_CONNECT(surf_route_full); + XBT_LOG_CONNECT(surf_route_none); + XBT_LOG_CONNECT(surf_route_vivaldi); + XBT_LOG_CONNECT(surf_storage); + XBT_LOG_CONNECT(surf_trace); + XBT_LOG_CONNECT(surf_vm); + XBT_LOG_CONNECT(surf_host); + +#endif /* simgrid_EXPORTS */ +} - /** Set logs and init log submodule */ - for (i=1; i<*argc; i++) { - if (!strncmp(argv[i],"--gras-log=",strlen("--gras-log="))) { - found = 1; - opt=strchr(argv[i],'='); +static void xbt_log_help(void); +static void xbt_log_help_categories(void); + +/** @brief Get all logging settings from the command line + * + * xbt_log_control_set() is called on each string we got from cmd line + */ +void xbt_log_init(int *argc, char **argv) +{ + unsigned help_requested = 0; /* 1: logs; 2: categories */ + int j = 1; + + /* Set logs and init log submodule */ + for (int i = 1; i < *argc; i++) { + if (!strncmp(argv[i], "--log=", strlen("--log="))) { + char* opt = strchr(argv[i], '='); opt++; xbt_log_control_set(opt); - DEBUG1("Did apply '%s' as log setting",opt); - /*remove this from argv*/ - for (j=i+1; j<*argc; j++) { - argv[j-1] = argv[j]; - } - argv[j-1] = NULL; - (*argc)--; - i--; /* compensate effect of next loop incrementation */ + XBT_DEBUG("Did apply '%s' as log setting", opt); + } else if (!strcmp(argv[i], "--help-logs")) { + help_requested |= 1U; + } else if (!strcmp(argv[i], "--help-log-categories")) { + help_requested |= 2U; + } else { + argv[j++] = argv[i]; } } - if (!found && defaultlog) { - xbt_log_control_set(defaultlog); + if (j < *argc) { + argv[j] = NULL; + *argc = j; } -} -void xbt_log_exit(void) { - VERB0("Exiting log"); - xbt_dynar_free(&xbt_log_settings); - VERB0("Exited log"); + if (help_requested) { + if (help_requested & 1) + xbt_log_help(); + if (help_requested & 2) + xbt_log_help_categories(); + exit(0); + } } -static void _apply_control(xbt_log_category_t cat) { - int cursor; - xbt_log_setting_t setting=NULL; - int found = 0; +static void log_cat_exit(xbt_log_category_t cat) +{ + xbt_log_category_t child; - if (!xbt_log_settings) - return; + if (cat->appender) { + if (cat->appender->free_) + cat->appender->free_(cat->appender); + free(cat->appender); + } + if (cat->layout) { + if (cat->layout->free_) + cat->layout->free_(cat->layout); + free(cat->layout); + } + + for (child = cat->firstChild; child != NULL; child = child->nextSibling) + log_cat_exit(child); +} + +void xbt_log_postexit(void) +{ + XBT_VERB("Exiting log"); + xbt_os_mutex_destroy(log_cat_init_mutex); + xbt_dynar_free(&xbt_log_settings); + log_cat_exit(&_XBT_LOGV(XBT_LOG_ROOT_CAT)); +} - xbt_assert0(cat,"NULL category"); - xbt_assert(cat->name); +/* Size of the static string in which we build the log string */ +#define XBT_LOG_STATIC_BUFFER_SIZE 2048 +/* Minimum size of the dynamic string in which we build the log string + (should be greater than XBT_LOG_STATIC_BUFFER_SIZE) */ +#define XBT_LOG_DYNAMIC_BUFFER_SIZE 4096 - xbt_dynar_foreach(xbt_log_settings,cursor,setting) { - xbt_assert0(setting,"Damnit, NULL cat in the list"); - xbt_assert1(setting->catname,"NULL setting(=%p)->catname",(void*)setting); +void _xbt_log_event_log(xbt_log_event_t ev, const char *fmt, ...) +{ + xbt_log_category_t cat = ev->cat; - if (!strcmp(setting->catname,cat->name)) { - found = 1; + xbt_assert(ev->priority >= 0, "Negative logging priority naturally forbidden"); + xbt_assert(ev->priority < sizeof(xbt_log_priority_names), "Priority %d is greater than the biggest allowed value", + ev->priority); - xbt_log_threshold_set(cat, setting->thresh); - xbt_dynar_cursor_rm(xbt_log_settings,&cursor); + while (1) { + xbt_log_appender_t appender = cat->appender; - if (cat->threshold <= xbt_log_priority_verbose) { - s_xbt_log_event_t _log_ev = - {cat,xbt_log_priority_verbose,__FILE__,_XBT_GNUC_FUNCTION,__LINE__}; - _xbt_log_event_log(&_log_ev, - "Apply settings for category '%s': set threshold to %s (=%d)", - cat->name, - xbt_log_priority_names[cat->threshold], cat->threshold); + if (appender != NULL) { + xbt_assert(cat->layout, "No valid layout for the appender of category %s", cat->name); + + /* First, try with a static buffer */ + int done = 0; + char buff[XBT_LOG_STATIC_BUFFER_SIZE]; + ev->buffer = buff; + ev->buffer_size = sizeof buff; + va_start(ev->ap, fmt); + done = cat->layout->do_layout(cat->layout, ev, fmt); + va_end(ev->ap); + if (done) { + appender->do_append(appender, buff); + } else { + + /* The static buffer was too small, use a dynamically expanded one */ + ev->buffer_size = XBT_LOG_DYNAMIC_BUFFER_SIZE; + ev->buffer = xbt_malloc(ev->buffer_size); + while (1) { + va_start(ev->ap, fmt); + done = cat->layout->do_layout(cat->layout, ev, fmt); + va_end(ev->ap); + if (done) + break; /* Got it */ + ev->buffer_size *= 2; + ev->buffer = xbt_realloc(ev->buffer, ev->buffer_size); + } + appender->do_append(appender, ev->buffer); + xbt_free(ev->buffer); } } + + if (!cat->additivity) + break; + cat = cat->parent; } - if (!found && cat->threshold <= xbt_log_priority_verbose) { - s_xbt_log_event_t _log_ev = - {cat,xbt_log_priority_verbose,__FILE__,_XBT_GNUC_FUNCTION,__LINE__}; - _xbt_log_event_log(&_log_ev, - "Category '%s': inherited threshold = %s (=%d)", - cat->name, - xbt_log_priority_names[cat->threshold], cat->threshold); - } +} +/* NOTE: + * + * The standard logging macros use _XBT_LOG_ISENABLED, which calls _xbt_log_cat_init(). Thus, if we want to avoid an + * infinite recursion, we can not use the standard logging macros in _xbt_log_cat_init(), and in all functions called + * from it. + * + * To circumvent the problem, we define the macro DISABLE_XBT_LOG_CAT_INIT() to hide the real _xbt_log_cat_init(). The + * macro has to be called at the beginning of the affected functions. + */ +static int fake_xbt_log_cat_init(xbt_log_category_t XBT_ATTRIB_UNUSED category, + e_xbt_log_priority_t XBT_ATTRIB_UNUSED priority) +{ + return 0; } +#define DISABLE_XBT_LOG_CAT_INIT() \ + int (*_xbt_log_cat_init)(xbt_log_category_t, e_xbt_log_priority_t) XBT_ATTRIB_UNUSED = fake_xbt_log_cat_init; -void _xbt_log_event_log( xbt_log_event_t ev, const char *fmt, ...) { - xbt_log_category_t cat = ev->cat; - va_start(ev->ap, fmt); - while(1) { - xbt_log_appender_t appender = cat->appender; - if (appender != NULL) { - appender->do_append(appender, ev, fmt); - } - if (!cat->willLogToParent) - break; +static void _xbt_log_cat_apply_set(xbt_log_category_t category, xbt_log_setting_t setting) +{ + DISABLE_XBT_LOG_CAT_INIT(); + if (setting->thresh != xbt_log_priority_uninitialized) { + xbt_log_threshold_set(category, setting->thresh); - cat = cat->parent; - } - va_end(ev->ap); + XBT_DEBUG("Apply settings for category '%s': set threshold to %s (=%d)", + category->name, xbt_log_priority_names[category->threshold], category->threshold); + } + + if (setting->fmt) { + xbt_log_layout_set(category, xbt_log_layout_format_new(setting->fmt)); + + XBT_DEBUG("Apply settings for category '%s': set format to %s", category->name, setting->fmt); + } + + if (setting->additivity != -1) { + xbt_log_additivity_set(category, setting->additivity); + + XBT_DEBUG("Apply settings for category '%s': set additivity to %s", + category->name, (setting->additivity ? "on" : "off")); + } + if (setting->appender) { + xbt_log_appender_set(category, setting->appender); + if (!category->layout) + xbt_log_layout_set(category, xbt_log_layout_simple_new(NULL)); + category->additivity = 0; + XBT_DEBUG("Set %p as appender of category '%s'", setting->appender, category->name); + } } -static void _cat_init(xbt_log_category_t category) { +/* + * This gets called the first time a category is referenced and performs the initialization. + * Also resets threshold to inherited! + */ +int _xbt_log_cat_init(xbt_log_category_t category, e_xbt_log_priority_t priority) +{ + DISABLE_XBT_LOG_CAT_INIT(); + if (log_cat_init_mutex != NULL) + xbt_os_mutex_acquire(log_cat_init_mutex); + + if (category->initialized) { + if (log_cat_init_mutex != NULL) + xbt_os_mutex_release(log_cat_init_mutex); + return priority >= category->threshold; + } + + unsigned int cursor; + xbt_log_setting_t setting = NULL; + + XBT_DEBUG("Initializing category '%s' (firstChild=%s, nextSibling=%s)", category->name, + (category->firstChild ? category->firstChild->name : "none"), + (category->nextSibling ? category->nextSibling->name : "none")); + if (category == &_XBT_LOGV(XBT_LOG_ROOT_CAT)) { category->threshold = xbt_log_priority_info; category->appender = xbt_log_default_appender; + category->layout = xbt_log_default_layout; } else { + if (!category->parent) + category->parent = &_XBT_LOGV(XBT_LOG_ROOT_CAT); + + XBT_DEBUG("Set %s (%s) as father of %s ", category->parent->name, + (category->parent->initialized ? xbt_log_priority_names[category->parent->threshold] : "uninited"), + category->name); xbt_log_parent_set(category, category->parent); + + if (XBT_LOG_ISENABLED(log, xbt_log_priority_debug)) { + char *buf; + char *res = NULL; + xbt_log_category_t cpp = category->parent->firstChild; + while (cpp) { + if (res) { + buf = bprintf("%s %s", res, cpp->name); + free(res); + res = buf; + } else { + res = xbt_strdup(cpp->name); + } + cpp = cpp->nextSibling; + } + + XBT_DEBUG("Children of %s: %s; nextSibling: %s", category->parent->name, res, + (category->parent->nextSibling ? category->parent->nextSibling->name : "none")); + + free(res); + } } - _apply_control(category); -} -/* - * This gets called the first time a category is referenced and performs the - * initialization. - * Also resets threshold to inherited! - */ -int _xbt_log_cat_init(e_xbt_log_priority_t priority, - xbt_log_category_t category) { - - _cat_init(category); - + /* Apply the control */ + if (xbt_log_settings) { + xbt_assert(category, "NULL category"); + xbt_assert(category->name); + int found = 0; + + xbt_dynar_foreach(xbt_log_settings, cursor, setting) { + xbt_assert(setting, "Damnit, NULL cat in the list"); + xbt_assert(setting->catname, "NULL setting(=%p)->catname", (void *) setting); + + if (!strcmp(setting->catname, category->name)) { + found = 1; + _xbt_log_cat_apply_set(category, setting); + xbt_dynar_cursor_rm(xbt_log_settings, &cursor); + } + } + + if (!found) + XBT_DEBUG("Category '%s': inherited threshold = %s (=%d)", + category->name, xbt_log_priority_names[category->threshold], category->threshold); + } + + category->initialized = 1; + if (log_cat_init_mutex != NULL) + xbt_os_mutex_release(log_cat_init_mutex); return priority >= category->threshold; } -void xbt_log_parent_set(xbt_log_category_t cat, - xbt_log_category_t parent) { +void xbt_log_parent_set(xbt_log_category_t cat, xbt_log_category_t parent) +{ + xbt_assert(cat, "NULL category to be given a parent"); + xbt_assert(parent, "The parent category of %s is NULL", cat->name); - xbt_assert0(cat,"NULL category to be given a parent"); - xbt_assert1(parent,"The parent category of %s is NULL",cat->name); + /* if the category is initialized, unlink from current parent */ + if (cat->initialized) { + xbt_log_category_t *cpp = &cat->parent->firstChild; - /* unlink from current parent */ - if (cat->threshold != xbt_log_priority_uninitialized) { - xbt_log_category_t* cpp = &parent->firstChild; - while(*cpp != cat && *cpp != NULL) { + while (*cpp != cat && *cpp != NULL) { cpp = &(*cpp)->nextSibling; } + xbt_assert(*cpp == cat); *cpp = cat->nextSibling; } - /* Set new parent */ cat->parent = parent; cat->nextSibling = parent->firstChild; + parent->firstChild = cat; - /* Make sure parent is initialized */ - if (parent->threshold == xbt_log_priority_uninitialized) { - _cat_init(parent); - } - - /* Reset priority */ + if (!parent->initialized) + _xbt_log_cat_init(parent, xbt_log_priority_uninitialized /* ignored */ ); + cat->threshold = parent->threshold; + cat->isThreshInherited = 1; -} /* log_setParent */ +} -static void _set_inherited_thresholds(xbt_log_category_t cat) { +static void _set_inherited_thresholds(xbt_log_category_t cat) +{ xbt_log_category_t child = cat->firstChild; - for( ; child != NULL; child = child->nextSibling) { + + for (; child != NULL; child = child->nextSibling) { if (child->isThreshInherited) { if (cat != &_XBT_LOGV(log)) - VERB3("Set category threshold of %s to %s (=%d)", - child->name,xbt_log_priority_names[cat->threshold],cat->threshold); + XBT_VERB("Set category threshold of %s to %s (=%d)", + child->name, xbt_log_priority_names[cat->threshold], cat->threshold); child->threshold = cat->threshold; _set_inherited_thresholds(child); } } } -void xbt_log_threshold_set(xbt_log_category_t cat, - e_xbt_log_priority_t threshold) { +void xbt_log_threshold_set(xbt_log_category_t cat, e_xbt_log_priority_t threshold) +{ cat->threshold = threshold; cat->isThreshInherited = 0; + _set_inherited_thresholds(cat); } -static void _xbt_log_parse_setting(const char* control_string, - xbt_log_setting_t set) { - const char *name, *dot, *eq; - - set->catname=NULL; - if (!*control_string) - return; - DEBUG1("Parse log setting '%s'",control_string); +static xbt_log_setting_t _xbt_log_parse_setting(const char *control_string) +{ + const char *orig_control_string = control_string; + xbt_log_setting_t set = xbt_new(s_xbt_log_setting_t, 1); + + set->catname = NULL; + set->thresh = xbt_log_priority_uninitialized; + set->fmt = NULL; + set->additivity = -1; + set->appender = NULL; + + if (!*control_string) + return set; + XBT_DEBUG("Parse log setting '%s'", control_string); control_string += strspn(control_string, " "); - name = control_string; - control_string += strcspn(control_string, ".= "); - dot = control_string; - control_string += strcspn(control_string, "= "); - eq = control_string; - control_string += strcspn(control_string, " "); + const char *name = control_string; + control_string += strcspn(control_string, ".:= "); + const char *dot = control_string; + control_string += strcspn(control_string, ":= "); + const char *eq = control_string; - xbt_assert1(*dot == '.' && *eq == '=', - "Invalid control string '%s'",control_string); + xbt_assert(*dot == '.' || (*eq != '=' && *eq != ':'), "Invalid control string '%s'", orig_control_string); - if (!strncmp(dot + 1, "thresh", min(eq - dot - 1,strlen("thresh")))) { + if (!strncmp(dot + 1, "threshold", (size_t) (eq - dot - 1))) { int i; - char *neweq=xbt_strdup(eq+1); - char *p=neweq-1; - + char *neweq = xbt_strdup(eq + 1); + char *p = neweq - 1; + while (*(++p) != '\0') { if (*p >= 'a' && *p <= 'z') { - *p-='a'-'A'; + *p -= 'a' - 'A'; } } - - DEBUG1("New priority name = %s",neweq); - for (i=0; ithresh=i; + + if(ithresh = (e_xbt_log_priority_t) i; + } else { + THROWF(arg_error, 0, + "Unknown priority name: %s (must be one of: trace,debug,verbose,info,warning,error,critical)", eq + 1); + } + free(neweq); + } else if (!strncmp(dot + 1, "add", (size_t) (eq - dot - 1)) || + !strncmp(dot + 1, "additivity", (size_t) (eq - dot - 1))) { + char *neweq = xbt_strdup(eq + 1); + char *p = neweq - 1; + + while (*(++p) != '\0') { + if (*p >= 'a' && *p <= 'z') { + *p -= 'a' - 'A'; + } + } + if (!strcmp(neweq, "ON") || !strcmp(neweq, "YES") || !strcmp(neweq, "1")) { + set->additivity = 1; + } else { + set->additivity = 0; + } + free(neweq); + } else if (!strncmp(dot + 1, "app", (size_t) (eq - dot - 1)) || + !strncmp(dot + 1, "appender", (size_t) (eq - dot - 1))) { + char *neweq = xbt_strdup(eq + 1); + + if (!strncmp(neweq, "file:", 5)) { + set->appender = xbt_log_appender_file_new(neweq + 5); + }else if (!strncmp(neweq, "rollfile:", 9)) { + set->appender = xbt_log_appender2_file_new(neweq + 9,1); + }else if (!strncmp(neweq, "splitfile:", 10)) { + set->appender = xbt_log_appender2_file_new(neweq + 10,0); } else { - xbt_assert1(FALSE,"Unknown priority name: %s",eq+1); + THROWF(arg_error, 0, "Unknown appender log type: '%s'", neweq); } - xbt_free(neweq); + free(neweq); + } else if (!strncmp(dot + 1, "fmt", (size_t) (eq - dot - 1))) { + set->fmt = xbt_strdup(eq + 1); } else { char buff[512]; - snprintf(buff,min(512,eq - dot - 1),"%s",dot+1); - xbt_assert1(FALSE,"Unknown setting of the log category: %s",buff); + snprintf(buff, MIN(512, eq - dot), "%s", dot + 1); + xbt_die("Unknown setting of the log category: '%s'", buff); } - set->catname=(char*)xbt_malloc(dot - name+1); - - strncpy(set->catname,name,dot-name); - set->catname[dot-name]='\0'; /* Just in case */ - DEBUG1("This is for cat '%s'", set->catname); + set->catname = (char *) xbt_malloc(dot - name + 1); + + memcpy(set->catname, name, dot - name); + set->catname[dot - name] = '\0'; /* Just in case */ + XBT_DEBUG("This is for cat '%s'", set->catname); + + return set; } -static xbt_error_t _xbt_log_cat_searchsub(xbt_log_category_t cat,char *name, - /*OUT*/xbt_log_category_t*whereto) { - xbt_error_t errcode; +static xbt_log_category_t _xbt_log_cat_searchsub(xbt_log_category_t cat, char *name) +{ xbt_log_category_t child; - - if (!strcmp(cat->name,name)) { - *whereto=cat; - return no_error; + xbt_log_category_t res; + + XBT_DEBUG("Search '%s' into '%s' (firstChild='%s'; nextSibling='%s')", name, + cat->name, (cat->firstChild ? cat->firstChild->name : "none"), + (cat->nextSibling ? cat->nextSibling->name : "none")); + if (!strcmp(cat->name, name)) + return cat; + + for (child = cat->firstChild; child != NULL; child = child->nextSibling) { + XBT_DEBUG("Dig into %s", child->name); + res = _xbt_log_cat_searchsub(child, name); + if (res) + return res; } - for(child=cat->firstChild ; child != NULL; child = child->nextSibling) { - errcode=_xbt_log_cat_searchsub(child,name,whereto); - if (errcode==no_error) - return no_error; - } - return mismatch_error; -} -static void _cleanup_double_spaces(char *s) { - char *p = s; - int e = 0; - - while (1) { - if (!*p) - goto end; - - if (!isspace(*p)) - break; - - p++; - } - - e = 1; - - do { - if (e) - *s++ = *p; - - if (!*++p) - goto end; - - if (e ^ !isspace(*p)) - if ((e = !e)) - *s++ = ' '; - } while (1); - - end: - *s = '\0'; + return NULL; } /** - * \ingroup XBT_log + * \ingroup XBT_log * \param control_string What to parse * * Typically passed a command-line argument. The string has the syntax: * - * ( [category] "." [keyword] "=" value (" ")... )... - * - * where [category] is one the category names and keyword is one of the - * following: + * ( [category] "." [keyword] ":" value (" ")... )... * - * thresh value is an integer priority level. Sets the category's - * threshold priority. + * where [category] is one the category names (see \ref XBT_log_cats for a complete list of the ones defined in the + * SimGrid library) and keyword is one of the following: * - * \warning - * This routine may only be called once and that must be before any other - * logging command! Typically, this is done from main(). + * - thres: category's threshold priority. Possible values: + * TRACE,DEBUG,VERBOSE,INFO,WARNING,ERROR,CRITICAL + * - add or additivity: whether the logging actions must be passed to the parent category. + * Possible values: 0, 1, no, yes, on, off. + * Default value: yes. + * - fmt: the format to use. See \ref log_use_conf_fmt for more information. + * - app or appender: the appender to use. See \ref log_use_conf_app for more information. */ -void xbt_log_control_set(const char* control_string) { - xbt_error_t errcode; +void xbt_log_control_set(const char *control_string) +{ xbt_log_setting_t set; - char *cs; - char *p; - int done = 0; - - DEBUG1("Parse log settings '%s'",control_string); - if (control_string == NULL) + + /* To split the string in commands, and the cursors */ + xbt_dynar_t set_strings; + char *str; + unsigned int cpt; + + if (!control_string) return; + XBT_DEBUG("Parse log settings '%s'", control_string); + + /* Special handling of no_loc request, which asks for any file localization to be omitted (for tesh runs) */ + if (!strcmp(control_string, "no_loc")) { + xbt_log_no_loc = 1; + return; + } + /* some initialization if this is the first time that this get called */ if (xbt_log_settings == NULL) - xbt_log_settings = xbt_dynar_new(sizeof(xbt_log_setting_t), - _free_setting); + xbt_log_settings = xbt_dynar_new(sizeof(xbt_log_setting_t), _free_setting); - set = xbt_new(s_xbt_log_setting_t,1); - cs=xbt_strdup(control_string); + /* split the string, and remove empty entries */ + set_strings = xbt_str_split_quoted(control_string); - _cleanup_double_spaces(cs); + if (xbt_dynar_is_empty(set_strings)) { /* vicious user! */ + xbt_dynar_free(&set_strings); + return; + } - while (!done) { - xbt_log_category_t cat; - - p=strrchr(cs,' '); - if (p) { - *p='\0'; - *p++; - } else { - p=cs; - done = 1; - } - _xbt_log_parse_setting(p,set); - - errcode = _xbt_log_cat_searchsub(&_XBT_LOGV(root),set->catname,&cat); - if (errcode == mismatch_error) { - DEBUG0("Store for further application"); - DEBUG1("push %p to the settings",(void*)set); - xbt_dynar_push(xbt_log_settings,&set); - /* malloc in advance the next slot */ - set = xbt_new(s_xbt_log_setting_t,1); + /* Parse each entry and either use it right now (if the category was already created), or store it for further use */ + xbt_dynar_foreach(set_strings, cpt, str) { + set = _xbt_log_parse_setting(str); + xbt_log_category_t cat = _xbt_log_cat_searchsub(&_XBT_LOGV(XBT_LOG_ROOT_CAT), set->catname); + + if (cat) { + XBT_DEBUG("Apply directly"); + _xbt_log_cat_apply_set(cat, set); + _free_setting((void *) &set); } else { - DEBUG0("Apply directly"); - xbt_free(set->catname); - xbt_log_threshold_set(cat,set->thresh); + XBT_DEBUG("Store for further application"); + XBT_DEBUG("push %p to the settings", (void *) set); + xbt_dynar_push(xbt_log_settings, &set); } } - xbt_free(set); - xbt_free(cs); -} + xbt_dynar_free(&set_strings); +} -void xbt_log_appender_set(xbt_log_category_t cat, xbt_log_appender_t app) { +void xbt_log_appender_set(xbt_log_category_t cat, xbt_log_appender_t app) +{ + if (cat->appender) { + if (cat->appender->free_) + cat->appender->free_(cat->appender); + free(cat->appender); + } cat->appender = app; } +void xbt_log_layout_set(xbt_log_category_t cat, xbt_log_layout_t lay) +{ + DISABLE_XBT_LOG_CAT_INIT(); + if (!cat->appender) { + XBT_VERB ("No appender to category %s. Setting the file appender as default", cat->name); + xbt_log_appender_set(cat, xbt_log_appender_file_new(NULL)); + } + if (cat->layout) { + if (cat->layout->free_) { + cat->layout->free_(cat->layout); + } + free(cat->layout); + } + cat->layout = lay; + xbt_log_additivity_set(cat, 0); +} + +void xbt_log_additivity_set(xbt_log_category_t cat, int additivity) +{ + cat->additivity = additivity; +} + +static void xbt_log_help(void) +{ + printf("Description of the logging output:\n" + "\n" + " Threshold configuration: --log=CATEGORY_NAME.thres:PRIORITY_LEVEL\n" + " CATEGORY_NAME: defined in code with function 'XBT_LOG_NEW_CATEGORY'\n" + " PRIORITY_LEVEL: the level to print (trace,debug,verbose,info,warning,error,critical)\n" + " -> trace: enter and return of some functions\n" + " -> debug: crufty output\n" + " -> verbose: verbose output for the user wanting more\n" + " -> info: output about the regular functioning\n" + " -> warning: minor issue encountered\n" + " -> error: issue encountered\n" + " -> critical: major issue encountered\n" + " The default priority level is 'info'.\n" + "\n" + " Format configuration: --log=CATEGORY_NAME.fmt:FORMAT\n" + " FORMAT string may contain:\n" + " -> %%%%: the %% char\n" + " -> %%n: platform-dependent line separator (LOG4J compatible)\n" + " -> %%e: plain old space (SimGrid extension)\n" + "\n" + " -> %%m: user-provided message\n" + "\n" + " -> %%c: Category name (LOG4J compatible)\n" + " -> %%p: Priority name (LOG4J compatible)\n" + "\n" + " -> %%h: Hostname (SimGrid extension)\n" + " -> %%P: Process name (SimGrid extension)\n" + " -> %%t: Thread \"name\" (LOG4J compatible -- actually the address of the thread in memory)\n" + " -> %%i: Process PID (SimGrid extension -- this is a 'i' as in 'i'dea)\n" + "\n" + " -> %%F: file name where the log event was raised (LOG4J compatible)\n" + " -> %%l: location where the log event was raised (LOG4J compatible, like '%%F:%%L' -- this is a l as " + "in 'l'etter)\n" + " -> %%L: line number where the log event was raised (LOG4J compatible)\n" + " -> %%M: function name (LOG4J compatible -- called method name here of course).\n" + " Defined only when using gcc because there is no __FUNCTION__ elsewhere.\n" + "\n" + " -> %%b: full backtrace (Called %%throwable in LOG4J). Defined only under windows or when using the " + "GNU libc because\n" + " backtrace() is not defined elsewhere, and we only have a fallback for windows boxes, not " + "mac ones for example.\n" + " -> %%B: short backtrace (only the first line of the %%b). Called %%throwable{short} in LOG4J; " + "defined where %%b is.\n" + "\n" + " -> %%d: date (UNIX-like epoch)\n" + " -> %%r: application age (time elapsed since the beginning of the application)\n" + "\n" + " Miscellaneous:\n" + " --help-log-categories Display the current hierarchy of log categories.\n" + " --log=no_loc Don't print file names in messages (for tesh tests).\n" + "\n"); +} + +static int xbt_log_cat_cmp(const void *pa, const void *pb) +{ + xbt_log_category_t a = *(xbt_log_category_t *)pa; + xbt_log_category_t b = *(xbt_log_category_t *)pb; + return strcmp(a->name, b->name); +} + +static void xbt_log_help_categories_rec(xbt_log_category_t category, const char *prefix) +{ + char *this_prefix; + char *child_prefix; + unsigned i; + xbt_log_category_t cat; + + if (!category) + return; + + if (category->parent) { + this_prefix = bprintf("%s \\_ ", prefix); + child_prefix = bprintf("%s | ", prefix); + } else { + this_prefix = xbt_strdup(prefix); + child_prefix = xbt_strdup(prefix); + } + + xbt_dynar_t dynar = xbt_dynar_new(sizeof(xbt_log_category_t), NULL); + for (cat = category ; cat != NULL; cat = cat->nextSibling) + xbt_dynar_push_as(dynar, xbt_log_category_t, cat); + + xbt_dynar_sort(dynar, xbt_log_cat_cmp); + + xbt_dynar_foreach(dynar, i, cat){ + if (i == xbt_dynar_length(dynar) - 1 && category->parent) + *strrchr(child_prefix, '|') = ' '; + printf("%s%s: %s\n", this_prefix, cat->name, cat->description); + xbt_log_help_categories_rec(cat->firstChild, child_prefix); + } + + xbt_dynar_free(&dynar); + xbt_free(this_prefix); + xbt_free(child_prefix); +} + +static void xbt_log_help_categories(void) +{ + printf("Current log category hierarchy:\n"); + xbt_log_help_categories_rec(&_XBT_LOGV(XBT_LOG_ROOT_CAT), " "); + printf("\n"); +}