Macro Help

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • chris
    PosChengband Maintainer
    • Jan 2008
    • 702

    Macro Help

    (or, why does GCC keep complaining at me?)

    So, I'm trying to do a good thing and look into warnings when compiling poschengband on gcc. I'm not really a C developer, so I don't really know what I am doing!

    Anyway, some warnings have me stumped. First, is this:
    Code:
    cmd1.c:2492:29: warning: comparison is always false due to limited range of data type [-Wtype-limits]
         if (prace_is_(MIMIC_BAT))
    The macro prace_is_ is defined as follows:
    Code:
    #define prace_is_(A) (p_ptr->mimic_form == (A) || (p_ptr->mimic_form == MIMIC_NONE && (A) < MAX_RACES && p_ptr->prace == (A)))
    where mimic_form is an s16b and prace is a byte. MIMIC_BAT is indeed outside the range of prace as it is over 1000 so could have been problematic before I added the "(A) < MAX_RACES" check to the macro.

    Expanding the macro manually gives a warning as well:
    Code:
        if (p_ptr->mimic_form == MIMIC_BAT || (p_ptr->mimic_form == MIMIC_NONE && MIMIC_BAT < MAX_RACES && p_ptr->prace == MIMIC_BAT))
            return 100;
    The code works correctly, and as far as I can tell, is coded correctly. There is no chance of comparing prace vs MIMIC_BAT. But even if there was, I don't get the comparison is always false claim. It defintely will return true if mimic_form is set to MIMIC_BAT!

    So, is the macro incorrect? I inherited the mixed data sizes from Hengband and would rather not make prace an s16b.

    Thanks!

    EDIT: I think the warning actually applies only to the p_ptr->prace == (A) comparison, even though that conditional is unreachable in the case A exceeds the storage limits of a byte. Also, gcc is flagging the "if" as the point of the error, rather than the comparison, which is highly confusing. So, for example, changing from a macro to something like:
    Code:
    bool prace_is_(int which)
    {
        if (p_ptr->mimic_form == which)
            return TRUE;
        else if (p_ptr->mimic_form == MIMIC_NONE && p_ptr->prace == which)
                return TRUE;
        return FALSE;
    }
    fixes the problem.
    Last edited by chris; February 18, 2015, 19:21.
  • chris
    PosChengband Maintainer
    • Jan 2008
    • 702

    #2
    Ah, good times. Learning to debug on Linux and what better way to cut my teeth than attempting to mod main-gcu.c. Subtle changes to the map display size are now causing stack stompage. Eventually, after about 4 hours, I discover this:

    Code:
    diff --git a/src/main-gcu.c b/src/main-gcu.c
    index 18606dd..e018ae3 100644
    --- a/src/main-gcu.c
    +++ b/src/main-gcu.c
     
    @@ -1094,7 +1094,7 @@ static errr Term_text_gcu(int x, int y, int n, byte a, cptr s)
     
        int i;
     
    -   char text[81];
    +   char text[1024];
    I was wondering why 80x24 was hardcoded all over the place ...

    Comment

    • AnonymousHero
      Veteran
      • Jun 2007
      • 1393

      #3
      Originally posted by chris
      Ah, good times. Learning to debug on Linux and what better way to cut my teeth than attempting to mod main-gcu.c. Subtle changes to the map display size are now causing stack stompage. Eventually, after about 4 hours, I discover this:

      Code:
      diff --git a/src/main-gcu.c b/src/main-gcu.c
      index 18606dd..e018ae3 100644
      --- a/src/main-gcu.c
      +++ b/src/main-gcu.c
       
      @@ -1094,7 +1094,7 @@ static errr Term_text_gcu(int x, int y, int n, byte a, cptr s)
       
          int i;
       
      -   char text[81];
      +   char text[1024];
      I was wondering why 80x24 was hardcoded all over the place ...
      If you're not already doing so, you NEED to start using ASAN (Address Sanitizer) when compiling (works for both clang and gcc). Other tools from the same "suite" of flags may also be helpful, but even just ASAN catches a LOT of these problems early on and very often gives you the exact location of the origin of the problem. See the "-fsanitize" family of gcc/clang command line options for details. It takes literally less than 5 minutes to get started and it'll save you from a lot of guessing and flailing around in the dark.

      EDIT: I should say that ASAN+... are runtime instrumentation, so you'll actually need to run the game and play a bit, but that sure as hell beats mucking around in ancient C code .

      Comment

      • chris
        PosChengband Maintainer
        • Jan 2008
        • 702

        #4
        Apologies for posting in the wrong thread before. I was asking for help with linux -fsanitize.

        (On a tablet, so forgive the brevity)

        Output: there may be an option or env variable you can set to force sanitizer output to a file. If not, I'm pretty sure you can just redirect stderr to a file, eg using "2>&1 myfile" as the last part of the command line. (The reason the output is mangled is that ncurses disables newlines on the terminal.)

        About the missing "undefined": see the gcc manual, i was workingfrom memory and the option may be a little different, something to do with "undefined behavior". It's possible that the option wasn't available in gcc 4.8.

        Generally i'd recommend clang when using the sanitizer since it usually has more recent support. (And is mostly cmdline compatible with gcc.)
        Thanks. gcc 4.9 got -fsanitize=undefined but hasn't been released yet on Mint. I managed to upgrade, though.

        For the record, I've had a lot of problems finding documentation. The gcc manual barely mentions these options. Eventually, I found this. I've never heard of clang before. Is this a substitute for gcc?

        Thanks, again for your patience.

        Edit: I just found this as well.

        Comment

        • AnonymousHero
          Veteran
          • Jun 2007
          • 1393

          #5
          Originally posted by chris
          Apologies for posting in the wrong thread before. I was asking for help with linux -fsanitize.



          Thanks. gcc 4.9 got -fsanitize=undefined but hasn't been released yet on Mint. I managed to upgrade, though.

          For the record, I've had a lot of problems finding documentation. The gcc manual barely mentions these options. Eventually, I found this. I've never heard of clang before. Is this a substitute for gcc?
          clang is a completely different C/C++ compiler. It's mostly command-line compatible with gcc -- for the common options at least. You should basically be able to replace "gcc" with "clang" in your compilation command line.

          Originally posted by chris
          Edit: I just found this as well.
          Ah, yes, looks like you'd want something like
          Code:
          ASAN_OPTIONS=log_path=asan_log ./poschengband
          (and then look in asan_log.XXX for the output, obviously) to avoid messing up the terminal. Or, as mentioned in the other thread, just redirect stderr to a file by running
          Code:
          ./poschengband 2> asan.log
          HTH.

          Comment

          • chris
            PosChengband Maintainer
            • Jan 2008
            • 702

            #6
            Chris' Linux Development Q&amp;A Thread

            OK, thanks again for tips.

            Today I learned that the "escape delay" on the curses port has nothing whatsoever to do with ESCDELAY in curses. Not a thing! It actually has to do with multi-character macros setup in pref-gcu.pref to map the multi-character sequences that curses generates for certain keystrokes. Like, you press '2' on the num pad and curses sends \e[B (3 calls to getch()). pref-gcu.pref sets up a macro to translate this back to '2' for the game engine. Now, when the user presses \e (say, to dismiss a menu), our code (not curses!) is pausing to see if the user really wants to type \e[B by hand! Fun stuff, I just had to share

            Anyway, today's question relates to setting up compiler options in the build. For instance, if I want -fsanitize=address to be set, then this should only happen for me, not for other people who download and compile on linux (as they may use an older gcc or some other compiler). What I came up with was doing a
            Code:
            ./configure CFLAGS=-fsanitize=address LDFLAGS=-fsanitize=address --with-no-install
            which is a bit cumbersome. Is there a better way?

            On a similar note, sometimes I want a debug build (-O0 -g) and sometimes not (-O3). I gather that changing src/Makefile (which is under version control) is not the best way to do this ... Help?

            Thanks in advance!

            Comment

            • AnonymousHero
              Veteran
              • Jun 2007
              • 1393

              #7
              Originally posted by chris
              OK, thanks again for tips.

              Today I learned that the "escape delay" on the curses port has nothing whatsoever to do with ESCDELAY in curses. Not a thing! It actually has to do with multi-character macros setup in pref-gcu.pref to map the multi-character sequences that curses generates for certain keystrokes. Like, you press '2' on the num pad and curses sends \e[B (3 calls to getch()). pref-gcu.pref sets up a macro to translate this back to '2' for the game engine. Now, when the user presses \e (say, to dismiss a menu), our code (not curses!) is pausing to see if the user really wants to type \e[B by hand! Fun stuff, I just had to share
              Yeah, maddening, isn't it? The input/macro/keymap system is truly bizarre -- just people adding onto other people's work without any true understanding, just hacking until "it works". (I'm going by the probably-reasonable assumption that Hengband's system is similar to Zangband's which is probably similar to ToME 2.x.)

              Originally posted by chris
              Anyway, today's question relates to setting up compiler options in the build. For instance, if I want -fsanitize=address to be set, then this should only happen for me, not for other people who download and compile on linux (as they may use an older gcc or some other compiler). What I came up with was doing a
              Code:
              ./configure CFLAGS=-fsanitize=address LDFLAGS=-fsanitize=address --with-no-install
              which is a bit cumbersome. Is there a better way?

              On a similar note, sometimes I want a debug build (-O0 -g) and sometimes not (-O3). I gather that changing src/Makefile (which is under version control) is not the best way to do this ... Help?
              Personally, I wouldn't shy away from changing the Makefile. I just try to careful about what I merge (note: not "commit"). My workflow is usually heavily rebase-oriented so I just: a) branch, b) commit ad libitum (including makefile changes), c) commit some more (xN), d) rebase + squash branch appropriately on current master, e) excise undesirable parts of the squashed commit(s) using "git gui" (in amend mode) or "git rebase -i". And finally, f) merge (fast-forward, preferably).

              Comment

              • Nivim
                Apprentice
                • Jan 2014
                • 69

                #8
                It's nice to finally know why there's that little hang; I'd really been curious about what the program was doing! So much so that even given a plethora of other possibilities, I feel like it was development time well spent.

                Comment

                • chris
                  PosChengband Maintainer
                  • Jan 2008
                  • 702

                  #9
                  Help? I can't seem to figure out why I stopped getting symbols in Address Sanitizer output when using clang as my compiler.

                  What I am doing:
                  Code:
                  ./configure CFLAGS=-fsanitize=address LDFLAGS=-fsanitize=address --with-no-install CC=clang-3.5
                  and in Makefile:
                  Code:
                  CFLAGS += -DBUILD_ID=${VERSION} -I. -std=c99 -Wdeclaration-after-statement -O0 -g -fno-omit-frame-pointer
                  Now, if I drop the CC=clang-3.5 from my configure, and just use gcc, I get symbols. However, my gcc does not do leak detection and clang-3.5 does, so I'd like to be able to use that if possible.

                  The symbols must be in the executable, since clang barfs out a 25Mb file (versus 9 for gcc). However, I can't figure out what I might be missing.

                  Thanks for any help!

                  (FYI: Address sanitizer does not detect out of bounds stuff for arrays inside the middle of structs.)

                  EDIT: If I do exactly what is suggested here at the top, I don't get symbols either. So, how to get symbols with clang might be my problem:
                  Code:
                  > cat leak.c
                  #include <stdlib.h>
                  
                  void *p;
                  
                  int main() {
                    p = malloc(7);
                    p = 0; // The memory is leaked here.
                    return 0;
                  }
                  > clang-3.5 -fsanitize=address -g leak.c
                  > ./a.out
                  
                  =================================================================
                  ==20475==ERROR: LeakSanitizer: detected memory leaks
                  
                  Direct leak of 7 byte(s) in 1 object(s) allocated from:
                      #0 0x49a13b (/home/chris/Src/poschengband/src/a.out+0x49a13b)
                      #1 0x4b79a0 (/home/chris/Src/poschengband/src/a.out+0x4b79a0)
                      #2 0x7fcd376ceec4 (/lib/x86_64-linux-gnu/libc.so.6+0x21ec4)
                  
                  SUMMARY: AddressSanitizer: 7 byte(s) leaked in 1 allocation(s).

                  EDIT2: Found answer here. Or an answer, anyway. Now I just need to remember to type:
                  Code:
                  ./poschengband -g -uChris -- -n1 2>&1 | /tmp/asan_symbolize.py | c++filt
                  whenever I run

                  EDIT3: Better answer. Install llvm-3.5 package and run
                  Code:
                  ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer-3.5 ./poschengband -g -uChris -- -n1
                  . I'm not sure that's any easier to type, though!
                  Last edited by chris; April 19, 2015, 18:47.

                  Comment

                  • AnonymousHero
                    Veteran
                    • Jun 2007
                    • 1393

                    #10
                    Originally posted by chris
                    (FYI: Address sanitizer does not detect out of bounds stuff for arrays inside the middle of structs.)
                    Are you sure? I think it should be able to, unless the structs are packed for some reason. (Maybe it's version-dependent, I'm currently running clang 3.6.)

                    Originally posted by chris
                    EDIT3: Better answer. Install llvm-3.5 package and run
                    Code:
                    ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer-3.5 ./poschengband -g -uChris -- -n1
                    . I'm not sure that's any easier to type, though!
                    Sounds like bad packaging to me. I get symbol output by default without having to fiddle with ASAN_SYMBOLIZER_PATH or anything like that. (I'm pretty sure that was also the case with Clang 3.5.)

                    I guess you could always just add ASAN_SYMBOLIZER_PATH to your ~/.bash_profile (or whatever your shell is) so that you at least won't have to type all of that every time.

                    Comment

                    • chris
                      PosChengband Maintainer
                      • Jan 2008
                      • 702

                      #11
                      Code:
                      #include <stdlib.h>
                      
                      struct foo_s
                      {
                          int foo[5];
                          int bar;
                      };
                      typedef struct foo_s foo_t;
                      
                      int main() 
                      {
                          foo_t foo;
                          foo.foo[5] = 1;
                          return 0;
                      }
                      This code gives a warning during compilation, but the access is not reported at runtime. Oddly,
                      Code:
                      p_ptr->innate_attacks[MAX_INNATE_ATTACKS].blows = 1;
                      neither warns nor reports, even though it seems to be the same thing:
                      Code:
                      struct player_type
                      {
                          ...
                          innate_attack_t innate_attacks[MAX_INNATE_ATTACKS];
                          ....
                      };

                      Comment

                      Working...
                      😀
                      😂
                      🥰
                      😘
                      🤢
                      😎
                      😞
                      😡
                      👍
                      👎