Jump to content
Dany Marmur

Help needed. Re-raising exception gives AV.

Recommended Posts

Came across this today in my client-server project.

An exception is thrown and catched for server-side logging.

Then, the exception should be re-raised in order to go all the way down to the network communication layer.

If sucessful the layer will foward the exception to the client.

 

So ... this gives an AV:

      on E: Exception do
      begin
        LogErrors(E, SessionID, UserName, UserID);
        raise;  // AV Here
      end;

Edit: It also leaks one UnicodeString.

 

But this works (!):

      on E: Exception do
      begin
        LogErrors(E, SessionID, UserName, UserID);
        raise Exception.Create(E.Message);
      end;

I am not very versed in redaing the registers.

Did i suceed to corrup the exception object?

The LogError only does some type-checking on the exception and accesses it's Message member (works fine).

Any pointers (debugging tips) would be appreciated.

 

TIA,

 

/Dany

 

Edited by Dany Marmur
Leaks are logical.

Share this post


Link to post
44 minutes ago, Dany Marmur said:

The LogError only does some type-checking on the exception and accesses it's Message member (works fine)

And everything it does is completely synchronous? No threads, task, TThread.Queue, TThread.Synchronize and whatnot?

Share this post


Link to post

The whole deep stack that produces the original Exception is ran in a dedicated thread managed by the network communication layer (RTC, multithreaded, events-based 3rd party library), i.e. everything happens as a reaction to a single request, in one thread (created and amnaged by RTC), also the raising and re-raising. The final handling of the exception is done by the network layer. When i wrote "works" above then the server and client handles the excptions correctly without any leaks.

Share this post


Link to post

I am not sure what is the problem for sure, but i can point two things here

 

1) About raising Exception, you are raising an exception while the original one is exception and it is E, the raised one is not the original one, on side note if you are handling the abort exception of RTC, then i suggest either, try to reraise an identical exception to E ( may be check the type/class and reraise it ), or steer away from that and make the LogErrors call on the upper level, like at final stage in the network communication layer, eg. the abort exception is rare in RTC but it is essential for its threading model so if E is the ERtcSystem or ERtcSocketError then your code is raising the default Exception.

Since you want the exception to reach the transport layer, i assume it is not yours, hence you should be careful and make sure the exception with its parameters been passed as they raised.

 

2) Memory leak ?!, i think you should investigate that and fix it as it should not happen, may be there is a bigger problem that didn't show all symptoms, find it and fix it and that might solve your problem, as the first code of yours should be the right way to do it.

Share this post


Link to post

@Kas Ob., "the raised one is not the original one". True, and that is when everything works (as i expect it to). So apparently if i create another object (as i code snippet number 2) the memory management works, the original exception is freed properly. About RTC Exception, no, the original exception is raised from my code. It is not an RTC exception.

 

But the AV is raised immediately (if i just re-raise) and not after my RTC event-response-code has finished. So it seems i have done something with the exception object before we reach the code above.

 

Thank you, and @Der schöne Günther, i'll start a focused session and see what i can discern.

 

Of course, i tried to remove the logging call... no!! Ooops! OK! Thanks *blushes*.

Apparently the IDE did compile from chache or some such when i did that the first time. ARGH!!

Happening in 10.4.1. Dangerous!! Fed up with this IDE. Have to build and restart constantly.

 

Thanks guys!

Edited by Dany Marmur
IDE... YES i blame the IDE!!
  • Like 1

Share this post


Link to post
2 hours ago, Dany Marmur said:

So ... this gives an AV:


      on E: Exception do
      begin
        LogErrors(E, SessionID, UserName, UserID);
        raise;  // AV Here
      end;

This is the correct way to do this. It works fine. The problem is elsewhere. Don't create and raise a new exception. 

Share this post


Link to post

@David Heffernan, yes! This is why i started the thread. I asked for debugging tricks in order to pin down why that does not work. However, as i wrote above, the 10.4.1 IDE had me fooled. So now i have the "path" that i am used to to dig deeper inot this. If i find anything of value to other people than myself, or need more help, i'll post again.

 

Thanks!

Edited by Dany Marmur
added my own failings

Share this post


Link to post

It's really hard to tell but from what you described I think you're facing a memory corruption, I'd say a stack corruption -based on the raise; fails, raise exception; works-

I recommend the following things: 

- First, what happens if you omit the LogErrors  call ? does the AV gone ?

- Second, use madExcept and activate instanly crash on buffer this will help spotting heap corruption by over boundary.

- Third, put BP on raise statement and fall into RaiseAgain an see what line/instruction is causing the AV.

  • Like 1

Share this post


Link to post

- First, what happens if you omit the LogErrors  call ? does the AV gone ?

 

Yes, amazingly. The E parameter is a const (no deal as it is a pointer) but it is only read, like "LogMsg := 'This'+ that + E.Message".

I should publish that logging function here, but i'll wait until i've had the time to dig deeper.

 

- Second, use madExcept and activate instanly crash on buffer this will help spotting heap corruption by over boundary.

 

Good! Thanks! That is pertinent. Good idea, will-co!

 

- Third, put BP on raise statement and fall into RaiseAgain an see what line/instruction is causing the AV.

 

The AV is raised immediately on the re-raise "raise;" line. BOOM. I have traced into/out-of/and with strategic BPs.

 

Thanks, @Mahdi Safsafi.

Share this post


Link to post

