Segmentation fault when destructor called

Jan 28, 2012 at 7:08pm

Hi,

When main returns I get a Segmentation fault. Debugging with valgrind shows that an invalid read of size 4 (at address 0x0) is the cause when a destructor of one of my classes is called. The destructor is empty and the class (a "transaction table" called "TransRecTable") does not contain any pointers but it does have an array of "TableEntry" objects...

1
2
3
4
5
6
7
8
9
10
11
12
class TransRecTable
{
public:
    TransRecTable(void);
    virtual ~TransRecTable(void);
    void                put_entry(size_t idx, const TableEntry& entry);
    const Transaction&  get_entry(size_t idx) const;                     
    entry_status_t      get_entry_status(size_t idx) const;
    size_t              max_size(void) const;
protected:
    TableEntry  m_entries[N_ENTRIES];
};


Below is the output from valgrind...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
==4095== Invalid read of size 4
==4095==    at 0x804B0C2: TransRecTable::~TransRecTable() (TransRecTable.cpp:43)
==4095==    by 0x8049378: MSystems::~MSystems() (MSystems.cpp:48)
==4095==    by 0x419AA6E: __run_exit_handlers (exit.c:78)
==4095==    by 0x419AACE: exit (exit.c:100)
==4095==    by 0x4181E3E: (below main) (libc-start.c:258)
==4095==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==4095== 
==4095== 
==4095== Process terminating with default action of signal 11 (SIGSEGV)
==4095==  Access not within mapped region at address 0x0
==4095==    at 0x804B0C2: TransRecTable::~TransRecTable() (TransRecTable.cpp:43)
==4095==    by 0x8049378: MSystems::~MSystems() (MSystems.cpp:48)
==4095==    by 0x419AA6E: __run_exit_handlers (exit.c:78)
==4095==    by 0x419AACE: exit (exit.c:100)
==4095==    by 0x4181E3E: (below main) (libc-start.c:258) 


I haven't seen this kind of error before, and am not having any success getting to the bottom of it. Can anyone explain what this means and how I can fix this bug?

Any help much appreciated!
Jan 28, 2012 at 7:44pm
Address 0x0 ...

Trying to dereference NULL?
Jan 28, 2012 at 8:19pm

I don't know what could be getting dereferenced.

Main has returned and a global object which contains a "TransRecTable" object has had its destructor called. The destructor of the "TransRecTable" has then been called. The destructor is going through each member of the "m_entries" array and calling the destructor of each member. When it reaches one of the members (the second in the array) I get the segmentation fault. Any ideas on what the program could be attempting to dereference?

I never used new or delete in the program.
Jan 28, 2012 at 8:47pm
Why virtual destructor? you haven't declared a virtual member function...post the implementation of the class too.
Last edited on Jan 28, 2012 at 8:47pm
Jan 28, 2012 at 8:50pm
"Why virtual destructor? you haven't declared a virtual member function...post the implementation of the class too."
Jan 28, 2012 at 8:51pm
Since the problem appears to be in your destructor, it would help if you posted your destructor.

EDIT:

Actually....

The destructor is going through each member of the "m_entries" array and calling the destructor of each member.


You know you're never supposed to explicitly call destructors, right? If you're doing that, it's wrong. Destructors are called automatically.

Just about the only time you explicitly call dtors is when you use placement new.
Last edited on Jan 28, 2012 at 8:52pm
Jan 28, 2012 at 9:03pm

Here's the code for the "TransRecTable" class, and the "TableEntry" class. As you can see, the destructor for both classes is empty, except for a "cout" statement I put in as I was attempting to debug this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include "TransRecTable.h"

TableEntry::TableEntry(void):
m_transaction(),
m_status(t_vacant)
{
}

TableEntry::TableEntry(const Transaction& taction):
m_transaction(taction),
m_status(t_vacant)
{
}

TableEntry::TableEntry(const TableEntry& src):
m_transaction(src.m_transaction),
m_status(src.m_status)
{
}

