{"id":17932416,"url":"https://github.com/acolomb/libdebugmod","last_synced_at":"2025-06-24T23:46:15.424Z","repository":{"id":16193927,"uuid":"18940602","full_name":"acolomb/libdebugmod","owner":"acolomb","description":"Flexible debug messages for small and embedded applications","archived":false,"fork":false,"pushed_at":"2024-05-06T14:22:17.000Z","size":87,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-06-24T23:45:27.569Z","etag":null,"topics":["debugging","embedded-applications","library","logging"],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/acolomb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-04-19T12:25:09.000Z","updated_at":"2024-05-06T14:22:21.000Z","dependencies_parsed_at":"2024-12-16T07:21:43.181Z","dependency_job_id":"a671ad37-7fba-4ab5-8713-124a0383a830","html_url":"https://github.com/acolomb/libdebugmod","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/acolomb/libdebugmod","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acolomb%2Flibdebugmod","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acolomb%2Flibdebugmod/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acolomb%2Flibdebugmod/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acolomb%2Flibdebugmod/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/acolomb","download_url":"https://codeload.github.com/acolomb/libdebugmod/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acolomb%2Flibdebugmod/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261777041,"owners_count":23208099,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["debugging","embedded-applications","library","logging"],"created_at":"2024-10-28T21:27:12.895Z","updated_at":"2025-06-24T23:46:15.367Z","avatar_url":"https://github.com/acolomb.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"\nlibdebugmod - Debug Module Library\n==================================\n\n*libdebugmod* is a very small C library to easily manage debug output\nin console or embedded applications.\n\nAuthor: André Colomb \u003csrc@andre.colomb.de\u003e\n\nLicense\n-------\n\nCopyright (C) 2014  André Colomb\n\nlibdebugmod is free software: you can redistribute it and/or modify it\nunder the terms of the GNU Lesser General Public License as published\nby the Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nlibdebugmod is distributed in the hope that it will be useful, but\nWITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\nLesser General Public License for more details.\n\nYou should have received a copy of the GNU Lesser General Public\nLicense along with this program.  If not, see\n\u003chttp://www.gnu.org/licenses/\u003e.\n\n\nMotivation\n----------\n\nSmall programs, especially on embedded microcontrollers, often have\ndifferent needs for runtime debugging output than large-scale desktop\nor server applications, where minimal runtime overhead or compiled\nbinary size is less of a concern.  Outputting debug messages during\nthe program's execution is often the most valuable source of\ninformation for developers.\n\nThe traditional approach for controlling the level of debug verbosity\nis to assign each possible message a severity level and then filter\nout everything above a certain, runtime-configured level.  This level\ncan even be limited to a lower value for production builds with\nsmaller binary size and better runtime performance.\n\nWith *libdebugmod*, messages are instead enabled per translation unit\n(called a code **module** in this context).  A translation unit is\ntypically one source file (.c or .cpp), or whatever the compiler\ntreats as the scope of a `static` variable.  This is coupled with a\ndecentralized architecture, where no central component needs to have a\nlist of all subsystems.  Referencing modules via hard-coded string\nidentifiers makes it possible to still easily control the verbosity\nduring runtime, for example through text commands or a console.\n\nThe overhead is kept minimal and configurable to allow small\nexecutable sizes on embedded platforms.  Production builds are easily\nstripped of _almost_ all debugging code (see \"Limitations\" below).  In\ncontrast to traditional approaches, where debug output code is hidden\nby the preprocessor conditions, *libdebugmod* keeps the code visible\nto the compiler.  Such debugging code is less likely to break during\nrestructuring and leads to compilation errors before releasing broken\ndebug code which the compiler has never had a chance to validate.\n\n\n### Features ###\n+ Standard C streams as I/O descriptors make logging to files or\n  console easy.\n+ Suitable for embedded systems, leaving the choice of output function\n  to the user (`fprintf()` not mandatory).\n+ No wrapper functions, allowing the compiler to e.g. validate printf\n  format strings.\n+ Zero runtime overhead for production builds.\n+ Enable / disable debug output for individual modules at runtime.\n+ Minimal runtime overhead when debugging is disabled.\n+ Decentralized architecture for independent code module subsystems.\n+ Function context is provided for each debug output.\n+ Complete control over output prefixes via callback function.\n+ Debugging code is always compiled and therefore consistent with\n  other code changes (e.g. renamed variables).\n+ Small implementation with optional features.\n\n\n### Limitations ###\n- Relies on optimizing compiler for zero-overhead production builds\n- Slight binary size overhead for strings used as module identifier or\n  function context (compiler-dependent)\n- Not thread-safe\n\n\nInstallation\n------------\n\n### Requirements ###\n\nThere are no required dependencies for building the library.  Since\nthe code uses features from the C99 language standard, the compiler\nmust support this standard and it might have to be explicitly enabled\nwith a command line switch (e.g. `-std=c99`).\n\nThe source code and interfaces are well documented and prepared to\ngenerate documentation with [Doxygen][1].  A handy configuration named\n`Doxyfile` is included.\n\n[1]: http://www.doxygen.org/ \"Doxygen home page\"\n\n\n### Compilation ###\n\nA simple Makefile is included under the `src` directory, compatible at\nleast with GNU make.  It contains rules to create the static library\nand a test program demonstrating the API.  In addition, the `dump`\ntarget outputs a disassembly of the test program to verify the library\noverhead.\n\nThe number of debug modules which can be registered in a program is\nlimited during compilation of `debug_mod.c`.  This maximum can be\nchecked through the variable `debug_mod_max`.  To override it, define\nthe `DEBUG_MOD_MAX` macro to a numeric value during compilation.  The\ndefault is **four** modules.\n\nBy default, compilation is done with the build machine's standard\ntools like `cc` and `objdump`.  To test the library with this default\ntoolchain, use the `host` target:\n\n\tmake host\n\nAs another example usage, the `avr` target provides a convenient way\nto cross-build using the [avr-gcc toolchain][2] and then dump the\nresulting disassembly for inspection:\n\n\tmake avr\n\nTo use the library in your own code, there are two possible build\nscenarios.  Either way, the `include` directory should be added to the\npreprocessor include path for your project.\n\n[2]: http://www.nongnu.org/avr-libc/ \"AVR Libc, for use with avr-gcc\"\n\n\n#### Variant A: Using the Static Library ####\n\nWhen building a native host application or if you just want a binary\nfile for use in projects, make the `lib` target in the provided\nMakefile.  The standard variables from the environment or provided on\nthe command line can control the toolchain options (CC, LD, CPPFLAGS,\nCFLAGS, LDFLAGS). Example:\n\n\tmake -C libdebugmod/src/ CFLAGS=\"-g -O1\" clean lib\n\nMake sure to also configure optional library API features during this\nstep, as explained under the section \"Runtime management API\" below.\nThe built static library can then be regularly linked to your project\nby providing the linker with a library path:\n\n\tcc -Ilibdebugmod/include/ -Llibdebugmod/src/ \\\n\t\tmyproject.c -ldebugmod -o myproject\n\nOr by directly referencing the archive file:\n\n\tcc -Ilibdebugmod/include/ \\\n\t\tmyproject.c libdebugmod/src/libdebugmod.a -o myproject\n\n\n#### Variant B: Direct Linking ####\n\nIf the library source resides within your project directory, it may be\neasier to just include the source file for the API functions in your\nbuild process.  This way, your toolchain configuration is always\nconsistent with the *libdebugmod* build.  Keep in mind that a C99 mode\ncompiler is required, though.\n\n\tcc -Ilibdebugmod/include/ \\\n\t\tmyproject.c libdebugmod/src/debug_mod.c -o myproject\n\nIf your project uses a Makefile, simply add the include path to your\n`CPPFLAGS` variable, then add `libdebugmod/src/debug_mod.c` to the\nsource files.  Example Makefile excerpt:\n\n\tDEBUGMOD_DIR = libdebugmod\n\tCPPFLAGS += -I$(DEBUGMOD_DIR)/include\n\tmyproject: myproject.c $(DEBUGMOD_DIR)/src/debug_mod.c\n\n\nBasic API for Debug Output\n--------------------------\n\nTo employ any *libdebugmod* functionality in one of your code modules,\nyou need to:\n\n~~~~~~~~~~~~~{c}\n\n\t#define DEBUG_MOD_ENABLE\t// This is optional\n\t#include \u003cdebug_mod.h\u003e\n\tDEBUG_MOD_INIT(\"my module\")\n~~~~~~~~~~~~~\n\nWithout the `DEBUG_MOD_ENABLE` macro, the debug output code is still\nvisible to the compiler, but will be removed by the optimizer.  In\npractice, it may be easier to define the `DEBUG_MOD_ENABLE` macro via\ncommand line flags to the preprocessor during a debug build instead of\nhard-coding it into each source file.\n\nThe `debug_mod.h` header file contains most of the API declaration and\nprovides the magic to separate debug channels for different\ntranslation units.  It must be included once per translation unit.\nJust look at its contents or the generated Doxygen documentation for\ndetails.\n\nThe `DEBUG_MOD_INIT()` macro must also be used once per translation\nunit, before any debug output.  It defines the static storage used to\nidentify and register the current code module.  Lazy people with a\ngood C preprocessor may use the magic constant `__FILE__` for the\nmodule identifier:\n\n\tDEBUG_MOD_INIT(__FILE__)\n\nActual debug output is most conveniently generated with one of the\nmacros `DEBUGF()` and `DEBUGL()`.  They are designed to provide great\nflexibility around the stream I/O functions from the C Standard\nLibrary, namely `fprintf()`, `fputs()`, `fputc()`, etc.  Your average\ndebug message may look like this:\n\n~~~~~~~~~~~~~{c}\n\n\tfprintf(stderr, \"Houston, we have %d problems.\", count);\n\tfputc('\\n', stderr);\n~~~~~~~~~~~~~\n\nWith *libdebugmod*, the calls should look like this:\n\n~~~~~~~~~~~~~{c}\n\n\tDEBUGF(fprintf, \"Houston, we have %d problems.\", count);\n\tDEBUGL(fputc, '\\n');\n~~~~~~~~~~~~~\n\nThe `DEBUGF()` and `DEBUGL()` macros expand to a code block which\n\n1. Is optimized away when `DEBUG_MOD_ENABLE` was not defined.\n2. Evaluates a configured callback function to decide whether debug\n   output is desired for the current module.\n3. Calls the given output function with the configured stream as first\n   or last argument, respectively.\n4. Passes all remaining arguments to the output function verbatim.\n\nThe first two steps above can also be reused for code other than debug\noutput, for example to open a log file only when debugging is enabled.\nThe `DEBUG_CONDITION` macro expands to an `if (...)` statement with\nthe described behaviour.\n\nThe callback function serves two main purposes: a) Controlling whether\nthe specific debug statement should be executed by returning a\nnon-zero value and b) \"preparing\" the debug output, such as writing a\nprefix (timestamp, etc.) to the output stream.  In addition to the\ncurrent module's debug configuration, it is passed a string describing\nthe calling context.  By default, the `__func__` magic preprocessor\nmacro is used, but can be overridden setting `DEBUG_MOD_CONTEXT` to\nany expression evaluating to a string at runtime.\n\n\nRuntime Management API\n----------------------\n\nThe library allows to flexibly reconfigure each module at run-time.\nThe callback function deciding whether to execute the debug statements\ncan be exchanged, or entirely suppressed.  Switching the output stream\npassed in `DEBUGF()` and `DEBUGL()` statements is also supported.\n\nThese configuration changes can be done from within each module for\nits own settings, as well as through a module-external API to allow\nfor centralized management of debug facilities by the user.\n\n\n### Self-Configuration Within Modules ###\n\nEach module's own current output stream can be accessed directly\nthrough `debug_mod_get_stream()` and modified using\n`debug_mod_set_stream()`.  The default output stream at initialization\nis always `stderr`.\n\nInitially, each module's configuration is lazily initialized to use\nthe callback function address stored in `debug_mod_default_func`.  Set\nthe latter during early program initialization to provide a default\ncallback instead of disabling all debug modules.\n\nReplacing the callback function is done via `debug_mod_set_func()`,\nwhere a function pointer of type `debug_mod_f` must be passed in.\nSetting the callback to `NULL` disables debugging output for this\nmodule altogether, which is equivalent to `debug_mod_disable_self()`.\n\nIn some setups, a central module list may not be desired, but at some\npoint in time a complete enumeration of all registered modules is\nneeded.  However, the lazy initialization only happens upon the first\nevaluation of the `DEBUG_CONDITION`, which implies all side-effects of\nthe respective callback function.  To avoid these, use\n`debug_mod_register_self()` within each module, forcing registration\nearly in the program startup.  The `incremental_string_search.c` demo\nprogram (explained below) includes such a usecase.\n\n\n### External Reconfiguration API ###\n\nOther modules can control the debug output experience using the API\nprovided in `debug_mod_control.h`.  To access the respective API, you\nneed to:\n\n~~~~~~~~~~~~~{c}\n\n\t#define DEBUG_MOD_DYNAMIC\t// This is optional (see below)\n\t#define DEBUG_MOD_SAVE\t\t// This is optional (see below)\n\t#include \u003cdebug_mod_control.h\u003e\n~~~~~~~~~~~~~\n\nThis header file also pulls in the standard `debug_mod.h` header, so\nthe basic API for debug output can be used regularly after\ninitialization with `DEBUG_MOD_INIT()` (see above).  In addition the\nfunction `debug_mod_register()` is declared and can be used to\nconfigure a debug module in advance.\n\nEach module's configuration is lazily initialized during the first\nevaluation of the `DEBUG_CONDITION`, for example when first reaching a\n`DEBUGF()` call.  At this time, the `debug_mod_init()` function\narranges two things:\n\n  1. Make sure the module's identifier is registered in a central list\n     using `debug_mod_register()`.\n  2. Apply the `debug_mod_default_func` address (typically `NULL`),\n     unless the module has already been registered, in which case the\n     pre-recorded configuration is used.\n\nThis logic allows to enumerate modules and their individual settings\nfrom a central place using `debug_mod_register()` during program\ninitialization, like in the following example.\n\n~~~~~~~~~~~~~{c}\n\n\t// Example output preparation: Prefix with function context\n\tchar cb_context(debug_mod* self, const char* context) {\n\t\tfputs(context, self-\u003estream);\n\t\tfputs(\"(): \", self-\u003estream);\n\t\treturn 1;\n\t}\n\n\tint main(void) {\n\t\t// Enable debug output to stderr by default for any module\n\t\tdebug_mod_default_func = cb_context;\n\n\t\t// Override a specific module's default stream\n\t\tdebug_mod dm = { cb_context, stdout, \"my module\" };\n\t\tdebug_mod_register(\u0026dm);\n\n\t\t/* main program logic */\n\n\t\treturn 0;\n\t}\n~~~~~~~~~~~~~\n\n\n### Dynamic Module Reconfiguration (optional) ###\n\nThis part of the control API is optional and only available if the\nmacro `DEBUG_MOD_DYNAMIC` was defined before including\n`debug_mod_control.h`.  Remember to arrange this also when building\nthe static library, for example:\n\n\tmake -C libdebugmod/src/ CPPFLAGS=-DDEBUG_MOD_DYNAMIC clean lib\n\nAdditional functions exposed are `debug_mod_update()` and\n`debug_mod_disable()`.  In contrast to `debug_mod_register()`, they\ncan be used to alter modules' configurations anytime _after_ they have\nbeen registered and used, from outside the affected module.\n\n~~~~~~~~~~~~~{c}\n\n\t// Update callback and stream for all known modules\n\tdebug_mod_update(NULL, cb_context, stdout);\n\t// Disable a single module's output again:\n\tdebug_mod_disable(\"my module\");\n\t// which is equivalent to:\n\tdebug_mod_update(\"my module\", NULL, NULL);\n~~~~~~~~~~~~~\n\nSpecifying a `NULL` value for the module identifier applies the call\nto all modules that have already been registered (explicitly or\nautomatically).\n\n\n### Save and Restore Configuration (optional) ###\n\nThis part of the control API is optional and only available if the\nmacro `DEBUG_MOD_SAVE` was defined before including\n`debug_mod_control.h`.  Remember to arrange this also when building\nthe static library, for example:\n\n\tmake -C libdebugmod/src/ CPPFLAGS=-DDEBUG_MOD_SAVE clean lib\n\nAdditional functions exposed are:\n- `debug_mod_list()` to iterate through all known modules\n- `debug_mod_save()` to conveniently save the current modules'\n  configuration to an array\n- `debug_mod_restore()` to apply a previously stored configuration\n\n~~~~~~~~~~~~~{c}\n\n\tdebug_mod my_config[debug_mod_max];\t// Note the maximum size\n\tdebug_mod_save(my_config, sizeof(my_config) / sizeof(*my_config));\n\t// \"my_config\" may be copied to non-volatile memory now\n\tmemcpy(wherever, my_config, sizeof(my_config);\n\n\t// ... during next run, load my_config again, then:\n\tdebug_mod_restore(my_config, sizeof(my_config) / sizeof(*my_config));\n~~~~~~~~~~~~~\n\n\nDemo Programs\n-------------\n\nThe source code includes a small demo program illustrating all of the\nfeatures named above.  Inside the `src/` directory, look at the files\n`test_debug_mod.c` and `test_ext_module.c`.  The `Makefile` includes a\ntarget to compile and run this demo using the host toolchain.  Note\nthat the preprocessor macros to enable features **must be defined\nduring this compilation step**.\n\n\tmake -C libdebugmod/src/ \\\n\t\tCPPFLAGS=\"-DDEBUG_MOD_ENABLE -DDEBUG_MOD_DYNAMIC -DDEBUG_MOD_SAVE\" \\\n\t\tclean test\n\nTwo compilation units are tested, using the preprocessor's special\n`__FILE__` macro for the module identifier.  The `test_debug_mod.c`\nmodule contains different example implementations for setup functions,\nprepending the stream output with varying context information.\n\nThe test calls for API functions are all documented and use the\noptional API if enabled.  The `main()` function demonstrates debug\noutput from an external module and its own, with various configuration\nchanges in between.\n\nAnother example lives in `test_incremental_search.c` and shows a more\nsophisticated usage of the runtime management API.  It implements a\nrather efficient incremental string search algorithm to match a\n(partial) input against the list of registered debug modules,\nexercising the `debug_mod_list()` function.  Use the following\n`Makefile` target to run the example code with appropriate macro\ndefinitions already included.\n\n\tmake -C libdebugmod/src/ clean test-search\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Facolomb%2Flibdebugmod","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Facolomb%2Flibdebugmod","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Facolomb%2Flibdebugmod/lists"}