On 26/07/17 11:15, pozz wrote:
> Il 24/07/2017 09:40, David Brown ha scritto:
>> On 20/07/17 23:14, Stef wrote:
>>> On 2017-07-20 pozz wrote in comp.arch.embedded:
>>>> I usually prints some messages on a free UART used as a debug port.
>>>> They are very useful during debugging to understand the flow of
>>>> execution.
>>>>
>>>> Most of the time, I don't compile the debug log facility in release
>>>> build.
>>>>
>>>> I sometimes need to disable messages from some modules and enable
>>>> messages from other modules under debugging.
>>>> Moreover I sometimes need to enable/disable less important messages.
>>>>
>>>> Do you suggest some efficient implementation of debug log macros that
>>>> include all the features above?
>>>
>>> If I have a output stream available for this purpose, I often use the
>>> following (assuming you use C):
>>>
>>> #ifdef DEBUG_ENABLE
>>> #define DEBUG_PRINTF(level, ...) ((level) <= (DebugLevel) ?
>>> fprintf(stderr, __VA_ARGS__) : (level))
>>> #else
>>> #define DEBUG_PRINTF(level, ...)
>>>
>>> Call like a normal printf, except there is an initial parameter for the
>>> level of that paticular call.
>>>
>>> Lower the value of 'DebugLevel' to reduce the output.
>>>
>>
>> Yes, something like that is the way to do it. That means in the code
>> itself, you avoid having any extra "#ifdef DEBUG_ENABLE" conditional
>> compilation that /really/ messes up the readability of the code. You
>> just use DEBUG_PRINTF, safe in the knowledge that it does nothing when
>> debugging is disabled.
>>
>> The only unpleasant thing about this method is that if you have local
>> data that is only used for debugging, you are going to get warnings
>> about it when you disable debugging. Since I like to have most of my
>> compiler warnings enabled, and like my code to be warning-free when
>> compiled, this bugs me.
>>
>> So I have this definition:
>>
>> static inline int emptyFunctionX(int x, ...) { (void) x; return 0; }
>> #define emptyFunction(...) emptyFunctionX(0, ## __VA_ARGS__)
>>
>> Then when disabling debugging, I have:
>>
>> #define debugPrintf emptyFunction
>>
>> (I don't like defining a function as an expression in the way you did,
>> and I hate all-caps macro names for constants or function-like macros.)
>>
>>
>> "emptyFunction" uses a gcc extension here, but that's a common compiler
>> for many people. It swallows any arguments without generating code, and
>> without requiring the arguments to be calculated at all (assuming you
>> have optimisation enabled), but considers the arguments "used" as far as
>> warnings are concerned.
>
> As usual, thanks for the interesting tricks, David.
>
>
> If I understood correctly:
>
> #include "debug.h" // Where is debugPrintf, emptyFunction, ...
> static int var_used_only_in_debug;
> ...
> debug_printf(10, "The value is %d\n", var_used_only_in_debug);
>
> The compilation doesn't emit warnings (regarding unused static
> variables) and doesn't really allocate space for var_used_only_in_debug
> (thanks to optimization).
>
Yes.
In most such debug calls, you are just showing existing data, so it
makes little difference. But sometimes you might want to manipulate
something or make new local data before printing it.
>
> This should work for functions too:
>
> #include "debug.h"
> static const char *fun_used_only_in_debug(int arg);
> ...
> debug_printf(10, "The name is %s\n", fun_used_only_in_debug(value));
> ...
> static const char *fun_used_only_in_debug(int arg) {
> if (arg == 3) return "Three";
> if (arg == 3) return "Four";
> ...
> }
>
> In this case, the function isn't included in the output file (thanks to
> optimization).
Yes.
Note that this will only apply if the compiler knows it is safe to skip
the function call - either because it has the definition of the function
on hand and can see it (such as in this case), or because you have told
it is safe (such as by using the "const" or "pure" function attributes
in gcc).
>
>
> I have another situation. I want to include a member in a structure, but
> it's only used in debug. This member is used only in debug_printf calls,
> so it can be omitted in the Release build. If I use #ifdef DEBUG_ENABLE
> #endif to avoid including the member, I will have an error during build
> (because debug_print refers to a member that doesn't exist).
> If I leave the member in the structure in Release build, I will waste
> some space.
>
> Is there a trick to solve this problem?
That is a very interesting question. I hadn't thought about this
before, but I've got an answer for you:
#define enableDebugging
static inline int emptyFunctionX(int x, ...) { (void) x; return 0; }
#define emptyFunction(...) emptyFunctionX(0, ## __VA_ARGS__)
typedef struct {} empty_t;
#ifdef enableDebugging
extern int debugPrintf(const char *format, ...)
__attribute__ ((format (printf, 1, 2)));
#define debugField(T) T
#else
#define debugPrintf emptyFunction
#define debugField(T) empty_t
#endif
typedef struct {
int a;
int b;
debugField(int) debug;
} data_t;
data_t data;
int sizeofData(void) {
return sizeof(data_t);
}
int foo(void) {
data.a += data.b;
debugPrintf("%i %i %i", data.a, data.b, data.debug);
return data.a;
}
I tested this using the online compiler at <https://godbolt.org>. Copy
and paste it in, using whatever compiler version you like (gcc or clang,
because of the __attribute__).
Use "-Wall -Wextra -std=c11 -O1 -x c" as the command line options ("-x
c" makes gcc compile C, rather than the default C++ for the tools used
by the site).
You can see the real size of the "data_t" struct from the assembly code
generated for "sizeofData" - a field of type "empty_t" takes no space,
but avoids the error when it is used in the disabled debugPrintf call.
However, note that a struct with no members is invalid in strict ISO C.
gcc is happy with it, but if you use -Wpedantic or use another compiler
you may get warnings or errors.
The code here also shows how to use the "format" attribute to get
checking of the types in the debugPrintf call. Try changing one of the
%i to %s, and look at the warnings (they will only be checked when
debugging is enabled, but you can't have everything!)