TableEntry::~TableEntry()
{
    //cout << "table entry dtor called\n";
}

TableEntry& TableEntry::operator=(const TableEntry& rhs)
{
    if (this == &rhs) return *this; // handle self assignment
    m_transaction = rhs.m_transaction;
    m_status = rhs.m_status;
    return (*this);
}

//--------------------------------------------
//--------------------------------------------
//--------------------------------------------

TransRecTable::TransRecTable()
{
    //ctor
}

TransRecTable::~TransRecTable()
{
    cout << "transaction record table dtor called\n";
    //dtor
}

void TransRecTable::put_entry(size_t idx, const TableEntry& entry)
{
    m_entries[idx] = entry;
    CAS(&m_entries[idx].m_status, t_vacant, t_ready);
}


The table element is comprised of a size_t and a Transaction object. I guess it could be something to do with the "Transaction" class...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Transaction
{
        friend ostream& operator<<(ostream& ostr, const Transaction& rhs);
    public:
        Transaction(size_t trans_id = 0, const int* read_memory = NULL, t_ftn_t ftn = NULL);
        Transaction(const Transaction& src);
        virtual ~Transaction(void);

        Transaction& operator=(const Transaction& rhs);

        void        operator()(const int* context = NULL);
        void        retry(void);
        bool        commit(void);
        int         read(size_t location);
        void        write(size_t location, int value);

        void        event(mem_id_t event)           {m_event = event;}
        void        status(tstatus_t status)        {m_tstatus = status;}
        void        read_memory(const int* ptr)     {m_read_memory = ptr;}

        size_t      trans_id(void) const            {return m_trans_id;}
        mem_id_t    event(void) const               {return m_event;}
        tstatus_t   status(void) const              {return m_tstatus;}
        const int*  write_set(void) const           {return m_write_set;}
        const int*  read_memory(void) const         {return m_read_memory;}

    protected:
        size_t      m_trans_id;
        t_ftn_t     m_tfunction;
        tstatus_t   m_tstatus;
        mem_id_t    m_event;
        int         m_write_set[TRTN_SET_SIZE];
        bool        m_write_rec[TRTN_SET_SIZE];
        const int*  m_read_memory;
};


The destructor for that class is empty too though...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/**
* ctor
*/
Transaction::Transaction(size_t trans_id, const int* read_memory, t_ftn_t ftn):
m_trans_id(trans_id),
m_tfunction(ftn),
m_tstatus(trtn_ready),
m_read_memory(read_memory)
{
    memset(m_write_rec, 0, (sizeof(bool) * TRTN_SET_SIZE));
    memset(m_write_set, 0, (sizeof(int) * TRTN_SET_SIZE));
}

/**
* copy ctor
*/
Transaction::Transaction(const Transaction& src):
m_trans_id(src.m_trans_id),
m_tfunction(src.m_tfunction),
m_tstatus(src.m_tstatus),
m_event(src.m_event),
m_read_memory(src.m_read_memory)
{
    memcpy(m_write_rec, src.m_write_rec, (sizeof(bool) * TRTN_SET_SIZE));
    memcpy(m_write_set, src.m_write_set, (sizeof(int) * TRTN_SET_SIZE));
}

/**
* dtor
*/
Transaction::~Transaction()
{
    //dtor
    cout << "transaction " << m_trans_id << " dtor called\n";
}

/**
* Execute the transaction
*/
void Transaction::operator()(const int* context)
{
    assert(context != NULL);
    m_read_memory = context;
    m_tfunction(*this);
}

/**
* Reassign the transction
*/
Transaction& Transaction::operator=(const Transaction& rhs)
{
    if(&rhs != this)
    {
        m_trans_id = rhs.m_trans_id;
        m_tfunction = rhs.m_tfunction;
        m_tstatus = rhs.m_tstatus;
        m_event = rhs.m_event;
        m_read_memory = rhs.m_read_memory;
        memcpy(m_write_rec, rhs.m_write_rec, (sizeof(bool) * TRTN_SET_SIZE));
        memcpy(m_write_set, rhs.m_write_set, (sizeof(int) * TRTN_SET_SIZE));
    }
    return *this;
}

