Author Topic: Stupid debugging of macro values  (Read 1156 times)

0 Members and 1 Guest are viewing this topic.

Offline T3sl4co1lTopic starter

  • Super Contributor
  • ***
  • Posts: 21688
  • Country: us
  • Expert, Analog Electronics, PCB Layout, EMC
    • Seven Transistor Labs
Stupid debugging of macro values
« on: May 19, 2022, 05:00:28 pm »
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: [Select]
#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 it extern, etc.  It shows up in the objdump -h -S listing towards the top (with the other PROGMEM stuff).

Code: [Select]
...
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
Seven Transistor Labs, LLC
Electronic design, from concept to prototype.
Bringing a project to life?  Send me a message!
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14481
  • Country: fr
Re: Stupid debugging of macro values
« Reply #1 on: May 19, 2022, 05:11:35 pm »
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.
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: Stupid debugging of macro values
« Reply #2 on: May 19, 2022, 06:35:34 pm »
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.
Nandemo wa shiranai wa yo, shitteru koto dake.
 
The following users thanked this post: T3sl4co1l

Offline ejeffrey

  • Super Contributor
  • ***
  • Posts: 3719
  • Country: us
Re: Stupid debugging of macro values
« Reply #3 on: May 19, 2022, 07:49:19 pm »
gcc -dM -E

Runs the preprocessor only and outputs all defined macros.
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Re: Stupid debugging of macro values
« Reply #4 on: May 19, 2022, 07:49:54 pm »
Running
    gcc ...options... -dM -E ...sources...
outputs only the macro definitions; a #define Name Value per macro.  Piping the output through sort makes it easier to find specific definitions; I've also used awk to recursively expand the definitions.

You can also use
    #pragma message string
during compilation.  For example:
    #define  STRINGIFY_(msg)  #msg
    #define  STRINGIFY(msg)  STRINGIFY_(msg)
    #pragma message "INTEN_SLOPE=\"" STRINGIFY(INTEN_SLOPE) "\"."
will output something like
    source.c:LINE: note: #pragma message: INTEN_SLOPE="expanded-value".
during compilation.
 
The following users thanked this post: hans, T3sl4co1l, newbrain, RoGeorge, DC1MC

Offline T3sl4co1lTopic starter

  • Super Contributor
  • ***
  • Posts: 21688
  • Country: us
  • Expert, Analog Electronics, PCB Layout, EMC
    • Seven Transistor Labs
Re: Stupid debugging of macro values
« Reply #5 on: May 19, 2022, 08:37:12 pm »
Running
    gcc ...options... -dM -E ...sources...
outputs only the macro definitions; a #define Name Value per macro.  Piping the output through sort makes it easier to find specific definitions; I've also used awk to recursively expand the definitions.

You can also use
    #pragma message string
during compilation.  For example:
    #define  STRINGIFY_(msg)  #msg
    #define  STRINGIFY(msg)  STRINGIFY_(msg)
    #pragma message "INTEN_SLOPE=\"" STRINGIFY(INTEN_SLOPE) "\"."
will output something like
    source.c:LINE: note: #pragma message: INTEN_SLOPE="expanded-value".
during compilation.

That's a good one, especially for more complicated expressionsexpansions than my particular case [fortunately] happens to be:

Code: [Select]
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
« Last Edit: May 19, 2022, 08:54:24 pm by T3sl4co1l »
Seven Transistor Labs, LLC
Electronic design, from concept to prototype.
Bringing a project to life?  Send me a message!
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 828
Re: Stupid debugging of macro values
« Reply #6 on: May 19, 2022, 09:50:52 pm »
Use the online compiler-
https://godbolt.org/z/j4Y9W8e38

I 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.
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Re: Stupid debugging of macro values
« Reply #7 on: May 20, 2022, 12:08:33 am »
You can also create for example the following Bash script, make it executable (chmod a+rx script), 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)'
outputs 860 nicely without any hassles.  Just remember to use single quotes to stop the shell from expanding or interpreting the string.

The script itself:
Code: [Select]
#!/bin/bash
# SPDX-License-Identifier: CC0-1.0

if [ $# -ne 1 ] || [ ":$1" = ":-h" ] || [ ":$1" = ":--help" ]; then
    exec >&2
    printf '\n'
    printf 'Usage: %s [ -h | --help ]\n' "$0"
    printf '       %s C-statement\n' "$0"
    printf '\n'
    exit 1
fi

Work=$(mktemp -d) || exit 1
trap "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 1
exit 0
and 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 of mktemp -d and trap "..." EXIT to 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'
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Re: Stupid debugging of macro values
« Reply #8 on: May 20, 2022, 05:06:48 am »
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.
 

Online IanB

  • Super Contributor
  • ***
  • Posts: 11892
  • Country: us
Re: Stupid debugging of macro values
« Reply #9 on: May 20, 2022, 05:19:13 am »
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.
 

Offline dcarr

  • Regular Contributor
  • *
  • Posts: 117
Re: Stupid debugging of macro values
« Reply #10 on: May 20, 2022, 05:38:20 am »
Hack:
Add another #define that redefines the previous macro.
Gcc will spit out an error that helpfully includes the previous macro value.
 

Offline Doctorandus_P

  • Super Contributor
  • ***
  • Posts: 3365
  • Country: nl
Re: Stupid debugging of macro values
« Reply #11 on: May 22, 2022, 12:47:10 pm »
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.
 
The following users thanked this post: thm_w


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf