Как получить трассировку стека из обработанного / перехваченного исключения и записать ее в журнал трассировки

Мы создали сервис Datasnap (с Delphi XE), используя в качестве руководства официальный документ Боба Сварта. Он работает нормально, и мы развернули его на нашем тестовом сервере.

Теперь возникает проблема, когда мы выполнили большое количество запросов (через JMeter), происходит какое-то повреждение памяти. Некоторые запросы выполняются успешно, некоторые не выполняются из-за нарушения прав доступа. В конце концов, он стал настолько коррумпированным, что каждый запрос к нашим СОБСТВЕННЫМ (не DSAdmin) методам отвечает нарушением доступа.

Однако я не могу достать трассировку стека, чтобы получить дополнительную информацию, потому что исключение уже перехвачено при обработке запроса.

Если я проведу интенсивное тестирование с версией этого приложения для VCL, оно все равно будет работать правильно.

Кто-нибудь знает, что это может быть, или столкнулся с той же проблемой, или вы можете помочь мне получить трассировку стека из перехваченного исключения (в чужом коде, который я не могу редактировать)?

Заранее спасибо.


person Geerten    schedule 15.12.2011    source источник
comment
Вы можете добавить обработчик исключений (например, MadExcept или наш Модуль с открытым исходным кодом) для получения трассировки стека во время выполнения. Без этой трассировки стека было бы невозможно найти основную причину. Добавление ведения журнала очень поможет при отладке службы.   -  person Arnaud Bouchez    schedule 15.12.2011
comment
ДА, ВЫ МОЖЕТЕ записать трек стека при обнаружении исключения. Получите JclDebug из библиотек классов JEDI и добавьте его в свое приложение. Если вы измените свой вопрос на то, как мне получить трассировку стека из обработанного / перехваченного исключения и сбросить его в журнал трассировки, я с радостью отправлю такой ответ. Я много раз использовал эту технику (журнал трассировки + отладка JCL) для отладки сбоев службы.   -  person Warren P    schedule 15.12.2011
comment
Похоже, вы участвуете в гонке. Вы ведь думали о безопасности потоков?   -  person whosrdaddy    schedule 15.12.2011
comment
@ArnaudBouchez, да, я знаю это. Однако, как я уже упоминал, мне было нелегко это сделать, потому что это уже поймано.   -  person Geerten    schedule 16.12.2011
comment
@WarrenP, так я и сделал! С нетерпением жду вашего ответа .. Это мне очень поможет.   -  person Geerten    schedule 16.12.2011
comment
@whosrdaddy, да, я думал о безопасности потоков. Но код, который я выполняю прямо сейчас, действительно мал и не касается выделения памяти (только выполнение метода Am I Up).   -  person Geerten    schedule 16.12.2011
comment
связанный вопрос: stackoverflow.com/questions/347365/   -  person Warren P    schedule 17.12.2011


Ответы (2)


Чтобы регистрировать как перехваченные, так и неперехваченные исключения с помощью JEDI JCL, вы должны установить JEDI JCL.

Затем попробуйте какой-нибудь код, подобный этому коду, взятому из jcl\examples\windows\debug\framestrack\FramesTrackDemoMain.pas:

Вы должны компилировать с полной информацией об отладке как в параметрах компилятора, так и в параметрах компоновщика в параметрах проекта delphi, чтобы это сработало.

Обратите внимание, что вам не нужно вызывать LogException, оно вызывается автоматически после того, как вы добавили обратный вызов уведомителя об исключении (JclAddExceptNotifier). не забудьте также вызвать JclRemoveExceptNotifier, когда форма или модуль данных, из которого вы его добавляете, уничтожается, как показано здесь:

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;

Это обычный код инициализации:

initialization

  JclStackTrackingOptions := JclStackTrackingOptions + [stExceptFrame];
  JclStartExceptionTracking;

Вот запущенная демонстрация JCL FramesTrackExample.dproj:

введите описание изображения здесь

Для ваших целей измените код, который добавляет строку в TMemo.Lines, чтобы вместо этого записывать в файл журнала на диске. Если у вас уже есть система ведения журналов, это прекрасно, а если нет, рассмотрите вариант Log4D.

person Warren P    schedule 16.12.2011
comment
Спасибо за Ваш ответ. Надеюсь, я смогу разобраться в этом завтра, в наши дни здесь немного занято ... - person Geerten; 20.12.2011
comment
Спасибо, все заработало! Однако небольшой комментарий: ExceptFrame.ExcFrame должен ExceptFrame.FrameLocation .. (нашел это в последней загрузке предоставленной вами ссылки) - person Geerten; 21.12.2011
comment
Я предполагаю, что JCL Debug был немного изменен с тех пор, как я последний раз смотрел на него. - person Warren P; 22.12.2011

Каждый новый вызов веб-службы - это новый поток. Некоторые ресурсы могут быть выделены предыдущим потоком, когда поступает следующий вызов службы и новый поток пытается получить к ним доступ. Или некоторые ресурсы могут быть освобождены одним потоком, когда другой поток пытается их использовать. Вы должны использовать TCriticalSection, чтобы убедиться, что все ресурсы доступны только для одного потока. Также убедитесь, что TCriticalSection является глобальной переменной и доступна для всех экземпляров.

person Illya Pavlov    schedule 19.12.2011
comment
Или просто не пользуйтесь глобальными ресурсами. Как упоминалось в моем комментарии к самому вопросу, это не проблема небезопасного кода. - person Geerten; 20.12.2011
comment
Маловероятно, что какой-либо из этих чрезвычайно общих советов применим к человеку, задавшему этот вопрос. Вы пытаетесь быть полезным, но я проголосовал против, потому что у вас нет конкретных подробных инструкций, которые хотя бы применимы к его конкретному случаю. (Делать что-то глобальное или нет? Что?) - person Warren P; 20.12.2011