26 Oct, 2009, Kayle wrote in the 1st comment:
Votes: 0
So anytime a character logs out and disconnects from the mud, the mud crashes. GDB is either being entirely unhelpful, or I'm missing something simple.
GDB said:
#0 0x000000000045d8e7 in ~Account (this=0xb144e0) at account.cpp:53
53 STRFREE( race );
(gdb) print race
No symbol "race" in current context.
(gdb) frame 0
#0 0x000000000045d8e7 in ~Account (this=0xb144e0) at account.cpp:53
53 STRFREE( race );
(gdb) print race
No symbol "race" in current context.
(gdb) print this
$1 = (Account * const) 0xb144e0
(gdb) print this->name
$2 = 0x0
(gdb) print this->race
There is no member or method named race.
(gdb) bt
#0 0x000000000045d8e7 in ~Account (this=0xb144e0) at account.cpp:53
#1 0x000000000053305f in deleteptr<Account> (ptr=@0xb13068) at templates.h:68
#2 0x0000000000524d83 in free_desc (d=0xb13040) at comm.cpp:1090
#3 0x0000000000525894 in close_socket (dclose=0xb13040, force=false) at comm.cpp:1252
#4 0x000000000052ddce in nanny_account_menu (d=0xb13040, argument=0x7fff341b84a0 "0") at comm.cpp:2140
#5 0x0000000000530151 in nanny (d=0xb13040, argument=0x7fff341b84a0 "0") at comm.cpp:4540
#6 0x0000000000531828 in game_loop () at comm.cpp:775
#7 0x0000000000532f63 in main (argc=5, argv=0x7fff341b89d8) at comm.cpp:446
(gdb) frame 0
#0 0x000000000045d8e7 in ~Account (this=0xb144e0) at account.cpp:53
53 STRFREE( race );
(gdb) list
48
49 AccountChar::~AccountChar( )
50 {
51 STRFREE( password );
52 STRFREE( name );
53 STRFREE( race );
54 STRFREE( quitLocation );
55 STRFREE( clan );
56 }
57
(gdb)


Astute readers will probably notice the same thing I did. That's not even the destructor for the account class. So.. Why is GDB saying it is?

Here are the two destructors:
Account::~Account( )
{
AccountChar *ach;
std::list< AccountChar * >::iterator iAch;

STRFREE( host );
STRFREE( name );
STRFREE( last_played );
STRFREE( password );
for( iAch = CharList.begin( ); iAch != CharList.end( ); ++iAch )
{
ach = *iAch;
CharList.remove( ach );
deleteptr( ach );
}

AccountList.remove( this );
}

AccountChar::~AccountChar( )
{
STRFREE( password );
STRFREE( name );
STRFREE( race );
STRFREE( quitLocation );
STRFREE( clan );
}


And the deleteptr template:
// Thanks to Ksilyan for this little trick. A nifty little template that behaves like DISPOSE used to.
template< typename T > void deleteptr( T* & ptr )
{
delete ptr;
ptr = NULL;
}


I'm at a loss, and with GDB behaving like this, I'm not sure how to proceed, or even whether it's being reliable. If any further code is needed to help me figure this out just say so, and I'll toss it in here… I'm tired of ripping my hair out trying to figure this out. =/
26 Oct, 2009, bbailey wrote in the 2nd comment:
Votes: 0
Kayle said:
Astute readers will probably notice the same thing I did. That's not even the destructor for the account class. So.. Why is GDB saying it is?


The KISS answer is: are you sure that source file matches the actual object code being debugged? This sort of behavior is very common when the source files and compiled code just plain don't match up, though typically gdb will notice and complain about that.. At the very least I'd suggest a full recompile and try to debug a new dump.
26 Oct, 2009, Kayle wrote in the 3rd comment:
Votes: 0
Done that. But I'll try it again and repost the GDB output.
26 Oct, 2009, Kayle wrote in the 4th comment:
Votes: 0
Bit long. but the whole process.

