This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

uVision Watch window does not like static ...

I use uVision 4.53 (with Armcc V4.1.0.894), processor STM32F407 with standard settings.

If I compile the following small program:

class Test{
public:
        static int i1;
        static int i2;

        int TestFunc( void);
};

int Test::TestFunc( void){
        i1= i2;
        return 0;
}

Test T;

int main( void){

        T.TestFunc();

        while( 1);
}

Then the watch window will show the class element T with the address "0x20000014 &T". This is a bit strange, as the address of i1 is 0x2000001C and address of i2 is 0x20000018 (as you can easily see if you look at the disassembly of the line "i1=i2" in function TestFunc).

If I try to change the value of T1.i1 in the watch window, it will fail - it always keeps zero, even after I enter some changed value.

The miracle happens, if I change the class definition to

class Test{
public:
        static int i1;
        static int i2;

        static int TestFunc( void);
};

Then the watch window will show the class element T with the address "0x00000000" (this usually would mean, that T is not used in the software - so this evidently is wrong). But nicely now i1 and i2 can be accessed without problems through the watch window.

I came to this problem through large classes with structs. For structs you clearly see the address in the watch window. There I recognized that the adresses sometimes were complete nonsense (it showed 0x4000000 adresses for normal variables - 0x4000000 is reserved for peripheral registers, so it is impossible).

It seems to be important, that all functions which are invoked from some static C-like funktion (as main()), need to be static. Otherwise the watch window does very strange things.

BTW: It would make a bit sense, if in the "Type" field uVision would show "class T" (now it says "struct T", which is slightly disturbing ...).

Parents
  • And how should I watch then this variable in the watch window?

    I'm not sure, but here's a suggestion: try to compile the example you gave, as-is. It won't work, because there's something missing. Then think about whether those missing things might have some relation to your problem.

    Neither T.i1 works, nor if I look into T, and then open T with the + button and then look at i1.

    That would be because i1 is not actually in T.

Reply
  • And how should I watch then this variable in the watch window?

    I'm not sure, but here's a suggestion: try to compile the example you gave, as-is. It won't work, because there's something missing. Then think about whether those missing things might have some relation to your problem.

    Neither T.i1 works, nor if I look into T, and then open T with the + button and then look at i1.

    That would be because i1 is not actually in T.