/**
* Read
*/
int Transaction::read(size_t location)
{
    if(m_write_rec[location])
        return m_write_set[location];

    return m_read_memory[location];
}

/**
* Write
*/
void Transaction::write(size_t location, int value)
{
    m_write_set[location] = value;
    m_write_rec[location] = true;
}


Jan 28, 2012 at 9:48pm

You know you're never supposed to explicitly call destructors, right? If you're doing that, it's wrong. Destructors are called automatically.


I'm not calling the destructor. The destructor is being called automatically after main returns (The object is a global object) and this is when I'm getting the seg-fault.

Valgrind says "invalid read". What could the program be reading? Has this type of problem happened to anyone else? What are the "usual suspects" when a seg-fault occurs when a destructor is called from a global object?
Jan 28, 2012 at 9:58pm

Oh, hang on. The "TransRecTable" object field was in the last position in the encompassing class declaration, after the "m_slots" field (see below). I changed the "m_ttable" field's position to be first and the segmentation fault is no longer occurring. Maybe when some part of the program is writing to one of the "m_slots" fields it is over-writing into the "m_ttable" object memory?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MSystems
{
    friend ostream& operator<<(ostream& ostr, const MSystems& rhs);
public:
    MSystems(void);
    MSystems(const MSystems& src);
    virtual ~MSystems();

    MSystems& operator=(const MSystems& rhs);
    bool enter(const Transaction& transaction);
    const Node& node(size_t idx) const {return m_nodes[idx];}

protected:
    Node& root(void){return m_nodes[0];}
    //void update_node_path(size_t slot_no);
    bool synchronize_transactions(size_t& slot_no, size_t& commit_count);
    CommitChallenge& get_challenge(CommitChallenge& to_challenge);
    bool contest(CommitChallenge& c1, CommitChallenge& c2);

    Node            m_nodes[N_NODES];
    Slot            m_slots[N_SLOTS];
    TransRecTable   m_ttable;
};
Jan 29, 2012 at 7:43am
Yeah this is sounding like heap corruption. You must be stepping out of bounds on one of your arrays.
Jan 29, 2012 at 10:43am
Hi ,
I am not able to understand some of the points .. as

How and why you are using ...m_tstatus(trtn_ready),
in the constructor .
1
2
3
4
5
6
7
8
9
Transaction::Transaction(size_t trans_id, const int* read_memory, t_ftn_t ftn):
m_trans_id(trans_id),
m_tfunction(ftn),
m_tstatus(trtn_ready),
m_read_memory(read_memory)
{
    memset(m_write_rec, 0, (sizeof(bool) * TRTN_SET_SIZE));
    memset(m_write_set, 0, (sizeof(int) * TRTN_SET_SIZE));
}



I am not able to understand m_tfunction(*this); in the function .

1
2
3
4
5
6
void Transaction::operator()(const int* context)
{
    assert(context != NULL);
    m_read_memory = context;
    m_tfunction(*this);
}



Again i am not able to see TableEntry.h ...please explain to get what is going on ..

please explain .. thanks in advance ..
Last edited on Jan 29, 2012 at 10:52am
Jan 29, 2012 at 4:57pm

Hi,

I got to the bottom of the problem. I had a loop in one of my class methods that was set to an incorrect maximum number of runs. This was causing corruption to memory, overwriting on of the table entries. For some reason, I thought valgrind would have picked that up when I debugged the program, and when I didn't see any mention of that happening in valgrind, I discounted that as a possible cause.

I guess if I had put an assert when I wrote the loop in question, I would have caught the error sooner. Lesson learned for future.

I am not able to understand some of the points .. as


Those points are quite complicated to explain blucoder, I'm implementing a transactional memory system and those things you quoted are part of the transaction mechanism.

Thanks to those who replied!
Topic archived. No new replies allowed.