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

variables in debugger watch window not right...?

I was trying to make a simple static heap module, and when trying to verify proper operation of the heap functions via the debugger I got quite confused.

Whether I build and run the below code for an AT91SAM7S32 in the simulator or on my target hardware, local variables in the watch window change in ways that I just can't believe could be valid.

For example, when stepping through the heap_alloc function (for the first call to it from main), the blocksize variable changes every time I press F10 once I get into the part where I'm actually allocating the block. The comments show how the value of blocksize changes when I don't think it should be changing at all.

Can anyone shed any light on this...?

It doesn't matter whether I declare the heap variables at the top of the file as static or not, the wierd behavior is the same.

#define NULL (0)

#define HEAP_SIZE (1000)

static char the_heap[HEAP_SIZE];
static char* ptr_the_heap = the_heap;
static int heap_left = HEAP_SIZE;

int heap_size(void)
  {
    return(heap_left);
  }

void* heap_alloc(int blocksize)
  {
    int remainder;
    int padding;
    void* ptr_block;

    // allocate blocks on 32-bit boundaries
    remainder = blocksize % 4;
    padding = 4 - remainder;
    blocksize += padding;

    if (blocksize <= heap_left)
      {
        ptr_block = (void*)ptr_the_heap; // blocksize changes to 0x3E8 (1000) here
        ptr_the_heap += blocksize; // blocksize changes to 0x200000 here
        heap_left -= blocksize; // blocksize changes to 0x20000C here
        return(ptr_block); // blocksize changes to 0x3E8 here
      }
    else
      {
        return(NULL); // not enough space on the_heap for blocksize
      }

  }

int main(void)
  {
    volatile void *p1;
    volatile void *p2;
    volatile void *p3;
    volatile void *p4;

    p1 = heap_alloc(1);
    p2 = heap_alloc(2);
    p3 = heap_alloc(3);
    p4 = heap_alloc(4);

    while(p1);
    while(p2);
    while(p3);
    while(p4);

    return(0);
  }

Frustrated,
Dave.

P.S. I previewed this post, but the preview of it got really screwy in the code listing part as soon as I started using the wheel on my mouse to go up and down through the post. I hope it doesn't look that way once I actually post it!

Parents Reply Children
  • The compiler's optimizer is set to level 0, which I think is as low as it can go.

  • Dave,

    If you look at the disassembled code you will probably find that blocksize was passed in R0 and subsequently moved onto the stack or more likely to another register.

    It looks like blocksize in the locals window is still showing the contents of R0.

    From the values that quote in the comments it seems likely that R0 is being used in the previous expressions.

    I don't know how you are going to get around it though.

  • The funny thing is that p1 through p4 all get set to the proper values after calling the heap_alloc function as shown, implying that the code that got generated was indeed correct.

    However, what good is a watch window if the values shown for watched variables can be so wrong? How can you know when you should trust or not trust the information displayed in watch windows...?

    If I had been debugging a larger, more complicated program, I would've probably incorrectly assumed that somehow code associated with an interrupt was corrupting those variables. The unfortunate result being much time wasted chasing down a problem that didn't really exist, well... at least it didn't exist in my code... it existed in the toolset.

    Surely the solution is not to use global variables for everything. Can anyone tell me if there's some way to make the watch window work properly?

  • Dave,

    this lookes like a compiler issue rather than a
    debugger issue. While trying your example and looking
    at the generated elf/dwarf output, the following can
    be seen:

    parameter 'blocksize' is assigned to R0
    temporary '__result' is also mapped to R0
    variable 'ptr_block' is mapped to R0

    this means, that the compiler optimizes the usage of registers
    even with optimize #0. The debugger has no chance to know which
    value is represented by 'R0' at a given location.
    The code is correct, just the lifetime info and probably the
    alternative location for the variables is missing in the generated
    debug info:

      001127: 13  = 0x11 (DW_TAG_compile_unit)
      001128:   DW_AT_name Heap.c
      00112f:   DW_AT_producer ARM/Thumb C/C++ Compiler with , RVCT3.0 [Build 942] for uVision
       ...
    
      001181:   59  = 0x2e (DW_TAG_subprogram)
      001182:     DW_AT_sibling 0xde (0x11fa)
       ...
      001187:     DW_AT_name heap_alloc
    
      0011a1:     90  = 0x5 (DW_TAG_formal_parameter)
      0011a2:       DW_AT_name blocksize
      0011ac:       DW_AT_type indirect DW_FORM_ref1 0x94 (0x11b0)
      0011ae:       DW_AT_location  block size 0x1 = { DW_OP_reg0 }  <<=== R0
      0011b0:     101  = 0x35 (DW_TAG_volatile_type)
      0011b1:       DW_AT_type indirect DW_FORM_ref_addr 0x821
      0011b6:     82  = 0x34 (DW_TAG_variable)
      0011b7:       DW_AT_name __result
      0011c0:       DW_AT_type indirect DW_FORM_ref1 0xde (0x11fa)
      0011c2:       DW_AT_location  block size 0x1 = { DW_OP_reg0 } <<=== R0
      0011c4:       DW_AT_artificial 0x1
      0011c5:     78  = 0x34 (DW_TAG_variable)
      0011c6:       DW_AT_name remainder
      0011d0:       DW_AT_type indirect DW_FORM_ref_addr 0x821
      0011d5:       DW_AT_location  block size 0x1 = { DW_OP_reg1 }
      0011d7:       DW_AT_start_scope 0x0
      0011d8:     78  = 0x34 (DW_TAG_variable)
      0011d9:       DW_AT_name padding
      0011e1:       DW_AT_type indirect DW_FORM_ref_addr 0x821
      0011e6:       DW_AT_location  block size 0x1 = { DW_OP_reg1 }
      0011e8:       DW_AT_start_scope 0x0
      0011e9:     78  = 0x34 (DW_TAG_variable)
      0011ea:       DW_AT_name ptr_block
      0011f4:       DW_AT_type indirect DW_FORM_ref1 0xde (0x11fa)
      0011f6:       DW_AT_location  block size 0x1 = { DW_OP_reg0 }  <<=== R0
      0011f8:       DW_AT_start_scope 0x34
       ...
    

  • Hmmm... sounds like one more level of optimization might be needed to fix this problem. Perhaps that level should be called -1, since 0 is already taken. Seems to me that currently there is no way to turn all compiler optimizations off, which in my opinion markedly reduces the usefulness of the debugger.

    I've submitted this situation to KEIL for customer assistance, and a dialog has begun.

    Thanks Peter.

    Dave.

  • Dave,

    This is not a problem with the optimization level. It is a problem in the compiler where the debug information regarding the lifetime chains and locations is incomplete. This problem has already been addressed, the next compiler release will fix this. The current internal compiler release (under development) already fixes this issue.
    I need to find out when this new release will be available.

    Regards,
    Peter

  • Many thanks Peter.

    I'll be anxiously awaiting the next compiler release. Hopefully it'll be before my current support period expires.

    When my wife recently me asked me how my day went, I told her about this and she asked "Why don't you just write your own tools?" I got a good chuckle out of that!

    I've always been very pleased with the quality of KEIL products and their support. I'm glad to hear that this problem should disappear soon.

    Thanks for your support,
    Dave.