Stupid debugging of macro values - Page 1
Forum section: electronics / general

author: T3sl4co1l
author_rank: Super Contributor
timestamp: May 19, 2022, 05:00:28 pm
content: Obvious solution: echo macro values during compilation.I...don't know that GCC can do this??Dumb solution: echo it into the program somewhere.What if you're not debug printf()-ing? Don't want to drop in a whole pile of code to do that? Just see the raw values wherever they are?For example, on AVR, you assign constants, they'll be split into LDI instructions or whatever, you can't search on the exact value. Maybe what variable it's saved to. If you do low optimization and look at the listing interspersed with code, you can find that, still gotta sort out what the ASM is doing though.Easier then: just save it to a variable in the first place, and force it to be put somewhere easy to find.Code:#define INTEN_SLOPE /* some long expressions */...const uint16_t check_values[] __attribute__((used)) PROGMEM = {INTEN_SLOPE,INTEN_OFFS,INTEN_ADCMIN,INTEN_ADCMAX};Just toss in an array of the values, of course... The attribute prevents it from being optimized out (-O3, -lto), no need to declare itextern, etc. It shows up in the objdump -h -S listing towards the top (with the other PROGMEM stuff).Code:...000002da <check_values>:2da:a6 02 20 ae 10 52 f8 b1 .. ..R.....I, at least, have .lss generated all the time so this is a convenient place to find it. If you want to find the object itself of course you can extract/dump it by name in the usual way, etc.Tim

author: SiliconWizard
author_rank: Super Contributor
timestamp: May 19, 2022, 05:11:35 pm
content: GCC has the -E option to output the result of preprocessing on a given source file.The downside is that it does full preprocessing, thus outputting the content of all include files as well.

author: newbrain
author_rank: Super Contributor
timestamp: May 19, 2022, 06:35:34 pm
content: I checked whether #warning could be of any help - as it's not part of the standard I just vaguely remembered of its existence.But nope, it echoes exactly what follows it on the line, not its expansion.

author: ejeffrey
author_rank: Super Contributor
timestamp: May 19, 2022, 07:49:19 pm
content: gcc -dM -ERuns the preprocessor only and outputs all defined macros.

author: Nominal Animal
author_rank: Super Contributor
timestamp: May 19, 2022, 07:49:54 pm
content: Runninggcc...options...-dM -E...sources...outputs only the macro definitions; a#defineNameValueper macro. Piping the output throughsortmakes it easier to find specific definitions; I've also usedawkto recursively expand the definitions.You can also use#pragma messagestringduring compilation. For example:#define STRINGIFY_(msg) #msg#define STRINGIFY(msg) STRINGIFY_(msg)#pragma message "INTEN_SLOPE=\"" STRINGIFY(INTEN_SLOPE) "\"."will output something likesource.c:LINE: note: #pragma message:INTEN_SLOPE="expanded-value".during compilation.

author: T3sl4co1l
author_rank: Super Contributor
timestamp: May 19, 2022, 08:37:12 pm
content: Quote from: Nominal Animal on May 19, 2022, 07:49:54 pmThat's a good one, especially for more complicatedexpressionsexpansions than my particular case  happens to be:Code:main.c:111:9: note: #pragma message: INTEN_SLOPE="(uint16_t)((float)(85) / ((float)(0x9580) - (float)(0x7c30)) * 65536.0f + 0.5f)".#pragma message "INTEN_SLOPE=\"" STRINGIFY(INTEN_SLOPE) "\"."^~~~~~~The downside for mine is, I would like the expression itself evaluated -- which the prepro isn't quite capable of, unfortunately.So I guess unless there's a message path deeper inside the compiler -- dumping it to an easy-to-find variable doesn't seem that bad (dumb, even?) of a method after all?Kind of on a related note, there's been a few occasions I've wanted to test a fairly simple function, but fairly extensively (i.e. min, max, sampled points; or 100% exhaustively for that matter), which just seems a bit of a PITA in most C environments; it's never* interpreted so it's not like you can just pop over somewhere, paste in the expression, add a loop or whatever to test it, or logging output, etc., and be done; more that you need a whole-ass project and compile it and go, and run it, and finally there's your magic oracle output. Well, I probably do this less often than I should, for this reason. (I don't intend to discuss further along this line, in this thread; but it's interesting as a related pain point.)*Except when it is. Maybe I should look into one of those environments... Or learn, say, bash or whatever, scripting language with very C-like syntax.Tim

author: cv007
author_rank: Frequent Contributor
timestamp: May 19, 2022, 09:50:52 pm
content: Use the online compiler-https://godbolt.org/z/j4Y9W8e38I use it all the time to test ideas, verify what is happening in pieces of code, etc. You can also switch to a pc compiler and output via printf (into another panel) if the idea under test is generic (will not see avr instructions of course).I wrote a printf replacement in the c++ cout style using only the online compiler (using a pc compiler so could output to stdout via a simulated Uart class, and could also compare output to a 'real' cout). All done in the online compiler, which will eliminate the compile step (compiles at regular intervals) and you debug while you write code. So now have a ~300 line class in a header that any class can inherit and get cout style 'printing'. I use it for cortex-m to simplify printing, but works on avr or anything else with a c++ compiler.Cannot use the online compiler for everything, but it is a most underrated tool. I would think ide creators would be stumbling over themselves to be the first to do something similar, but crickets (for years now). You can install it locally, which has the advantage of using your own compiler(s), but still have to use via a web page.

