4

We've created a Datasnap service (with Delphi XE), using Bob Swart's white paper as a guide. It works fine, and we've deployed it to our test server.

Now a problem occurs, when we have executed a large number of requests (through JMeter), some sort of memory corruption occurs. Some requests succeed, some fail with an access violation. In the end, it has become so corrupt, that every request to our OWN (not the DSAdmin) methods responds with an access violation.

However, I can't get my hands on a stacktrace to get more info, because the exception is already catched in the processing of the request.

If I test heavily with the VCL version of this application, it remains working correctly.

Has anyone any clue what this could be, or experienced the same problem, or can you help me get the stack trace from a caught exception (in someone else's code, which I can't edit)?

Thanks in advance.

Geerten
  • 1,027
  • 1
  • 9
  • 22
  • 1
    You can add an exception handler (like [MadExcept](http://madshi.net/madExceptDescription.htm) or our [Open Source unit](http://blog.synopse.info/post/2011/04/14/Enhanced-logging-in-SynCommons)) to retrieve the stacktrace during the run. Wihtout this stacktrace, it would be impossible to find the root cause. Adding logging would help a lot for debugging a service. – Arnaud Bouchez Dec 15 '11 at 16:42
  • 2
    YES YOU CAN log a stack track on a caught exception. Get JclDebug from the JEDI Class Libraries, and add it to your app. If you edit your question to "how do I get a stack trace from a handled/caught exception and dump it to a trace log" I will gladly post such as an answer. I have used that technique (trace log + JCL Debug) many times to debug service crashes. – Warren P Dec 15 '11 at 17:18
  • Sounds like you are experiencing race conditions. You thought about thread safety, right? – whosrdaddy Dec 15 '11 at 19:25
  • @ArnaudBouchez, yes I know this. However, as I mentioned I had a hard time doing this, because it is already caught. – Geerten Dec 16 '11 at 08:18
  • @WarrenP, so I did! Looking forward to your answer..This would help me a lot. – Geerten Dec 16 '11 at 08:19
  • @whosrdaddy, yes I thought about thread safety. But the code I'm executing right now is really small, and does not concern any memory allocation (only execution of an "Am I Up" method). – Geerten Dec 16 '11 at 08:20
  • related question: http://stackoverflow.com/questions/347365/how-can-i-find-out-which-procedure-threw-an-exception-in-delphi – Warren P Dec 16 '11 at 21:36

2 Answers2

7

To log both caught and uncaught exceptions using JEDI JCL, you should install the JEDI JCL.

Then try some code like this code taken from jcl\examples\windows\debug\framestrack\FramesTrackDemoMain.pas:

You should compile with full Debug information on in both the Compiler and Linker options in your delphi project options, for this to work.

Note that you don't have to call LogException, it's called automatically one you've added the exception notifier callback (JclAddExceptNotifier). don't forget to also call JclRemoveExceptNotifier, when the form or data module you are adding it from is destroyed, as shown here:

procedure TForm1.LogException(ExceptObj: TObject; ExceptAddr: Pointer; IsOS: Boolean);
var
  TmpS: string;
  ModInfo: TJclLocationInfo;
  I: Integer;
  ExceptionHandled: Boolean;
  HandlerLocation: Pointer;
  ExceptFrame: TJclExceptFrame;

begin
  TmpS := 'Exception ' + ExceptObj.ClassName;
  if ExceptObj is Exception then
    TmpS := TmpS + ': ' + Exception(ExceptObj).Message;
  if IsOS then
    TmpS := TmpS + ' (OS Exception)';
  mmLog.Lines.Add(TmpS);
  ModInfo := GetLocationInfo(ExceptAddr);
  mmLog.Lines.Add(Format(
    '  Exception occured at $%p (Module "%s", Procedure "%s", Unit "%s", Line %d)',
    [ModInfo.Address,
     ModInfo.UnitName,
     ModInfo.ProcedureName,
     ModInfo.SourceName,
     ModInfo.LineNumber]));
  if stExceptFrame in JclStackTrackingOptions then
  begin
    mmLog.Lines.Add('  Except frame-dump:');
    I := 0;
    ExceptionHandled := False;
    while (chkShowAllFrames.Checked or not ExceptionHandled) and
      (I < JclLastExceptFrameList.Count) do
    begin
      ExceptFrame := JclLastExceptFrameList.Items[I];
      ExceptionHandled := ExceptFrame.HandlerInfo(ExceptObj, HandlerLocation);
      if (ExceptFrame.FrameKind = efkFinally) or
          (ExceptFrame.FrameKind = efkUnknown) or
          not ExceptionHandled then
        HandlerLocation := ExceptFrame.CodeLocation;
      ModInfo := GetLocationInfo(HandlerLocation);
      TmpS := Format(
        '    Frame at $%p (type: %s',
        [ExceptFrame.ExcFrame,
         GetEnumName(TypeInfo(TExceptFrameKind), Ord(ExceptFrame.FrameKind))]);
      if ExceptionHandled then
        TmpS := TmpS + ', handles exception)'
      else
        TmpS := TmpS + ')';
      mmLog.Lines.Add(TmpS);
      if ExceptionHandled then
        mmLog.Lines.Add(Format(
          '      Handler at $%p',
          [HandlerLocation]))
      else
        mmLog.Lines.Add(Format(
          '      Code at $%p',
          [HandlerLocation]));
      mmLog.Lines.Add(Format(
        '      Module "%s", Procedure "%s", Unit "%s", Line %d',
        [ModInfo.UnitName,
         ModInfo.ProcedureName,
         ModInfo.SourceName,
         ModInfo.LineNumber]));
      Inc(I);
    end;
  end;
  mmLog.Lines.Add('');
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
  JclAddExceptNotifier(Form1.LogException);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  JclRemoveExceptNotifier(Form1.LogException);
end;

This is the usual initialization code:

initialization

  JclStackTrackingOptions := JclStackTrackingOptions + [stExceptFrame];
  JclStartExceptionTracking;

Here's the JCL FramesTrackExample.dproj demo running:

enter image description here

For your purposes, change the code that adds a line to TMemo.Lines, to write to a log file on disk instead. If you already have a logging system, that's great, and if not, then consider Log4D.

Warren P
  • 65,725
  • 40
  • 181
  • 316
  • Thanks for your answer. I hope I can look into it tomorrow, it's a bit busy these days.. – Geerten Dec 20 '11 at 15:25
  • Thanks, I got it to work! However just a little comment: ExceptFrame.ExcFrame should ExceptFrame.FrameLocation..(found this in the latest download of the link you provided) – Geerten Dec 21 '11 at 15:17
  • I guess the JCL Debug has been tweaked a bit since I last looked at it. – Warren P Dec 21 '11 at 23:03
-1

Every new web service call is a new thread. Some resources can be allocated by previous thread when the next service call is coming and the new thread tries to access them. Or some resources may be released by one thread when another thread tries to use them. You should use TCriticalSection to make sure that all resources are available only for one thread. Also make sure that the TCriticalSection is a global variable and accessible by all instances.

Illya Pavlov
  • 253
  • 2
  • 9
  • Or just don't use global resources. As mentioned in my comment on the question itself, it's not a problem of non-thread-safe code. – Geerten Dec 20 '11 at 15:25
  • Not very likely that any of this extremely generic advice applies to the person who asked this question. You're trying to be helpful, but I have downvoted because you have no specific detailed instructions that apply in the least to his particular case. (Making something a global, or not? What?) – Warren P Dec 20 '11 at 16:52