Quote
[kayle@valhalla src]$ rm ../area/core
[kayle@valhalla src]$ make clean
[kayle@valhalla src]$ make
make -s swtsw
$ make
make -s swtsw
[- Compiling o/imc.o….
[- Compiling o/sql.o….
[- Compiling o/account.o….
[- Compiling o/act_comm.o….
[- Compiling o/act_info.o….
[- Compiling o/act_move.o….
[- Compiling o/act_obj.o….
[- Compiling o/act_wiz.o….
[- Compiling o/area.o….
[- Compiling o/ban.o….
[- Compiling o/bank.o….
[- Compiling o/boards.o….
[- Compiling o/buidty.o….
[- Compiling o/build.o….
[- Compiling o/calendar.o….
[- Compiling o/changes.o….
[- Compiling o/channels.o….
[- Compiling o/character.o….
[- Compiling o/chess.o….
[- Compiling o/clans.o….
[- Compiling o/color.o….
[- Compiling o/comm.o….
[- Compiling o/commands.o….
[- Compiling o/const.o….
[- Compiling o/db.o….
[- Compiling o/dns.o….
[- Compiling o/editor.o….
[- Compiling o/events.o….
[- Compiling o/event_handler.o….
[- Compiling o/fight.o….
[- Compiling o/finger.o….
[- Compiling o/handler.o….
[- Compiling o/hashstr.o….
[- Compiling o/helps.o….
[- Compiling o/hint.o….
[- Compiling o/hotboot.o….
[- Compiling o/interp.o….
[- Compiling o/intro.o….
[- Compiling o/languages.o….
[- Compiling o/liquids.o….
[- Compiling o/magic.o….
[- Compiling o/makeobjs.o….
[- Compiling o/mapper.o….
[- Compiling o/mccp.o….
[- Compiling o/misc.o….
[- Compiling o/mobile.o….
[- Compiling o/mssp.o….
[- Compiling o/mud_comm.o….
[- Compiling o/mud_prog.o….
[- Compiling o/mudmsg.o….
[- Compiling o/objects.o….
[- Compiling o/news.o….
[- Compiling o/pcdata.o….
[- Compiling o/pfiles.o….
[- Compiling o/planets.o….
[- Compiling o/player.o….
[- Compiling o/races.o….
[- Compiling o/rent.o….
[- Compiling o/renumber.o….
[- Compiling o/reset.o….
[- Compiling o/rooms.o….
[- Compiling o/rss.o….
[- Compiling o/save.o….
[- Compiling o/sector.o….
[- Compiling o/services.o….
[- Compiling o/sha256.o….
[- Compiling o/shops.o….
[- Compiling o/skills.o….
[- Compiling o/space.o….
[- Compiling o/spcships.o….
[- Compiling o/special.o….
[- Compiling o/sysdata.o….
[- Compiling o/tables.o….
[- Compiling o/track.o….
[- Compiling o/update.o….
[- Compiling o/variables.o….
[- Compiling o/vector3.o….
[- Compiling o/weather.o….
[- Generating dependency file …
[- Done compiling mud.
make -s dns
[- Done compiling DNS resolver.
[kayle@valhalla src]$ nohup ./startup &
[1] 3540
[kayle@valhalla src]$ nohup: ignoring input and appending output to `nohup.out'
[kayle@valhalla src]$ gdb swtsw ../area/core
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>;
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu"…
Reading symbols from /lib/libz.so.1…done.
Loaded symbols for /lib/libz.so.1
Reading symbols from /usr/lib/libmysqlclient.so.15…done.
Loaded symbols for /usr/lib/libmysqlclient.so.15
Reading symbols from /lib/libdl.so.2…done.
Loaded symbols for /lib/libdl.so.2
Reading symbols from /usr/lib/libstdc++.so.6…done.
Loaded symbols for /usr/lib/libstdc++.so.6
Reading symbols from /lib/libm.so.6…done.
Loaded symbols for /lib/libm.so.6
Reading symbols from /lib/libgcc_s.so.1…done.
Loaded symbols for /lib/libgcc_s.so.1
Reading symbols from /lib/libc.so.6…done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/libpthread.so.0…done.
Loaded symbols for /lib/libpthread.so.0
Reading symbols from /lib/libcrypt.so.1…done.
Loaded symbols for /lib/libcrypt.so.1
Reading symbols from /lib/libnsl.so.1…done.
Loaded symbols for /lib/libnsl.so.1
Reading symbols from /lib/ld-linux-x86-64.so.2…done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
Reading symbols from /lib/libnss_files.so.2…done.
Loaded symbols for /lib/libnss_files.so.2
Reading symbols from /lib/libnss_dns.so.2…done.
Loaded symbols for /lib/libnss_dns.so.2
Reading symbols from /lib/libresolv.so.2…done.
Loaded symbols for /lib/libresolv.so.2
Core was generated by `../src/swtsw 2075'.
Program terminated with signal 11, Segmentation fault.
[New process 3546]
#0 0x000000000045d8e7 in ~Account (this=0xb17700) at account.cpp:53
53 STRFREE( race );
(gdb) list
48
49 AccountChar::~AccountChar( )
50 {
51 STRFREE( password );
52 STRFREE( name );
53 STRFREE( race );
54 STRFREE( quit_location );
55 STRFREE( clan );
56 }
57
(gdb) bt
#0 0x000000000045d8e7 in ~Account (this=0xb17700) at account.cpp:53
#1 0x000000000053305f in deleteptr<Account> (ptr=@0xb166b8) at templates.h:68
#2 0x0000000000524d83 in free_desc (d=0xb16690) at comm.cpp:1090
#3 0x0000000000525894 in close_socket (dclose=0xb16690, force=false) at comm.cpp:1252
#4 0x000000000052ddce in nanny_account_menu (d=0xb16690, argument=0x7fff0c79fab0 "0") at comm.cpp:2140
#5 0x0000000000530151 in nanny (d=0xb16690, argument=0x7fff0c79fab0 "0") at comm.cpp:4540
#6 0x0000000000531828 in game_loop () at comm.cpp:775
#7 0x0000000000532f63 in main (argc=2, argv=0x7fff0c79ffe8) at comm.cpp:446
(gdb) [/quote]
26 Oct, 2009, KaVir wrote in the 5th comment:
Votes: 0
Quote
(gdb) print this->name
$2 = 0x0
(gdb) print this->race
There is no member or method named race.

I assume race is a member of AccountChar, and not derived from another class?

And what exactly is STRFREE() out of interest? Some sort of macro? Is it possible race wasn't properly initialised?
26 Oct, 2009, Kayle wrote in the 6th comment:
Votes: 0
STRFREE is:
#define STRFREE(point)                           \
do \
{ \
if((point)) \
{ \
if( !in_hash_table( (point) ) ) \
{ \
log_printf( "&RSTRFREE called on str_dup pointer: %s, line %d\n", __FILE__, __LINE__ ); \
log_string( "Attempting to correct." ); \
free( (void*) (point) ); \
} \
else if( str_free((point)) == -1 ) \
log_printf( "&RSTRFREEing bad pointer: %s, line %d\n", __FILE__, __LINE__ ); \
(point) = NULL; \
} \
else \
(point) = NULL; \
} while(0)


Account/AccountChar classes:
class AccountChar
{
private:
AccountChar( const AccountChar & );
AccountChar &operator = ( const AccountChar & );

public:
AccountChar( );
virtual ~AccountChar( );

public:
Account *account;
const char *password;
const char *name;
const char *race;
const char *quitLocation;
const char *clan;
int topLevel;
};

extern std::list< Account * > AccountList;
class Account
{
private:
Account( const Account & );
Account &operator = ( const Account & );

public:
Account( );
virtual ~Account( );

public:
std::list< AccountChar * > CharList;
AccountChar *pending;
const char *host;
const char *name;
const char *lastPlayed;
const char *password;
time_t timer;
bool immortal;
bool multiplay;
int alts;
int accountTopLevel;
};


The reason it said "There is no member or method named race." is because it was looking at Account, when the code displayed is from AccountChar.

[Edit:] Figured I'd throw the Constructors up too in case there was something in them wrong.
std::list< Account * > AccountList;

Account::Account( ) :
pending(NULL), host(NULL), name(NULL), lastPlayed(NULL), password(NULL),
timer(0), immortal(0), multiplay(0), alts(0), accountTopLevel(0)
{
}

AccountChar::AccountChar( ) :
account(NULL), password(NULL), name(NULL), race(NULL),
quitLocation(NULL), clan(NULL), topLevel(0)
{
}
26 Oct, 2009, David Haley wrote in the 7th comment:
Votes: 0
When you delete a derived class, superclass destructors are called as well.

$ cat test.cpp 


#include <iostream>

using namespace std;

class C1 {
public:
virtual ~C1() { cout << "~C1"; }
};
class C2 : public C1 {
public:
virtual ~C2() { cout << "~C2"; }
};

int main() {
C2* o = new C2();
cout << "deleting o…" << endl;
delete o;
}

$ g++ test.cpp
$ ./a.out
deleting o…
~C2~C1$

My bet is that something is being freed twice. I'd run it through valgrind.
26 Oct, 2009, KaVir wrote in the 8th comment:
Votes: 0
David Haley said:
When you delete a derived class, superclass destructors are called as well.

Assuming they're virtual, yes. But once the derived class destructor has been called, its data members assume undefined values (and likewise, virtual functions resolve to the base class, which is why virtual functions should never be called inside a destructor). That was the angle I was considering. But a double free does sound more likely.
26 Oct, 2009, Kayle wrote in the 9th comment:
Votes: 0
Valgrind spoke a different tale, and while I worked on it a bit, I wasn't get much of anywhere, so David's gonna have a peek when he gets home from work. I'll post again once the problems been resolved, because David's like Bob the Builder. He can fix it. :P
27 Oct, 2009, Erok wrote in the 10th comment:
Votes: 0
Kayle said:
deleteptr( ach );

This looks odd… Don't you need to specify the data type used by the template like this… deleteptr<AccountChar>(ach)

If that's not it, use gdb to insert a breakpoint in the destructor and then single step through the execution to isolate the problem.
27 Oct, 2009, David Haley wrote in the 11th comment:
Votes: 0
Erok said:
Don't you need to specify the data type used by the template like this… deleteptr<AccountChar>(ach)

The compiler does that for you with automatic specialization, actually.

One problem was the following:
for( iAch = CharList.begin( ); iAch != CharList.end( ); ++iAch )
{
ach = *iAch;
CharList.remove( ach ); // <— HERE
deleteptr( ach );
}

You generally don't want to modify STL containers as you iterate over them, because it can invalidate iterators and so forth. This was a double-free IIRC. After fixing this there was still another problem that I didn't know how to reproduce when I looked at it, so I'll be taking another look at some point.
27 Oct, 2009, Erok wrote in the 12th comment:
Votes: 0
David Haley said:
The compiler does that for you with automatic specialization, actually.

I figured something like that was happening since it was building without warnings/errors, but am used to being explicit about it.

Yes, list removal will invalidate the iterator. I don't think it's a double free though. The space allocated for the pointer itself is freed when removed from the list, but not the space of the object that it was pointing to. So I assume the code looks something like this now…

for (std::list<AccountChar*>::iterator iter(CharList.begin()), end(CharList.end()); iter != end; ++iter)
{
deleteptr(*iter);
}

CharList.clear();

What's the other problem?
27 Oct, 2009, David Haley wrote in the 13th comment:
Votes: 0
It's not the object that's being freed twice, it was something in the list (at least according to valgrind). I'm not sure what the other problem is yet since I haven't had a chance to run it through the debugger again.
27 Oct, 2009, David Haley wrote in the 14th comment:
Votes: 0
Actually, now that I think about it, maybe it was accessing memory that was already freed… it was kind of late last night and I don't really remember. You're right that that sounds more plausible for this particular piece. Maybe I was seeing a double-free somewhere else…
28 Oct, 2009, David Haley wrote in the 15th comment:
Votes: 0
So I tracked down the double-free that I was seeing (and mixed up with this other problem). A data structure was copying a shared string with a straight pointer copy, but then freeing it with STRFREE. As a result, the first time the structure was created and destroyed, things are happy, but the next time such a structure was destroyed, the pointer was freed again.

C++ makes it far easier to have self-managing shared strings, by the way, that can help wipe out this kind of problem.
28 Oct, 2009, Kayle wrote in the 16th comment:
Votes: 0
Yeah, Baby steps. std::string and the like are not something that's particularly friendly to stuff into Diku if I recall correctly from when Samson did it for AFKMud.
28 Oct, 2009, David Haley wrote in the 17th comment:
Votes: 0
Indeed, it's not an easy conversion. My policy is to not go in and convert everything, but instead when I add new fields, to use the string classes.
29 Oct, 2009, Runter wrote in the 18th comment:
Votes: 0
David Haley said:
Indeed, it's not an easy conversion. My policy is to not go in and convert everything, but instead when I add new fields, to use the string classes.


That's precisely what I have done in the past. Not much extra work either.
31 Oct, 2009, Kaz wrote in the 19th comment:
Votes: 0
I gave a longer e-mail to Kayle because I had some registration problems, but thought I'd post for the benefit of all anyway.

I think the problem is that remove() doesn't remove() - it just moves all elements with a certain pattern to the end of the list and returns an iterator to the first (re)moved element. So, by iterating through the list, deleting each and then moving it to the end, you're eventually going to delete one of the elements you removed.

In this case, either erase() should be used (careful, because it invalidates the current iterator and returns a valid iterator to the next element) or just delete them and leave them because it's a destructor and the list will clean itself up.
31 Oct, 2009, David Haley wrote in the 20th comment:
Votes: 0
Actually, in this case, list.remove() does in fact remove. You're thinking about the global std::remove function. See also the std::list.remove documentation.
0.0/22