BorlandTalk.com Forum Index BorlandTalk.com
Borland discussion newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Weird Run-time error

 
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> C++ Builder (VCL Components Development)
View previous topic :: View next topic  
Author Message
E_RGilbert
Guest





PostPosted: Tue Jun 06, 2006 5:10 am    Post subject: Weird Run-time error Reply with quote



I am using the C++ Personality in BDS2006 (Update2).

I have done various Components in 2 Packages - which I use in a MDI
Application (on the Child Form).

As I have been progessively adding functionality, I have suddenly started
having a really weird run-time problem in code that actually used to work (I
am not sure if the code stopped working with installing Update 2). I have
also applied the Update 2 "PopPack.h" Hotfix - but it didn't help.

The symptoms are that in some class method being called, the 1st variable
that is declared in this method will have a value of "1" on entry (and not
some big unitialised "random" value as before). Assigning any other value is
to this variable in this method makes no difference, it remains "1". Also,
changing the value of the variable using the Debugger also makes no
difference - it remains "1" ! (this is the really weird part to me - you
cannot change the value in any way!).

For example, in the following Constructor:

__fastcall TPrEF::TPrEF(EFHdrType efHdr,TPrDF* parent,TTreeNode*
treeNode,TPrProgressUpdate *progress,
AnsiString description,bool
invalidated,bool shareable):
TPrFile(efHdr.id,parent,treeNode,progress,description,efHdr.arr_id,efHdr.arr_rec,efHdr.rfm_access,invalidated,shareable)
{
efType = (Ef_Type)(efHdr.status &0x0F);
inFlash = false;
readAndUpdateWhenInvalidated = (bool)(efHdr.status &0x20);
increaseAllowed = !((bool)(efHdr.status &0x80));

int nn = 0;
int ll = 0;
if(efHdr.rec_len)
ll = (int)efHdr.rec_len;
else
ll = (int)(efHdr.len-14);
if(efHdr.rec_len)
nn = (int)((efHdr.len-14)/efHdr.rec_len);
else
nn = 1;
content = new TEfContent(NULL, this);
SetEfContentSize( content, ll, nn);
}

The value of "int nn" is "stuck" (for want of a better word) on "1" and
cannot be changed. I can fudge this by declaring another variable "kk"
before "nn" is declared, as in:

__fastcall TPrEF::TPrEF(EFHdrType efHdr,TPrDF* parent,TTreeNode*
treeNode,TPrProgressUpdate *progress,
AnsiString description,bool
invalidated,bool shareable):
TPrFile(efHdr.id,parent,treeNode,progress,description,efHdr.arr_id,efHdr.arr_rec,efHdr.rfm_access,invalidated,shareable)
{
efType = (Ef_Type)(efHdr.status &0x0F);
inFlash = false;
readAndUpdateWhenInvalidated = (bool)(efHdr.status &0x20);
increaseAllowed = !((bool)(efHdr.status &0x80));

int kk, nn = 0;
int ll = 0;
...

But this just causes the same thing to happen somewhere late on, such as in
this method:

TPrFile* __fastcall TPrDF::add(TPrFile* file)
{
TTreeNode* tempTreeNode = this->getTreeNode();

if (tempTreeNode)
file->setTreeNode( tempTreeNode->Owner->AddChildObject(tempTreeNode,
"" ,file) );

children.push_back(file);
file->setParent(this);
return file;
}

Here "tempTreeNode " gets stuck on "1", even though it is clear (using the
Debugger) that the getTreeNode() method call returns a valid TreeNode.

I've initially suspected that it may have something to do with Data
Alignment (overwriting boundaries of structs maybe), especialy since I have
some structures which are packed on 1 BYTE alignment, as in:

#pragma pack(push, 1)
typedef struct {
BYTE type; // DOM (file) type: DOM_OBJ_EF
USHORT len; // DOM internal size (including header)
BYTE sfi; // Short file ID
USHORT id; // EF identifier
USHORT arr_id; // EF-ARR ID
BYTE arr_rec; // EF-ARR record no
BYTE rfm_access; // RFM access conditions
BYTE status;
BYTE parent_dfi; // parent DF identifier
BYTE rec_len; // record length
BYTE last_rec; // last record updated if cyclic (0..N-1),
otherwise '00'
}EFHdrType; //14 bytes
#pragma pack(pop)

I've tried to reproduce the symptoms in a small VCL Forms app - to allow
posting with this Post - but in my test app everything works fine (including
the above BYTE alignment).

If I consider the differnces between the real app and my test app:

1. The 2 classes mentioned (as well as the structure) in which this occurs
are in one of the Component Packages (even though neither are actual
Components), whilst in my test app does not use the Package - I simply
hardcoded the same actions into the Constructors, used the same Struct's,
etc.

2. The real app is obviously much bigger than the Test app, and uses some
3rd Party Components (from TMS and Raize). the Test app has a single button.

I've also tried to comment out the #pragma push and #pragma pop statements
round the struct and compiling the Components as well as the App with Data
alignment of 1 Byte - but that did not help, I still got the exact same
effect. So now I am not so sure that it does actually have to do with Data
alignment.

Any help / hints / advice will be greatly appreaciated since I have
struggled with this for days now without any progress.

Also, I was really excited about the Update 2 "PopPack.h" Hotfix - I thought
this could be it. But it did not make any difference. Do I need to delete
any precompiled headers maybe?

Regards,
Ettienne
Back to top
Remy Lebeau (TeamB)
Guest





PostPosted: Tue Jun 06, 2006 5:17 am    Post subject: Re: Weird Run-time error Reply with quote



"E_RGilbert" <rgilbert at telkomsa dot net> wrote in message
news:4484c803 (AT) newsgroups (DOT) borland.com...

Quote:
The symptoms are that in some class method being called, the 1st variable
that is declared in this method will have a value of "1" on entry (and
not
some big unitialised "random" value as before). Assigning any other value
is
to this variable in this method makes no difference, it remains "1". Also,
changing the value of the variable using the Debugger also makes no
difference - it remains "1" ! (this is the really weird part to me - you
cannot change the value in any way!).

Such behavior usually suggests that you have multiple variables of the same
name, usually global variables, and you are making changes to one variable
when you think you are making changes to another. Another possibility is
the stack becoming corrupted.

Quote:
The value of "int nn" is "stuck" (for want of a better word) on "1"

Is it initialized to 1 right away? Or does it only become stuck after it
has been assigned when evaluating the efHdr.rec_len member? What does
TEfContent's constructor do? What does SetEfContentSize() do?

Quote:
The 2 classes mentioned (as well as the structure) in which this occurs
are in one of the Component Packages (even though neither are actual
Components), whilst in my test app does not use the Package - I simply
hardcoded the same actions into the Constructors, used the same Struct's,
etc.

Are the classes exported from the package properly? Did you verify the
structure size in both projects by using sizeof()?


Gambit
Back to top
Remy Lebeau (TeamB)
Guest





PostPosted: Tue Jun 06, 2006 8:11 am    Post subject: Re: Weird Run-time error Reply with quote



"E_RGilbert" <rgilbert at telkomsa dot net> wrote in message
news:44852750 (AT) newsgroups (DOT) borland.com...

Quote:
Correct. But that is not the case here. The 2 cases where this
happens the variables are local to the method.

That does not exclude what I said. If a global has the same name as a local
variable, the code could still assign values to the global instead of the
local, and vice versa.

Quote:
The criteria seems to be that it has to be the 1st variable that
are declared in the method

That is not required criteria for assigning values to a variable. If your
project is requiring that, then something is seriously screwed up with it.
I'm still not ruling out the possibility of stack corruption yet.

Quote:
Ouch. Any advice on how to check for this?

Make sure that you never write more data into any declared buffers than
there is space declared for them. Make sure that thr calling conventions on
all functions are accurate, especially functions in any DLLs or static
libraries that you may be using. Use the debugger to make sure that the
CPU's ESP register has the same valid when leaving a function that it had
just before entering the function.

Quote:
It is initialized to "1" right away.

Then your stack is very likely corrupted. Your code is explicitally
initializing the variable to 0, so stack corruption is about the only way
the variable could get initialized to 1 instead of 0, meaning that your 0
value is getting written to the wrong address on the stack than it should
be.

Quote:
Or does it only become stuck after it
has been assigned when evaluating the efHdr.rec_len member?

No, it is stuck immediately. I also tried the following to eliminate that
any of the data passed in the method arguments are not causing this - the
problem remained in that 'nn' is stuck on '1' as soon as the method is
entered:

The __fastcall calling convention passes most function parameters using CPU
registers instead of the stack, so it is very unlikely that any of them are
causing the problem. The stack is likely being corrupted long before your
function is ever being called, and you just happen to have noticed the
prolem at that point in the chain of function calls.

Quote:
Is it a problem making the Owner NULL in creating the TEfContent object?

No. TComponent works just fine with a NULL Owner assigned.

Quote:
It creates a buffer of the required size in the TEfContent object:

void TPrEF::SetEfContentSize(TEfContent *c, int rec_size, int no_recs)
{
c->rec_len = rec_size;
c->no_recs = no_recs;
c->Create(rec_size*no_recs);
}

You are assigning the rec_len and no_recs members before actually allocating
the buffer. You should be setting them afterwards instead. If something
goes wrong with the allocation, those members would contain incorrect
values. If you use those values later on to access the buffer, you could be
accessing the wrong memory.

A better design cwould be to pass the new sizes to Create() and additional
parameters and let Create() manage the assignment of the members. That way,
the members are only updated if the buffer itself is updated.

Quote:
catch(EOutOfMemory &e)

You should be catching exceptions by 'const' reference, ie:

catch(const EOutOfMemory &e)

Quote:
catch(Exception &e)

You are not catching C runtime exceptions at all, only VCL exceptions. The
'new' operator will not throw a VCL exception if it fails to allocate a
memory block.


Gambit
Back to top
E_RGilbert
Guest





PostPosted: Tue Jun 06, 2006 8:11 am    Post subject: Re: Weird Run-time error Reply with quote

"Remy Lebeau (TeamB)" wrote: in message
news:4484cbec$1 (AT) newsgroups (DOT) borland.com...

Quote:
Such behavior usually suggests that you have multiple variables of the
same
name, usually global variables, and you are making changes to one variable
when you think you are making changes to another.

Correct. But that is not the case here. The 2 cases where this happens the
variables are local to the method. The criteria seems to be that it has to
be the 1st variable that are declared in the method, but excluding any
arguments passed to the method -which does not seem affected. It does not
seem to happen to class variables either. I find it very strange.


Quote:
Another possibility is the stack becoming corrupted.

Ouch. Any advice on how to check for this?


Quote:
Is it initialized to 1 right away?

It is initialized to "1" right away.


Quote:
Or does it only become stuck after it
has been assigned when evaluating the efHdr.rec_len member?

No, it is stuck immediately. I also tried the following to eliminate that
any of the data passed in the method arguments are not causing this - the
problem remained in that 'nn' is stuck on '1' as soon as the method is
entered:

__fastcall TPrEF::TPrEF(EFHdrType efHdr,TPrDF* parent,TTreeNode*treeNode,
TPrProgressUpdate *progress,AnsiString description,bool invalidated,bool
shareable):
TPrFile(efHdr.id,parent,treeNode,progress,description,efHdr.arr_id,efHdr.arr_rec,efHdr.rfm_access,invalidated,shareable)
{
int nn = 0;
int ll = 0;

efType = (Ef_Type)(efHdr.status &0x0F);
inFlash = false;
readAndUpdateWhenInvalidated = (bool)(efHdr.status &0x20);
increaseAllowed = !((bool)(efHdr.status &0x80));

if(efHdr.rec_len)
ll = (int)efHdr.rec_len;
....


Quote:
What does TEfContent's constructor do?

__fastcall TEfContent::TEfContent(TComponent* Owner, TPrEF *ef)
: TVarBinArray(Owner)
{
rec_len = 0;
no_recs = 1;
this->ef = ef;
}

TEfContent is descended from TVarBinArray:

__fastcall TVarBinArray::TVarBinArray(TComponent* Owner)
: TComponent(Owner)
{
Ffill = 'F';
fill = 0xFF;
Fbin_val = NULL;
Fbin_val_len = 0;
buf_size = 0;
Fmem_view = NULL;
Fsync_with_mem_view = false;
has_changed = false;
alloc_buf_chunk = DEF_ALLOC_BUF_CHUNK;
}

Is it a problem making the Owner NULL in creating the TEfContent object? As
in:
content = new TEfContent(NULL, this);


Quote:
What does SetEfContentSize() do?
It creates a buffer of the required size in the TEfContent object:


void TPrEF::SetEfContentSize(TEfContent *c, int rec_size, int no_recs)
{
c->rec_len = rec_size;
c->no_recs = no_recs;
c->Create(rec_size*no_recs);
}

void __fastcall TVarBinArray::Create( int length )
{
try
{
BYTE *pb;
if( (length > buf_size) ||
(length < buf_size - alloc_buf_chunk) ) {
buf_size = length + alloc_buf_chunk;
pb = new BYTE[buf_size];
}
else
pb = Fbin_val;

memset( pb, fill, buf_size);

PCrc32 new_crc;
new_crc.Add( pb, length );
if( crc32.Get() == new_crc.Get() ) {
if( pb != Fbin_val )
delete []pb;
}
else {
if( pb != Fbin_val )
delete []Fbin_val;
Fbin_val = pb;
Fbin_val_len = length;
crc32 = new_crc;
has_changed = true;
}
}
catch(EOutOfMemory &e)
{
ShowMessage( AnsiString(e.ClassName())+ e.Message );
OutOfMemoryError();
}
catch(Exception &e)
{
ShowMessage( AnsiString(e.ClassName())+ e.Message );
throw PrException( "Exception thrown from TVarBinArray::Create");
}
}


Quote:
Are the classes exported from the package properly?

I imagine so. They worked fine until I started hitting this problem. How can
I check this?


Quote:
Did you verify the
structure size in both projects by using sizeof()?

No, I haven't. Excellent idea. I'll do so.


Remy, as always - your help is much appreciated.
Regards,
Ettienne
Quote:

Gambit

Back to top
E_RGilbert
Guest





PostPosted: Tue Jun 06, 2006 7:13 pm    Post subject: Re: Weird Run-time error Reply with quote

"Remy Lebeau (TeamB)" <no.spam (AT) no (DOT) spam.com> wrote:

Quote:
That does not exclude what I said. If a global has the same name as a
local
variable, the code could still assign values to the global instead of the
local, and vice versa.

Point taken. I'll have a look.


Quote:
Then your stack is very likely corrupted. Your code is explicitally
initializing the variable to 0, so stack corruption is about the only way
the variable could get initialized to 1 instead of 0, meaning that your 0
value is getting written to the wrong address on the stack than it should
be.

The __fastcall calling convention passes most function parameters using
CPU
registers instead of the stack, so it is very unlikely that any of them
are
causing the problem. The stack is likely being corrupted long before your
function is ever being called, and you just happen to have noticed the
prolem at that point in the chain of function calls.

Ok. Thanks for the advice - at least it gives me something to work on.


Quote:
You are assigning the rec_len and no_recs members before actually
allocating
the buffer. You should be setting them afterwards instead. If something
goes wrong with the allocation, those members would contain incorrect
values. If you use those values later on to access the buffer, you could
be
accessing the wrong memory.

A better design cwould be to pass the new sizes to Create() and additional
parameters and let Create() manage the assignment of the members. That
way,
the members are only updated if the buffer itself is updated.

The Base class TVarBinArray does not have a concept of storing records - it
is merely a "storage manger" for a buffer that can dynamically grow/shrink.
As a result it simply allocates and manages memory in a single block. The
derived class TEfContent is a specialization of this class where the buffer
can either be "flat", or contain "records" (of a fixed length). Hence the
TEfContent class has members rec_len & no_recs whilst TVarBinArray only has
a 'size' member Fbin_val_len.


Quote:
You are not catching C runtime exceptions at all, only VCL exceptions.
The
'new' operator will not throw a VCL exception if it fails to allocate a
memory block.

Oh. So EOutOfMemory will not be thrown for a memory allocation failure by
'new'. I somehow assumed it would, but now that you mention it - it
obviously wouldn't. I'll make the required changes. In any case, my
intention was to only catch the exceptions I wanted to deal with, and to let
the Component user deal with the rest as he/she sees fit, rather than have a
global catch(...) block.

Thanks again,
Ettienne
Back to top
Remy Lebeau (TeamB)
Guest





PostPosted: Tue Jun 06, 2006 9:59 pm    Post subject: Re: Weird Run-time error Reply with quote

"E_RGilbert" <rgilbert at telkomsa dot net> wrote in message
news:44858d80 (AT) newsgroups (DOT) borland.com...

Quote:
The Base class TVarBinArray does not have a concept of
storing records - it is merely a "storage manger" for a buffer that
can dynamically grow/shrink. As a result it simply allocates and
manages memory in a single block. The derived class TEfContent
is a specialization of this class where the buffer can either be "flat",
or contain "records" (of a fixed length). Hence the TEfContent class
has members rec_len & no_recs whilst TVarBinArray only has a
'size' member Fbin_val_len.

You should still be setting those members after the allocation rather than
before, to ansure their accuracy.

Quote:
Oh. So EOutOfMemory will not be thrown for a memory allocation
failure by 'new'.

There are two memory maagers involved - the VCL memory manager and the C
memory manager. If you use 'new' to instantiate a VCL object, the VCL
memory manager is used. EOutOfMemory is thrown by that manager. If you use
'new' to allocate non-VCL objects and memory buffers (which you are in this
case), the VCL manager is not used, so EOutOfMemory cannot be thrown. The
C++ standard dictates that 'new' is supposed to throw a std::bad_alloc
exception instead.

Quote:
In any case, my intention was to only catch the exceptions I wanted
to deal with, and to let the Component user deal with the rest as
he/she sees fit, rather than have a global catch(...) block.

Since you are putting this code into a VCL component, you should catch any C
runtime exceptions in your own code so that you can then throw VCL
exceptions to the user, ie:

try
{
pb = new BYTE[size];
}
catch(...)
{
throw EOutOfMemory("");
}

Otherwise, use the VCL memory manager for allocations so that VCL exceptions
are always thrown. You can allocate a memory buffer with GetMem() or
AllocMem(), ie:

pb = AllocMem(size);
//...
FreeMem(pb);


Gambit
Back to top
E_RGilbert
Guest





PostPosted: Tue Jun 06, 2006 11:39 pm    Post subject: Re: Weird Run-time error Reply with quote

"Remy Lebeau (TeamB)" <no.spam (AT) no (DOT) spam.com> wrote:

Quote:
You should still be setting those members after the allocation rather than
before, to ansure their accuracy.

OK. Point taken.


Quote:
There are two memory maagers involved - the VCL memory manager and the C
memory manager...etc.

OK. I get the picture - it makes sense now. Thanks - this information is
really helpful.


Now to find that stack corruption....

Thanks again, Remy.
Regards,
Ettienne
Back to top
Display posts from previous:   
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> C++ Builder (VCL Components Development) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.