author: Nominal Animal
author_rank: Super Contributor
timestamp: May 20, 2022, 12:08:33 am
content: You can also create for example the following Bash script, make it executable (chmod a+rxscript), move it somewhere along your PATH (I use$HOME/bin/), so that e.g.script'(uint16_t)((float)(85) / ((float)(0x9580) - (float)(0x7c30)) * 65536.0f + 0.5f)'outputs860nicely without any hassles. Just remember to use single quotes to stop the shell from expanding or interpreting the string.The script itself:Code:#!/bin/bash# SPDX-License-Identifier: CC0-1.0if [ $# -ne 1 ] || [ ":$1" = ":-h" ] || [ ":$1" = ":--help" ]; thenexec >&2printf '\n'printf 'Usage: %s [ -h | --help ]\n' "$0"printf ' %s C-statement\n' "$0"printf '\n'exit 1fiWork=$(mktemp -d) || exit 1trap "rm -rf '$Work'" EXIT(printf '#define _POSIX_C_SOURCE 200809L\n'printf '#define _GNU_SOURCE\n'printf '#include <stdlib.h>\n'printf '#include <inttypes.h>\n'printf '#include <string.h>\n'printf '#include <stdio.h>\n'printf '#include <math.h>\n'printf 'void print_u(const unsigned long long u) { printf("%%llu\\n", u); }\n'printf 'void print_i(const long long i) { printf("%%lld\\n", i); }\n'printf 'void print_ld(const long double ld) { printf("%%.18Lf\\n", ld); }\n'printf 'void print_d(const double d) { printf("%%.12f\\n", d); }\n'printf 'void print_f(const float f) { printf("%%.6f\\n", f); }\n'printf 'void print_s(const char *const s) { printf("\\"%%s\\"\\n", s); }\n'printf 'void print_c(const char c) { if (c>=32 && c<=126 && c!=39) printf("\\047%%c\\047\\n", c); else printf("(char)%%d\\n", c); }\n'printf '#define PRINT(expr) (_Generic((expr), unsigned long long: print_u, unsigned long: print_u, unsigned: print_u, long long: print_i, long: print_i, int: print_i, long double: print_ld, double: print_d, float: print_f, char *: print_s, char: print_c, default: print_i)(expr))\n'printf 'int main(void) { PRINT(%s); return EXIT_SUCCESS; }' "$1") > "$Work/source.c"gcc -Wall -Wextra -O2 -std=c11 "$Work/source.c" -lm -o "$Work/executable" || exit 1"$Work/executable" || exit 1exit 0and is licensed under the Creative Commons 1.0 license per the SPDX license identifier: do whatever you like with it at all, just don't blame me for any breakage or bugs.The real trick in the script is the use ofmktemp -dandtrap "..." EXITto make an auto-removed work directory that is always removed when the script exits, even if it is interrupted or exits due an error.It does use the C11 and later _Generic keyword to select the proper printf pattern to use for outputting the value of the expression based on the type of the expression, so based on your needs, you might wish to add new types, or modify the type-specific print statements.This handles even obscure C syntax correctly:script'2["foo"]'outputs'o'

author: Nominal Animal
author_rank: Super Contributor
timestamp: May 20, 2022, 05:06:48 am
content: You could also write a scriptlet that compiles named source files using specified options and-dM -E, incorporating all definitions to a temporary program that also prints all values of non-function-like macros, very similarly to above.

author: IanB
author_rank: Super Contributor
timestamp: May 20, 2022, 05:19:13 am
content: I guess I have got quite used to Visual Studio, where hovering over a macro will show the currently evaluated value in the tooltip. This has been sufficient for me when writing and debugging code without having to go searching through header files.The tooltip shows the original macro definition, the currently evaluated value with the substituted arguments, and allows me to jump to the header file where it is defined. IMHO this is one advantage IDEs have over the command line.

author: dcarr
author_rank: Regular Contributor
timestamp: May 20, 2022, 05:38:20 am
content: Hack:Add another #define that redefines the previous macro.Gcc will spit out an error that helpfully includes the previous macro value.

author: Doctorandus_P
author_rank: Super Contributor
timestamp: May 22, 2022, 12:47:10 pm
content: Macro's in C a bit of a weird thing, but they can make code a lot more readable, but also harder to debug.One important thing to always remember with macro's is that it is not a language thing, but only a text substitution feature.I've stopped using a C compiler a long time ago, but compile all C code with a C++ compiler.C++ has better error checking and a lot of extra features.Most of the old C macro's can be rewritten as C++ compile time constants. If you do this, you can even call a function to initialize your constant, and if that function is not used elsewhere, it won't even get included in the binaries. Your compile time constant also has a real type.