I just remembered my first question in this forum, it was if there is a cure for the side effect of the debugger caused leaks, then i had may be similar bug/leak, it was like going in circles, after hours and when i found the bug which was so obvious and i could spot it without the debugger, only the debugger was causing one leak per string evaluation, everywhere !, and i was running in circles.

 

The more you trying to find a leak while debugging the more debugger will cause string related leaks and distract you, specially in the not so obvious cases, so make sure to check for the leaks without the debugger and be careful not to mix what is real from what is fake.

Share this post


Link to post
4 hours ago, David Heffernan said:

Don't create and raise a new exception. 

Sure, if you want code higher up on the call stack to catch and handle the original exception as-is.  But there are use-cases where it makes sense to raise a new exception instead, in which case you should use the Exception.RaiseOuterException() method so that you can capture the original exception in the InnerException of the new exception so that it is not lost if the higher code needs it.

Share this post


Link to post

First my apologies for not being clear enough from the start. I postponed my deployment (another project) that got me in a rather stressed mode) a day and started digging.

 

So this works in 10.3 but not in 10.4.1. "Obviously" i would not have produced that code if Berlin worked the same way. Happening over this was just by chance. I'm 100% sure there's a QP already. Granted, when i updated from Tokyo to Sydney i ran "cursory" tests since the project is only deployed as "Beta" to a few select persons. Just to avoid a series of comments about my negligence; the area of the application was not profiled yet, it is in the to-do.

 

program ExceptionsReRaise;

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

function LogException(const E: Exception): string;
begin
  Result := E.Message;
  //Logging('Blah blah' + Result, 'Exceptions');  FYI
end;

procedure LogExceptionEx(const E: Exception; var lResult: string);
begin
  lResult := E.Message;
  //Logging('Blah blah blah' + lResult, 'Exceptions'); FYI
end;

var
  lMessage: string;
begin
  try
    try
      raise Exception.Create('This is a raised Exception');
    except
      on E: Exception do
      begin
//        LogException(E);  // AV in Sydney, OK i Berlin
        LogExceptionEx(E, lMessage);  // OK
        raise;
      end;
    end;
    { TODO -oUser -cConsole Main : Insert code here }
  except
    on E: Exception do
    begin
      Writeln(E.ClassName, ': ', E.Message);
      ReadLn;
    end;
  end;
end.

@Kas Ob., yes the leak was a debugger side-effect, my bad.

  • Like 2

Share this post


Link to post

Interestingly, the two following changes eliminates the AV on raise.

Either, change LogException to a procedure

procedure LogException(const E: Exception);
begin
  Writeln('Log + ' + E.Message);
  //Logging('Blah blah' + Result, 'Exceptions');  FYI
end;

or assign the result to a variable:

      on E: Exception do
      begin
        lMessage := LogException(E);  // AV in Sydney, OK i Berlin
//        LogExceptionEx(E, lMessage);  // OK
        raise;
      end;

It does look like a compiler problem.

Is there a QP report?

 

  • Like 2

Share this post


Link to post
22 minutes ago, Lars Fosdal said:

Is there a QP report?

I have not filed any. Would the above code snipped be enought for a QP? In that case i can file it.

I am more of a voter, the QC is a bit messy for me and frankly i fail to understand Embas answers... So much else to do and learn!!

Share this post


Link to post
program SydnetExceptionsReRaise;
{$APPTYPE CONSOLE}
uses
  System.SysUtils;

function LogExceptionFunc(const E: Exception): string;
begin
  Result := E.Message;
  Writeln('Func Log + ' + E.Message);
end;

procedure LogExceptionProc(const E: Exception);
begin
  Writeln('Proc Log + ' + E.Message);
end;

procedure Test1_OK;
begin
  try
    try
      raise Exception.Create('This is a raised Exception');
    except
      on E: Exception do
      begin
        LogExceptionProc(E);
        raise;
      end;
    end;
  except
    on E: Exception do
    begin
      Writeln('Test 1 ', E.ClassName, ': ', E.Message);
    end;
  end;
end;

procedure Test2_OK;
var
  lMessage: string;
begin
  try
    try
      raise Exception.Create('This is a raised Exception');
    except
      on E: Exception do
      begin
        lMessage := LogExceptionFunc(E);
        raise;
      end;
    end;
  except
    on E: Exception do
    begin
      Writeln('Test 2 ', E.ClassName, ': ', E.Message);
    end;
  end;
end;

procedure Test3_Fail;
begin
  try
    try
      raise Exception.Create('This is a raised Exception');
    except
      on E: Exception do
      begin
        LogExceptionFunc(E);
        raise;
      end;
    end;
  except
    on E: Exception do
    begin
      Writeln('Test 3 ', E.ClassName, ': ', E.Message);
    end;
  end;
end;

begin
  try
    try
      Test1_OK;
      Test2_OK;
      Test3_Fail;
    except
      on E: Exception do
      begin
        Writeln('Unexpected ',E.ClassName, ': ', E.Message);
      end;
    end;
  finally
    Write('Press Any Key');
    ReadLn;
  end;
end.

Here you go. No joke.

  • Thanks 1

Share this post


Link to post

@Lars Fosdal if you have all the logins and know what version of their QC is the correct one and you want to, then you are very welcome to ticket this. I'll vote for it 🙂. Silence and i will do it (procrastinated).

Edited by Dany Marmur
procrastination

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×