Here is an extremely simplified version of my class:
Class MyClass {
public:
int sizeDesired;
};
I'm creating a vector of MyClass instances in main:
int main(int argc, char **argv) {
std::vector<MyClass> myvec;
for(int i=0; i<10; ++i)
myvec.push_back(MyClass());
for(int i=0; i<myvec.size(); ++i)
doWork(myvec[i]);
return 0;
}
There's some memory corruption (I think) error that is causing my program to crash. I have observed that the value of MyClass::sizeDesired is garbage when the program crashes. So, I want to set a watchpoint on each MyClass:sizeDesired member so I can see exactly when any of these members' values changes.
Using GDB, how can I do this?
When I break after pushing all the instances of MyClass onto the std::vector<MyClass> in main, I then do
(gdb) watch myvec[0].sizeDesired
but GDB just hangs. It doesn't display a new command prompt (i.e., it doesn't show (gdb) on the succeeding line... just a blank line and nothing seems to be happening).
I'm open to non-GDB based solutions. If this type of inspection/monitoring is not possible in GDB, is there an alternative tool that could be used?
I did not do much C++ debugging in gdb, so these are probably all well known issues.
The problem with your watchpoint seems to be caused by gdb's inability to actually execute some methods, like [] operator or at() method. You can try this by just giving print myvec.at(0). It looks like this test is missing from watchpoint code, and it freezes the gdb. (It's probably known gdb bug, but I'll check.)
Now for the workaround. You can access n-th element of the vector using:
(MyClass*)(myvec._M_impl._M_start+n)
For sizeDesired that would then be:
(((MyClass*)(myvec._M_impl._M_start+n))->sizeDesired)
Adding a watchpoint to this expression still freezes gdb for some reason.
But print works, so if you do something like:
print &(((MyClass*)(myvec._M_impl._M_start+3))->sizeDesired)
You will get pointer to the field you want to watch. Something like this will get printed out:
$1 = (int *) 0x40508c
Now issue:
watch *((int*)0x40508c)
continue
Hardware access (read/write) watchpoint 3: ((int)0x40508c)
...
BTW: Ideas how to print std containers were snitched from http://sourceware.org/ml/gdb/2008-02/msg00064/stl-views.gdb.
Related
I'm new to C++, coming from a python/kotlin background and so am having some trouble understanding what's going on behind the scenes here...
The Issue
I call the calculateWeights (public) method with its required parameters, it then calls a series of methods, including conjugateGradientMethod (private) and should return a vector of doubles. conjugateGradientMethod returns the vector of doubles to calculateWeights just fine, but calculateWeights doesn't return that to its caller:
The code
Callsite of calculateWeights:
Matrix cov = estimator.estimateCovariances(&firstWindow, &meanReturns);
cout << "before" << endl; // this prints
vector<double> portfolioWeights = optimiser.calculateWeights(&cov, &meanReturns);
cout << "after" << endl; // this does not print
Here's calculateWeights:
vector<double> PortfolioOptimiser::calculateWeights
(Matrix *covariances, vector<double> *meanReturns) {
vector<double> X0 = this->calculateX0();
Matrix Q = this->generateQ(covariances, meanReturns);
vector<double> B = this->generateB0();
vector<double> weights = this->conjugateGradientMethod(&Q, &X0, &B);
cout << "inside calculateWeights" << endl;
print(&weights); // this prints just fine
cout << "returning from calculateWeights..." << endl; // also prints
return weights; //this is where the SIGABRT shows up
The output
The output looks like this (I've checked and the weights it outputs are indeed numerically correct):
before
inside calculateWeights
1.78998
0.429836
-0.62228
-0.597534
-0.0365409
0.000401613
returning from calculateWeights...
And then nothing.
I appreciate this is printf debugging which isn't ideal and so I used Cion's debugger to find the following:
When I used CLion's debugger
I put a break point on the returns of the conjugateGradient method and calculateWeights methods. The debugger steppeed through the first one just fine. After I stepped over the return from calculateWeights, it showed me a SIGABRT with the following error:
Thread 1 "markowitzportfoliooptimiser" received signal SIGABRT, Aborted.
__gnu_cxx::new_allocator<std::vector<double, std::allocator<double> > >::deallocate (this=0x6, __p=0x303e900000762) at /usr/lib/gcc/x86_64-pc-cygwin/9.3.0/include/c++/ext/new_allocator.h:129
This is probably wrong but my first stab at understanding that is that I've overflowed over the size of vector<double> weights? It's only 6 doubles long and I never append anything to it after the loop below. This is how it's created inside conjugateGradientMethod:
How weights is created inside conjugateGradientMethod
vector<double> weights= vector<double>(aSize);
for (int i = 0; i < aSize; i++) {
weights[i] = aCoeff * a->at(i) + bCoeff* b->at(i);
}
Things I've tried
Initialising a vector for double weights in calculateWeights and passed a pointer to it to conjugateGradientMethod. Same result.
Having a public attribute on the class calculateWeights and conjugateGradientMethod both live on, and having it assign the weights to that (so both functions return void). Same result.
More generally, I've had this kind of issue before with passing up a return value from two functions deep. (If that makes sense?) ie passing from private method up to public method up to public method's callsite.
I'd be grateful for any advice on SIGABRT's in this context, I've read it's when abort() sends the calling process the SIGABRT signal, but am unsure how to make use of that in this example.
Also, I'm all ears for any other style/best practices that would help avoid this in future
Edit: Solution found
After much work, I installed and got Ubuntu 20.04 LTS up and running since I couldn't get the Address Sanitizer nor Valgrind to work via WSL on Windows 10 (first time on Linux - I kinda like it).
With Address Sanitizer now working, I was able to see that I was writing too many elements to a vector of doubles on two separate accounts, nothing to do with my weights vector as #Lukas Matena rightly spotted. Confusingly this was long before it ever got to the snippets above.
If anyone is finding this in the future, these helped me massively:
Heap Buffer Overflow
Heap vs Stack 1
Heap vs Stack 2
The error message says that it failed to deallocate an std::vector<double> when calculateWeights was about to return. That likely means that at least one of the local variables (which are being destroyed at that point) in the function is corrupted.
You seem to be focusing on weights, but since the attempts that you mention have failed, I would rather suspect X0 or B (weights is maybe not even deallocated at that point due to return value optimization).
Things you can try:
start using an address sanitizer like others have suggested
comment out parts of the code to see if it leads you closer (in other words, make a minimal example)
make one of the vectors a member variable so it is not destroyed at that point (not a fix, but it might give a clue about who is the offender)
You're likely doing something bad to the respective vector somewhere, possibly in calculateX0 or generateB0 (which you haven't shared). It may be delete-ing part of the vector, returning a reference to a temporary instead of a copy, etc. The SIGABRT at that return is where you were caught, but memory corruption issues often surface later than they're caused.
(I would have made this shorter and posted as a comment but I cannot as a newbie. Hopefully it will count as an "advice on SIGABRT's in this context", which is what was in fact asked for).
In cpp one can use an array declaration as
typename array[size];
or
typename *array = new typename[size];
Where array is of length 'size' and elements are indexed from '0' to 'size -1'
Here my question is am I allowed to access the elements beyond the index >= size.
So I wrote this little code to check it
#include <iostream>
using namespace std;
int main()
{
//int *c; //for dynamic allocation
int n; //length of the array c
cin>>n; //getting the length
//c = new int[n]; //for dynamic allocation
int c[n]; //for static allocation
for(int i=0; i<n; i++) //getting the elements
cin>>c[i];
for(int i=0; i<n+10; i++) //showing the elements, I have add up 10
cout<<c[i]<<" "; //with size to access the memory I haven't
//allocated for
return 0;
}
And the result is like this
2
1 2
1 2 2686612 1970422009 7081064 4199040 2686592 0 1 1970387429 1971087432 2686700
Shouldn't the program crashed but gives garbage values. And for both the allocation methods it gives the same result. It makes more bugs which are hard to detect. Is it related with the environment or the compiler I am using or anything else?
I was using codeblocks IDE having TDM-GCC 4.8.1 compiler on windows 8.1
Thanks in advance.
This is called "undefined behavior" in the C++ standard.
Undefined behavior can mean any one of the following:
The program crashes
The program continues to run, but produces meaningless, garbage results
The program continues to run, and automatically copies the entire contents of your hard drive, and posts it on Facebook
The program continues to run, and automatically subscribes you to Publishers Clearinghouse Sweepstakes
The program continues to run, but your computer catches fire and explodes
The program continues to run, and makes your computer self-aware, which automatically links and networks with other self-aware networks, forming Skynet, and destroying the human race
Conclusion: do not run and access elements past the end of your arrays.
The c++ compilers don't enforce this as there is no specification to do so.
When you access an element of an array there is no boundary check done. c[i] just gets translated to c + i * sizeof(int) and that's it. If that area of memory is not initialize you'll get garbage, but you could be getting other useful information it all depends on what is there.
Please note that depending on the OS and the c++ runtime you're running you can get different results, for instance on a linux box you'll probably be getting a segmentation fault and the program will crash.
In this code below I try to access the '-1'th element of an array, I don't get any runtime error.
#include <stdio.h>
int A[10] = {0};
int main(){
A[-1] += 12;
printf("%d",A[-1]);
return 0;
}
When I run the code, it outputs 12 that means it is adding 12 to the non-existent A[-1]. Till today whenever I had tried to access an out-of-bounds element, I had got a runtime-error. I had never tried it on a simple code before.
Can anyone explain why does my code run successfully?
I ran it on my computer and also on ideone, in both the cases it ran successfully.
You see, when you allocate a variable like this, it lands on the stack. Stack holds small packages of information about local variables in each function you call, to say it in simple words. The runtime is able to check, whether you exceed the bounds of allocated stack, but not if you write some data in the invalid place on the stack. The stack may look like the following:
[4 bytes - some ptr][4 bytes - A's first element][4 bytes - A's second element] ...
When you try to assign to -1th element of an array, you actually attempt to read four bytes preceding the array (four bytes, because it's an int array). You overwrite some data held on stack - but that's still in valid process's memory, so there are no complaints from the system.
Try running this code in release mode in Visual Studio:
#include <stdio.h>
int main(int argc, char * argv[])
{
// NEVER DO IT ON PURPOSE!
int i = 0;
int A[5];
A[-1] = 42;
printf("%d\n", i);
getchar();
return 0;
}
Edit: in response to comments.
I missed the fact, that A is global. It won't be held in stack, but instead (mostly probably) in .data segment of the binary module, however the rest of explanation stands: A[-1] is still within process's memory, so assignment won't raise AV. However, such assignment will overwrite something, that is before A (possibly a pointer or other part of the binary module) resulting in undefined behavior.
Note, that my example may work and may not, depending on compiler (or compiler mode). For example, in debug mode the program returns 0 - I guess, that memory manager inserts some sentry data between stack frames to catch errors like buffer over/underrun.
C and C++ does not have any bounds checking. It is a part of the language. It is to enable the language to execute faster.
If you want bounds checking use another language that has it. Java perhaps?
As your code executes you are just lucky.
In C++ (and C), the arrays don't check out of range indices. They're not classes.
In C++11, however you could use std::array<int,10> and at() function as:
std::array<int,10> arr;
arr.at(-1) = 100; //it throws std::out_of_range exception
Or you can use std::vector<int> and at() member function.
I am testing a function vect_dbg(vector<int>) that returns the contents of a STL vector as a C string, so I can call it with gdb output vect_dbg(test) or put it in a watch.
#include<vector>
using namespace std;
int main()
{
vector<int> test; //breakpoint; output vect_dbg(test) ends debugging.
for (int i=0;i<=10;i++) test.push_back(i);
return 0;
}
#include<sstream>
const char* vect_dbg(const vector<int> &V)
{
stringstream ss;
for (int i=0;i<V.size();i++)
ss<<"V["<<i<<"]:"<<V[i]<<' ;'; //line 16
static char sEnu[256];
ss.getline(sEnu,256);
return sEnu;
}
Unfortunately, output vect_dbg(test) breaks the debugging.
The program being debugged was signaled while in a function called
from GDB. GDB has restored the context to what it was before the call.
To change this behavior use "set unwindonsignal off" Evaluation of the
expression containing the function (vect_dbg(std::vector > const&)) will be abandoned. Program received
signal SIGSEGV, Segmentation fault. 0x004014b1 in vect_dbg
(V=#0x22ff34) at main.cpp:16
I discovered that the function works if I make test global, but i couldn't figure out a solution for the locally defined vector. How can i solve this? Many thanks.
EDIT: Mainly solved, the answer was pretty obvious, the vector wasn't initialized at breakpoint. Now i am wondering if it's possible to detect that in vect_dbg.
An alternative i found is to declare the vector<int> static.
Put breakpoint one line after (on for loop). The variable test was not yet initialized when you trying to use it.
My program crashes on this line but only in debug build, it works fine in release build.
m_lstIds.insert(m_lstIds.begin() + indexInsert, ID);
'm_lstIds' is a std::vector of int, 'ID' is an int. When the program crashed, m_lstIds had 3 items (1, 2, 3). indexInsert was '0' and ID was '0'.
The error message says:
Expression: vector iterator + offset out of range
I am running visual studio 2010; I am guessing it has something to do with bad project settings which conflicted with STL optimization.
Edit:
When I said: "works on release" I meant if I do std::cout<<m_lstIds[i] for i = 0..3, I will actually get 0,1,2,3 printed out. In debug build it just crashes when I try to insert.
Edit2:
I found the answer! Thanks everyone for the help.
Here is the shortest repro. The problem is the memset function I call at the constructor. Because the constructor of m_lstItem was called before the memset, it will erase whatever data in the vector that allowed insert to work properly.
What's really interesting is how this worked in release but not in debug. Would be great if someone can explain that part.
struct SimpleList
{
SimpleList()
{
memset(this, 0, sizeof(SimpleList));
m_lstItem.push_back(0);
m_lstItem.push_back(1);
m_lstItem.push_back(2);
}
void Crash()
{
m_lstItem.insert(m_lstItem.begin() + 0, 3);
}
std::vector<int>m_lstItem;
};
int main(int argc, char** argv[])
{
SimpleList sl;
sl.Crash();
return 0;
}
memset(this, 0, sizeof(SimpleList)); is unsafe when your struct is not a POD.
Due to the member std::vector<int>m_lstItem;, your struct is not a POD.
Therefor, using memset is unsafe in this case, and leads to undefined behaviour. In other words: anything is allowed to happen! And anything includes working as expected...
My advice: Do not use memset in C++ unless you totally know what you are doing.
I guess the indexInsert is not 0 when the programe running.
And the vector will throw exception when the vector out of range in DEBUG mode,and in RELEASE mode it won't.
So show you full code.