3

I have a stack-allocated std::vector that is being overwritten by some rogue code. It is not the data being overwritten, but the internal state. I know this because the size() member function returns a junk value some time into the program. It initialises correctly. I suspect a common pointer bug elsewhere in my code.

I am using Xcode 4.6.2. I want to set a hardware breakpoint (with lldb) upon memory access to the vector's first memory location (of the vector itself, not the data), so I can see what is overwriting it. According to this I need to first find the address of the vector. Normally one would use the & operator to get the address of a variable, but for some reason with lldb this doesn't return the address but instead prints some sort of summary string.

class myClass {
 public:
  myClass() : myVector(4) {}
 private:
  std::vector<double> myVector;
  double myDouble;
};

After breaking at an arbitrary breakpoint after everything is constructed:

(lldb) frame variable &myObject.myVector
(std::vector<double, std::allocator<double> > *) $7 = size=4

'expr' has the same result:

(lldb) expr &myObject.myVector;
(std::vector<double, std::allocator<double> > *) $8 = size=4

Normally I'd expect to see the address printed, such as with this plain data member:

(lldb) frame variable &myObject.myDouble
(double *) &myDouble = 0x0000000125589328

I tried assigning the address to a variable with 'expr' but that doesn't work either:

(lldb) expr std::vector<double, std::allocator<double> > * f = &myObject.myVector; f
(std::vector<double, std::allocator<double> > *) $12 = size=0

The zero size (instead of 4) returned there suggests it didn't actually pick up the right vector anyway.

So how do I get the address of this vector? If I right-click on it in Xcode's list of frame variables, and choose 'View Memory' of this vector, then it opens a view of 0x0 which of course is invalid. Note that the vector is allocated on the stack - it's actually inside a few other objects but all of these are stack constructed.

Please note that I am not wanting to obtain the address of the data within the vector - this isn't actually being overwritten. It is the internal storage of the stack-allocated vector object that is being damaged.

Community
  • 1
  • 1
davidA
  • 12,528
  • 9
  • 64
  • 96
  • What you're seeing in the debugger is just lldb trying to be helpful and make it more human-readable. In code, though, you should be able to just use `&myObject.myVector`. You could always create variable to hold the address, like `std::vector* vecPtr = &myObject.myVector;`. You could then see what lldb does with `vecPtr`, or use it in code. – user1118321 Apr 23 '13 at 11:59
  • I tried that variable idea, but when lldb prints that out it also formats it as a vector, since I suppose it knows it's a pointer to a vector. Perhaps I ought to have used a `void *`. – davidA Apr 23 '13 at 21:24

2 Answers2

9

Your myObject object is likely a local variable and exists on the stack:

(lldb) p &myObject
(myClass *) $0 = 0x00007fff5fbffad8
(lldb) p $fp >= &myObject && &myObject >= $sp
(bool) $1 = true

If you want lldb to not pretty-print the vector, use the -R / --raw-output option to frame variable:

(lldb) fr va -R myObject
(myClass) myObject = {
  myVector = {
    std::_Vector_base<double, std::allocator<double> > = {
      _M_impl = {
        _M_start = 0x00000001001008d0
        _M_finish = 0x00000001001008f0
        _M_end_of_storage = 0x00000001001008f0
      }
    }
  }
  myDouble = 6.95322297580907e-310
}

You'll notice the addresses on the vector are heap addresses - that is, they were malloc'ed (new'ed, whatever) instead of being located inside your myObject directly. So now you have to decide what you want to monitor. If you want to monitor the vector to see a new element being added to the end of your vector, then you want to watch the end-of-vector pointer,

(lldb) p &myObject.myVector._M_impl._M_finish
(double **) $4 = 0x00007fff5fbffae0

If you want to monitor the contents of one of the vectors, say the first, then you want to monitor

(lldb) p myObject.myVector._M_impl._M_start
(double *) $6 = 0x00000001001008d0

of course when elements are added to the vector it may be moved to a new address range (if the memory region on the heap cannot be grown to hold the new element) so this is going to be a little fragile.

Normally lldb's type formatters (for std::vector in this case) are preferable to seeing the raw implementation -- but if you want to work with the raw implementation, it may be easier to disable the formatters in this case. e.g.

(lldb) type category disable  gnu-libstdc++
(lldb) p myObject
(myClass) $9 = {
  (std::vector<double, std::allocator<double> >) myVector = {
    (std::_Vector_base<double, std::allocator<double> >) std::_Vector_base<double, std::allocator<double> > = {
      (std::_Vector_base<double, std::allocator<double> >::_Vector_impl) _M_impl = {
        (double *) _M_start = 0x00000001001008d0
        (double *) _M_finish = 0x00000001001008f0
        (double *) _M_end_of_storage = 0x00000001001008f0
      }
    }
  }
  (double) myDouble = 6.95322297580907e-310
}

You can see all of the type formatters built-in to lldb with the type summary list command.

Jason Molenda
  • 14,835
  • 1
  • 59
  • 61
  • Thank you, `-R` is the key here. This allows `fr va -R &myObject.myVector` and now displays the vector's address (of the object itself, not it's heap-based storage) instead of the size. – davidA Apr 23 '13 at 21:23
1

Just as an addendum, you should have been able to say: (lldb) watchpoint set expression &myVector even if you don’t really get to “see” the value

Also, what you were seeing was the summary for an std::vector - turns out that we were not instructing the summary to either not apply to pointers or to show the value as well.

I have made a fix to the open source LLDB that will allow you to see the numeric value of the pointer. Probably, the really correct solution, though, would have to be a special “for-pointers” summary format that shows value & summary. However, I am not overly enthusiastic with going down that route as it would duplicate the amount of regexp formatters we have in LLDB, which is potentially not the best thing to do for performance.

Enrico Granata
  • 3,303
  • 18
  • 25