Crash/Segfault in calling virtual member of subclass

Go To StackoverFlow.com

1

I have been trying to get this bit of code to work without success. gdb tells me that there is a segfault somewhere in the void Compiler::GenerateCode_ToFile(char* filename) function, and I have manually traced the problem to somewhere in the line:

 std::string tempfile = this->code->CodeGen( temp, AST_TYPE_UNDEF, symtab, 0);

but not after it or before it. Also, it seems to crash before any code of that virtual function is run.

Can anyone see the problem here? I just cant see what is causing it to crash. This is the function that calls the virtual function:

void Compiler::GenerateCode_ToFile(char* filename){

char directory[MAX_PATH];   //Actually represents the full path.
strcpy( directory, this->cwd.c_str());
strcat( directory, filename);

if(this->isVerboseMode)
    std::cout << "Source Output: " << directory << '\n';

std::fstream file( directory, std::ios::out);

int* temp = new int;
Symtable* symtab = new Symtable;
file << emit_core_code();
file << "\n\n";
std::string tempfile = this->code->CodeGen( temp, AST_TYPE_UNDEF, symtab, 0);
file.close();
}

This is the definition of the class represented by this->code.

/// CollectionExprAST - Expression class for multiple branches.
class CollectionExprAST : public ExprAST {
  std::vector<ExprAST*>* Code;
public:
  CollectionExprAST(std::vector<ExprAST*>* code) : Code(code) {}
  virtual std::string CodeGen(int* GeneratedCodeOpType,int WantOpType,Symtable* symtab, int depth);
  int GetType(void){return AST_TYPE_COLLECTION;};
  void* GetCollection(void){return this->Code;};
  void DebugPrint(int level);
};

This is its superclass:

/// ExprAST - Base class for all expression nodes.
class ExprAST {
public:
  virtual ~ExprAST() {}
  virtual std::string CodeGen(int* GeneratedCodeOpType,int WantOpType,Symtable* symtab, int depth) {return std::string("");};
  virtual void DebugPrint(int level){return;};
  virtual int GetType(void){return AST_TYPE_UNDEF;};
  virtual void* GetCollection(void){return NULL;};
};

and finally, this is the virtual function that is being called (although appears to crash before it is run):

std::string CollectionExprAST::CodeGen(int* GeneratedCodeOpType,int WantOpType,Symtable* symtab, int depth)
{
Sleep(3000);
std::string ret;
int j=0;
for(;j<this->Code->size();j++){
    int temp;
    int i=0;
    for(;i<depth;i++)
        ret += "\t";
    ret += (*this->Code)[j]->CodeGen(&temp,WantOpType,symtab, depth+1);
    ret += '\n';
}
return ret;
}

I know it crashes before it is run because the Sleep() never gets run.

Can anyone see the bug that is causing this mysterious segfault?

Thanks in advance.

2012-04-04 23:48
by 64bit_twitchyliquid
Too much code. You need to construct a minimal test-case before posting your code here. But are you sure that e.g. this->code is a valid pointer - Oliver Charlesworth 2012-04-04 23:53
@OliCharlesworth I am relatively sure that it is a valid pointer, as I call other virtual members without problems - 64bit_twitchyliquid 2012-04-04 23:59
@64bit_twitchyliquid: You need to construct a minimal test case before anybody here can give you concrete help - Oliver Charlesworth 2012-04-05 00:00
@64bit_twitchyliquid: "Pretty sure" tells me it probably isn't - Ed S. 2012-04-05 00:04


1

The reason is that code is not allocated or corrupted.

Check for nil before running the function, then check if you can run any other function from that pointer. The first one will be obvious, the latter may mean the pointer got corrupted somewhere.

2012-04-04 23:54
by Kornel Kisielewicz
milliseconds before, a different virtual function is called (DebugPrint()) and it runs correctly. Are you sure code is the problem - 64bit_twitchyliquid 2012-04-04 23:58
does DebugPrint() access any fields of the object? If it doesn't it isn't ran on the object at all - Kornel Kisielewicz 2012-04-04 23:59
Yes. It pretty much uses all of them - 64bit_twitchyliquid 2012-04-05 00:00
Anyway, show the code which creates the code field and we might be able to help - Kornel Kisielewicz 2012-04-05 00:01
This is the definition of 'code'. CollectionExprAST* code - 64bit_twitchyliquid 2012-04-05 00:04
and it is called from ParseCode( &this->tokenvect, &pos, &this->code, "", '\0');, as you can see, the pointer is passed, then fille - 64bit_twitchyliquid 2012-04-05 00:04
@64bit_twitchyliquid: I can't see that at all, I see you passing in the address of a pointer to a function with no definition available. That doesn't tell me or anyone else that the allocation was performed properly - Ed S. 2012-04-05 00:05
this->code is set really wierdly. &this->code is passed to a function that accepts CollectionExprAST** (and this param has the identifier ASTParent), then *ASTParent = new CollectionExprAST(...) allocates it - 64bit_twitchyliquid 2012-04-05 00:12
@64bit_twitchyliquid what is the value of this->Code->size() - keety 2012-04-05 00:25
Ohh wow .... I am such a complete and total retard. Under all my test cases and other things the function that populates this->code with a valid pointer is called, but for what I am doing it does not get called, therefore this->code is bogus. OMG its so simple! Anyways, thankyou all for your time, it was indeed this->code was not allocated - 64bit_twitchyliquid 2012-04-05 00:52
@64bit_twitchyliquid: You're not stupid, it happens all of the time :). That said, I would strongly recommend taking the advice in my post as there are other, not so obvious problems in your code - Ed S. 2012-04-05 01:03


1

I see nowhere in your example where you actually allocate the Code member variable. It is a pointer and what it points to needs to be allocated at some point.

There are other problems as well, though not directly related to your crash.

First, you never delete symtab or temp in GenerateCode_ToFile. This is a memory leak. For that matter, why in the world are you dynamically allocating an int there? Just declare an int on the stack and pass its address to the CodeGen function. Same goes for symtab if possible.

int i = 10;
SomeFuncThatTakesAPointer(&i);

Actually, looking more closely, you don't even use the int* parameter in the function and it isn't saved anywhere, so just get rid of it entirely.

Next...

std::vector<ExprAST*>* Code;

Pointers to vectors and vectors that store pointers are almost always wrong. You are preventing the vector from handling dynamic memory allocation and deallocation for you. You may as well just use an array at this point (ok, an array doesn't grow for you if you assign something beyond its boundaries, but still, bad practice).

Vectors use a pattern called RAII to handle safely allocating and deallocating memory for you. When you maintain a pointer to a vector you circumvent that process and are required to call delete on the vector yourself.

When you store pointers in a vector you, once again, prevent the vector from deallocating its stored objects. It will store the pointers themselves dynamically and call delete on them, but this will not deallocate what the original pointer was pointing to.

C++ is a complex language. I suggest spending some time learning more about memory management in general and patterns like RAII which can simplify the process for you.

2012-04-05 00:03
by Ed S.
Ads