Children
  • That's not correct.

    i1 of course belongs to T, and I do not know many other possiblities than watching it. (except memory win with memory address - which realy would get teasy ...).

    If I compile the example with the function TestFunc defined as static in the class, then all works as I expect it. That's just my hint for anybody touching this problem.

  • Given the example below with some static's, it is of course
    possible to have the static members being shown in the watch
    window. It is a matter of symbol qualification.

    Open the symbol window, then expand the module where your class is
    defined - then a careful look might help to see how the compiler
    lowers the static members.

    Anyway, drag the static symbol into a watch window - it will be
    there with the correct value being shown.

    You might also drag such a symbol into the command window,
    this shows the fully qualified symbol. By right clicking
    a variable in the symbol window, adding a symbol to a watch
    or memory or expanding the the qualification can be done as
    well.

    class CX1  {
      static  int    si1, si2;
    
    public:
      CX1();
      ...
    };
    
    int CX1::si1 = -10;  // static initializer
    int CX2::si2 = +10;  // static initializer
    

  • With the program example which I gave you in the first post of this thread, it definitely will NOT work.

    I came through it, because I recognized that in some of my classes in a larger project I was not able to watch some of the member variables. Looking into this problem quite I while, I came to this stripped example of my first thread - please just copy it in some main.cpp and compile yourself and you should see it. (You might take the blinky of some STM32F4 example, and just replace the main.cpp).

  • PS: The first example of this thread was compiled with the compiler switch --anachronisms. If you skip this, then you need to define the variables i1 and i2 separately again, as you showed in your example - you are right in this point. But this does not change the fact, that the watch window will NOT work, if you translate this "first post of this thread" example - also if you add the external definition of i1 and i2.

  • No, i1 does not "of course" belong to T. T is an instance of a class, and instances of classes do not own any static members. That would imply that two instances of the class would result in two instances of i1 too, which would totally break the idea of a static member.

  • Just checked your initial example, without initializing the
    static data members i1 and i2, the linker outputs an error message
    because of undefined symbols:

    linking...
    .\output\cpp_template.axf: Error: L6218E: Undefined symbol Test::i1 (referred from main.o).
    .\output\cpp_template.axf: Error: L6218E: Undefined symbol Test::i2 (referred from main.o).
    Target not created
    </pre/>
    
    After adding the static initializers as shown below, everything is fine -
    the value displays of i1 and i2 in the watch window are correct. Once more
    again, dragging such a symbol from the symbol window into the watch window
    is the easiest way to get correctly qualified symbols (instead of having to
    enter \\cpp_template\../../source/main.cpp\i1 or something similar).
    
    
    
    class Test  {
    public:
      static int i1;
      static int i2;
      int TestFunc( void);
    };
    
    int Test::TestFunc( void)  {
      i1 = i2;
      return 0;
    }
    
    Test T;
    int Test::i1 = 1;
    int Test::i2 = 2;
    
    int main1 (void)  {
      T.TestFunc();
      return (1);
    }
    ...
    <pre/>
    
    
    

  • I'm sorry for the bad formatting...

    Just checked your initial example, without initializing the
    static data members i1 and i2, the linker outputs an error message
    because of undefined symbols:

    linking...
    .\output\cpp_template.axf: Error: L6218E: Undefined symbol Test::i1 (referred from main.o).
    .\output\cpp_template.axf: Error: L6218E: Undefined symbol Test::i2 (referred from main.o).
    Target not created
    

    After adding the static initializers as shown below, everything is fine -
    the value displays of i1 and i2 in the watch window are correct. Once more
    again, dragging such a symbol from the symbol window into the watch window
    is the easiest way to get correctly qualified symbols (instead of having to
    enter \\cpp_template\../../source/main.cpp\i1 or something similar).

    class Test  {
    public:
      static int i1;
      static int i2;
      int TestFunc( void);
    };
    
    int Test::TestFunc( void)  {
      i1 = i2;
      return 0;
    }
    
    Test T;
    int Test::i1 = 1;
    int Test::i2 = 2;
    
    int main1 (void)  {
      T.TestFunc();
      return (1);
    }
    ...
    

  • But the watch window definitly has some flaws(not in this case)

    But sometimes i can not watch local variables in some methods/functions. If i stop debugging and start again, suddenly it works.

    I don't think that there are some code issues, because it works without recompiling...i just have to restart the debugging process.
    Until now i did not fidn a system behind it...just happens some times

  • Hi Gp F,
    I tried exactly your example, and it does NOT work.

    (but I renamed your "main1" to "main", and in main I replaced your "return (1);" by "while(1);").

    It shows T in the watch window (with address "0x20000014 &T"), and it is possible to enlarge T to see i1 and i2. But i1 and i2 somehow do not work - if I try to change the value of the variable i1 to e. g. "5", then it always flips back to zero (after pressing enter).

    If I change the declaration of "int TestFunc(void);" to "static int TestFunc(void);", then all works nicely (no problem to change the variable i1 in the watch window to any value I want).

    [ I am quite sure that the variable i1 in the watch window somehow has some completely wrong address (I met this problem in larger classes containing larger structs - there the struct address is always shown in the watch window, and the struct adresses shown there were in the 0x40000000 range, which is impossible for STM32F4 programs ...) ( Unfortunately it is not possible to see the address of i1 in the watch window ) (this would be a very nice additional feature in the watch window: Showing an additional column with the address, and if you would consider to create additional columns, perhaps also another column with decimal/float value (perhaps possible to switch on/off with right mouse key - in larger projects sometimes it is very useful, if you can watch your data as well in hex as in decimal or float)) ]

    To rule out, that it is a problem with my hardware (ST4Discovery, new ST-Link Debugger which came with uVision 4.53), I tried also in simulation mode, but there I come to the same problem. So it should be no problem for you, to duplicate the problem on your system.

  • It is not a problem of "sometimes working - sometimes not". It definitely NEVER works. I even tried it in simulation mode, without connected hardware, so everyone should be able to follow my problem on his own PC (see my reply above to Gp F).

  • Ok, I see the problem that is fooling you here as well. 'T' should
    not show up in the symbol window since it is a declarative type but
    not an object. All the members are lifted out to module level leaving
    'T' an orphant/undefined object that should be filtered out by the
    uVision debugger. That is the reason why you get invalid member addresses.

  • Thank you for confirmation.

    But why should 'T' not be visible in the watch window? If I want to watch the contents of 'T', it is ultimatively required, that it is visible in the watch window.

    (Please do NOT feel disturbed by the fact, that T contains only static members - the problem persists, if you fill up 'T' with any number of additional non-static members).

    (Further it is really strange, that 'T' works perfectly nice in the watch window, as soon as you define 'TestFunc' as a static func in the class declaration).

  • Well, I see the 'T' case as follows: when all members are static, then
    T is orphant and should be filtered out. Otherwise T should be
    available but the members marked as declaration (those without a location)
    should be removed/hidden from the T structure.

    Given the dwarf type info for 'T', the members i1, i2 and TestFunc
    should be hidden since they are lifted out and therefore have no location
    (that would be some DW_AT_location tag). Showing them when expanding T
    causes the confusion since their locations are undefined:

    000370af 01eef5: 64 = DW_TAG_structure_type
    000370b0 01eef6:   DW_AT_sibling DW_FORM_ref_udata 0x678 (0x1EF38)
    000370b2 01eef8:   DW_AT_name DW_FORM_string 'Test'
    000370b7 01eefd:   DW_AT_byte_size DW_FORM_udata 0x8
    000370b8 01eefe:   38 = DW_TAG_member
    000370b9 01eeff:     DW_AT_name DW_FORM_string 'x1'
    000370bc 01ef02:     DW_AT_type DW_FORM_indirect DW_FORM_ref2 0x89 (0x1E949)
    000370bf 01ef05:     DW_AT_data_member_location DW_FORM_block len=2 DW_OP_plus_uconst 0x0
    000370c2 01ef08:   38 = DW_TAG_member
    000370c3 01ef09:     DW_AT_name DW_FORM_string 'x2'
    000370c6 01ef0c:     DW_AT_type DW_FORM_indirect DW_FORM_ref2 0x89 (0x1E949)
    000370c9 01ef0f:     DW_AT_data_member_location DW_FORM_block len=2 DW_OP_plus_uconst 0x4
    000370cc 01ef12:   42 = DW_TAG_member
    000370cd 01ef13:     DW_AT_name DW_FORM_string 'i1'
    000370d0 01ef16:     DW_AT_type DW_FORM_indirect DW_FORM_ref2 0x89 (0x1E949)
    000370d3 01ef19:     DW_AT_declaration DW_FORM_flag 0x1
    000370d4 01ef1a:     DW_AT_artificial DW_FORM_flag 0x0
    000370d5 01ef1b:   42 = DW_TAG_member
    000370d6 01ef1c:     DW_AT_name DW_FORM_string 'i2'
    000370d9 01ef1f:     DW_AT_type DW_FORM_indirect DW_FORM_ref2 0x89 (0x1E949)
    000370dc 01ef22:     DW_AT_declaration DW_FORM_flag 0x1
    000370dd 01ef23:     DW_AT_artificial DW_FORM_flag 0x0
    000370de 01ef24:   99 = DW_TAG_subprogram
    000370df 01ef25:     DW_AT_sibling DW_FORM_ref_udata 0x677 (0x1EF37)
    000370e1 01ef27:     DW_AT_name DW_FORM_string 'TestFunc'
    000370ea 01ef30:     DW_AT_declaration DW_FORM_flag 0x1
    000370eb 01ef31:     DW_AT_artificial DW_FORM_flag 0x0
    000370ec 01ef32:     DW_AT_type DW_FORM_indirect DW_FORM_ref2 0x89 (0x1E949)
    000370ef 01ef35:     DW_AT_external DW_FORM_flag 0x0
    000370f0 01ef36:     0  null
    000370f1 01ef37:   0  null
    

  • But the problem persists, if I add non-static members to T.

    Even if all is static in T, then it is ultimately necessary that I have some possibility to watch the members of T.

    In no way T should be "filtered out", as it is a used global class.

  • But when will you finally realize that i1 is _not_ (!) a member of T. T is an object. i1 has static declaration so it is not a member of any object. It is a member of the class. The memory location of i1 does not care the slightest about any memory location of any object T, because i1 is basically a completely separate global variable. Just a variable that lives within a separate namespace - the namespace of class Test.

    Step one when programming in C++ is to learn the difference between classes and objects. Any debate about "static" is meaningless until you do grasp that difference.

    A memory dump of the object T will not span the memory use by i1, since i1 isn't part of the memory of the object T. T and i1 are two separate variables in the memory map.