интересная проблема стека? - PullRequest
2 голосов
/ 21 июня 2010

У меня есть интересная проблема в моем приложении Delphi 2009.при запуске в отладчике я получаю AV между ключевым словом Begin подпрограммы и первым оператором.Я считаю, что это когда он устанавливает локальные переменные.Вот информация, показанная в отладчике:

uDeviceModule.pas.940: begin  // _GetMeasurementsForChannel
00AF24C8 55               push ebp
00AF24C9 8BEC             mov ebp,esp
00AF24CB 51               push ecx
00AF24CC B9E9A90100       mov ecx,$0001a9e9    // isn't this a lot for the stack?

// error happens in here
00AF24D1 6A00             push $00
00AF24D3 6A00             push $00
00AF24D5 49               dec ecx
00AF24D6 75F9             jnz $00af24d1

00AF24D8 874DFC           xchg [ebp-$04],ecx
00AF24DB 53               push ebx
00AF24DC 894DF4           mov [ebp-$0c],ecx
00AF24DF 8955FC           mov [ebp-$04],edx
00AF24E2 8945F8           mov [ebp-$08],eax
00AF24E5 33C0             xor eax,eax
00AF24E7 55               push ebp
00AF24E8 687D2FAF00       push $00af2f7d
00AF24ED 64FF30           push dword ptr fs:[eax]
00AF24F0 648920           mov fs:[eax],esp
uDeviceModule.pas.941: SelectChannel(eChannelNum);       // first statement

Это упрощенная версия этой вложенной подпрограммы (см. ниже).

procedure TDeviceModule.GetMeasurements(ExpInfo:TExpInfo;
  _DisplayList:TMeasDisplayListAncestor; eExposureStatus:TExposureStatus;
  bActiveErrorEnabled:boolean);

  procedure _GetMeasurementsForChannel(_DisplayList:TObjectList;
    eChannelNum:TDeviceChannelNum; eExposureStatus:TMyEnum;
    bActiveErrorEnabled:boolean);
  var
    // these are all objects (not records)
    selChannel:TDeviceChannel;
    det:TDeviceDetector;
    shoKVMeas:TStoMeasurement;
  begin  // ********************* error happens on this line
    SelectChannel(eChannelNum);

    _GetMeasurement(ExpInfo, _DisplayList, eChannelNum, eExposureStatus, ctdVal1);
    _GetMeasurement(ExpInfo, _DisplayList, eChannelNum, eExposureStatus, ctdVal2);
    _GetMeasurement(ExpInfo, _DisplayList, eChannelNum, eExposureStatus, ctdVal3);
  end;  // _GetMeasurementsForChannel

begin
  // blah blah blah

      _GetMeasurementsForChannel(_DisplayList,
                                 eChannelNum,
                                 eExposureStatus,
                                 bActiveErrorEnabled);

  // blah blah blah
end;

это однопоточное приложение.

Как бы вы посоветовали мне найти причину этой проблемы?мои первые мысли были:

1) увеличить максимальный размер стека - я сделал, но это ничего не изменило.сейчас это 160000 долларов (1441792), но до этого я думаю, что это было 150000 долларов.2) этот объект все еще действителен?кажется ... он правильно отвечает на метод ClassName, и FastMM не предупреждает меня о каких-либо проблемах.

Интересно, что в трассировке стека ничего не говорится о подпрограмме, в которой возникает проблема.

:7e42b35c USER32.MoveWindow + 0xbe
:7e4565b7 USER32.GetRawInputDeviceInfoW + 0x5f
:7e428eec ; C:\WINDOWS\system32\USER32.dll
:7c90e473 ntdll.KiUserCallbackDispatcher + 0x13
ActnMenus.CallWindowHook(???,0,$31104)
:7e42b372 USER32.MoveWindow + 0xd4
:7e4565b7 USER32.GetRawInputDeviceInfoW + 0x5f
:7e428eec ; C:\WINDOWS\system32\USER32.dll
:7c90e473 ntdll.KiUserCallbackDispatcher + 0x13
:007b882d aqDockingWndProcHook + $1D
:7e42b372 USER32.MoveWindow + 0xd4
:7e4565b7 USER32.GetRawInputDeviceInfoW + 0x5f
:7e428eec ; C:\WINDOWS\system32\USER32.dll
:7c90e473 ntdll.KiUserCallbackDispatcher + 0x13
:7e428dd9 USER32.DefWindowProcW + 0xb9
:7e428d77 USER32.DefWindowProcW + 0x57
:7e418734 USER32.GetDC + 0x6d
:7e418816 ; C:\WINDOWS\system32\USER32.dll
:7e42a013 USER32.IsWindowUnicode + 0xa1
:7e42a039 USER32.CallWindowProcW + 0x1b
Controls.TWinControl.DefaultHandler(???)
:0050fac8 TWinControl.DefaultHandler + $DC
:0050b4b9 TControl.WndProc + $2D5
:0050f9cc TWinControl.WndProc + $518
:0050f0e3 TWinControl.MainWndProc + $2F
:0048874e StdWndProc + $16
:7e418734 USER32.GetDC + 0x6d
:7e418816 ; C:\WINDOWS\system32\USER32.dll
:7e428ea0 ; C:\WINDOWS\system32\USER32.dll
:7e428eec ; C:\WINDOWS\system32\USER32.dll
:7c90e473 ntdll.KiUserCallbackDispatcher + 0x13
:7e428dd9 USER32.DefWindowProcW + 0xb9
:7e428d77 USER32.DefWindowProcW + 0x57
:7e418734 USER32.GetDC + 0x6d
:7e418816 ; C:\WINDOWS\system32\USER32.dll
:7e42a013 USER32.IsWindowUnicode + 0xa1
:7e42a039 USER32.CallWindowProcW + 0x1b
:0050fac8 TWinControl.DefaultHandler + $DC
:0050f9cc TWinControl.WndProc + $518
:0050f0e3 TWinControl.MainWndProc + $2F
:0048874e StdWndProc + $16
:7e418734 USER32.GetDC + 0x6d
:7e418816 ; C:\WINDOWS\system32\USER32.dll
:7e428ea0 ; C:\WINDOWS\system32\USER32.dll
:7e428eec ; C:\WINDOWS\system32\USER32.dll
:7c90e473 ntdll.KiUserCallbackDispatcher + 0x13
:7e428dd9 USER32.DefWindowProcW + 0xb9
:7e428d77 USER32.DefWindowProcW + 0x57
:7e418734 USER32.GetDC + 0x6d
:7e418816 ; C:\WINDOWS\system32\USER32.dll
:7e42a013 USER32.IsWindowUnicode + 0xa1
:7e42a039 USER32.CallWindowProcW + 0x1b
:0050fac8 TWinControl.DefaultHandler + $DC
:0050f9cc TWinControl.WndProc + $518
:0050f0e3 TWinControl.MainWndProc + $2F
:0048874e StdWndProc + $16
:7e418734 USER32.GetDC + 0x6d
:7e418816 ; C:\WINDOWS\system32\USER32.dll
:7e428ea0 ; C:\WINDOWS\system32\USER32.dll
:7e428eec ; C:\WINDOWS\system32\USER32.dll
:7c90e473 ntdll.KiUserCallbackDispatcher + 0x13
:7e428dd9 USER32.DefWindowProcW + 0xb9
:7e428d77 USER32.DefWindowProcW + 0x57
:7e418734 USER32.GetDC + 0x6d
:7e418816 ; C:\WINDOWS\system32\USER32.dll
:7e42a013 USER32.IsWindowUnicode + 0xa1
:7e42a039 USER32.CallWindowProcW + 0x1b
:0050fac8 TWinControl.DefaultHandler + $DC
:0050f9cc TWinControl.WndProc + $518
:0065279d TcxControl.WndProc + $121
:0070b38d TcxCustomGrid.WndProc + $5
:0048874e StdWndProc + $16
:7e418734 USER32.GetDC + 0x6d
:7e418816 ; C:\WINDOWS\system32\USER32.dll
:7e428ea0 ; C:\WINDOWS\system32\USER32.dll
:7e428eec ; C:\WINDOWS\system32\USER32.dll
:7c90e473 ntdll.KiUserCallbackDispatcher + 0x13
:7e428dd9 USER32.DefWindowProcW + 0xb9
:7e428d77 USER32.DefWindowProcW + 0x57
:7e418734 USER32.GetDC + 0x6d
:7e418816 ; C:\WINDOWS\system32\USER32.dll
:7e42a013 USER32.IsWindowUnicode + 0xa1
:7e42a039 USER32.CallWindowProcW + 0x1b
:0050fac8 TWinControl.DefaultHandler + $DC
:0050f9cc TWinControl.WndProc + $518
:0065279d TcxControl.WndProc + $121
:0075bbc4 TcxGridSite.WndProc + $20
:0048874e StdWndProc + $16
:7e418734 USER32.GetDC + 0x6d
:7e418816 ; C:\WINDOWS\system32\USER32.dll
:7e428ea0 ; C:\WINDOWS\system32\USER32.dll
:7e428eec ; C:\WINDOWS\system32\USER32.dll
:7c90e473 ntdll.KiUserCallbackDispatcher + 0x13
:0044c91e HandleException + $22A
:004539af InterceptAHandleExcept + $3F
:0048874e StdWndProc + $16
:7e418734 USER32.GetDC + 0x6d
:7e418816 ; C:\WINDOWS\system32\USER32.dll
:7e4189cd ; C:\WINDOWS\system32\USER32.dll
:7e418a10 USER32.DispatchMessageW + 0xf

это наводит меня на мысль, что проблема заключается в переполнении стека в некотором роде.при обработке сообщений.

предложения ???СПАСИБО!

Ответы [ 7 ]

4 голосов
/ 21 июня 2010

Я сильно подозреваю, что ссылка на TDeviceModule неверна.Вы не всегда будете видеть какие-либо побочные эффекты вызова метода для неверной ссылки на объект до тех пор, пока какой-либо путь в тело метода не появится, если метод не является виртуальным, и в этом случае вызов самого метода обычно (всегда?) Приводит к AV.

3 голосов
/ 21 июня 2010

См. Этот вопрос: Исключения на странице защиты в Delphi?

Обычно вы должны получить исключение переполнения стека, когда у вас заканчивается ваш стек. Но если ваша страница защиты была затронута кем-то другим, и исключение было съедено без расширения стека - тогда ваш код потерпит крах с AV, когда вы расширите свой стек.

Это именно то, что происходит в вашем коде: вы расширяете стек и получаете AV. Этот цикл ассемблера предназначен для касания стека и запуска расширения стека за счет защитной страницы. Поскольку страница guarg исчезла, но стек не был расширен - вы получили простой AV здесь.

Обратите внимание, что увеличение размера стека не поможет, так как стек вообще не увеличивается.

Вам нужно найти, кто играет с вашим стеком.

3 голосов
/ 21 июня 2010

Из вашего комментария («здесь происходит ошибка») ваша ошибка появляется в цикле, который устанавливает пространство стека, всего 212 КБ!Он не имеет абсолютно никакого отношения к параметрам, которые вы передаете процедуре, и не имеет ничего общего с жизнеспособностью объекта, который вы передаете в качестве параметра (там нет CALL, это просто JNZ, который возвращается к PUSH $ 00пока операция DEC ECX не пометит флаг ZERO, то есть $ 1a9e9 раз.Больше!Более того, выясните, почему ваша процедура занимает так много места, и выясните, находятся ли другие процедуры в той же ситуации (обратите внимание на большие записи, используемые в качестве локальных переменных).

2 голосов
/ 21 июня 2010

Я бы закомментировал каждую из 3 переменных, а затем откомментировал по одной за раз, чтобы посмотреть, не взорвется ли какая-то конкретная из них.Если это так, вы только что сократили свою проблему на 2/3.

0 голосов
/ 22 июня 2010

Извините за упрощение, но ...

_DisplayList: TMeasDisplayListAncestor И _DisplayList: TObjectList одновременно находятся в области действия.

Так же как два eExposureStatus разных типов и два bActiveErrorEnabled из Boolean.

когда вы вызываете _GetMeasurement (ExpInfo, _DisplayList, eChannelNum, eExposureStatus, ctdVal1) в локальной процедуре, какую переменную и тип она использует? TobjectList или TTMeasDisplayListAncestor?

Если только я не пьян больше, чем я думаю ...:)

0 голосов
/ 22 июня 2010

вот что я узнал:

Осуществляя объект, я обнаружил, что он был здоров.

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

procedure TDeviceModule.Validate;
const
  icTestSize=400000;
var
  i:integer;
begin
  // ask the object stuff to try to see if it's healthy

  SelectChannel(dcCh1);

  ClassName;

  for eChannelNum:=low(TDeviceChannelNum) to high(TDeviceChannelNum) do
    if HasChannel(eChannelNum) then
      m_aChannels[eChannelNum].Validate;

  // exercise the stack to see if loading on extra stuff is a problem...it is

  i:=0;
  while i<icTestSize do
    begin
      asm
        push 00
      end;
      inc(i);
    end;

  i:=0;
  while i<icTestSize do
    begin
      asm
        pop ecx
      end;
      inc(i);
    end;
end;

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

procedure TDeviceModule.GetMeasurements(blah blah blah);

  function _DoSomething1(blah blah blah):TBigRecord;
  begin
  end;

  function _DoSomething2(blah blah blah):TBigRecord;
  begin
  end;

  function _DoSomething3(blah blah blah):TBigRecord;
  begin
  end;

begin
  _DoSomething1(blah blah blah);
  _DoSomething2(blah blah blah);
  _DoSomething3(blah blah blah);
end;

каждый раз, когда я его использую (и даже если я не использую результат), я получаю пространство стека, выделенное для значения результата.

Решение, которое я использовал сейчас, состояло в том, чтобы изменить эти функции на процедуры, поскольку я все равно не использовал возвращаемое значение.

Я увеличил место в стеке, но недостаточно для предотвращения этой проблемы.

можно ли ожидать, что в таком случае будет сообщено о переполнении стека?

спасибо всем за вашу ценную помощь! эта проблема волновала меня ...

0 голосов
/ 21 июня 2010

Возможно, что 3 локальные переменные (переменные стека) станут больше, чем ожидалось.Я полагаю, это может произойти, если объекты объявлены в модуле, который содержится в другом BPL, и он не перестроен правильно (т. Е. Ваша программа считает, что он меньше, чем на самом деле).
Какой бы ни была причина, вы можете поэкспериментировать и выяснить,если это происходитРазместите «буферные» переменные между и после ваших 3 переменных.

ex: 
  var 
    selChannel:TDeviceChannel; 
    Buff1 : array[1..1024] of AnsiChar;
    det:TDeviceDetector; 
    Buff2 : array[1..1024] of AnsiChar;
    shoKVMeas:TStoMeasurement; 
    Buff3 : array[1..1024] of AnsiChar;

Это должно сделать две вещи для вас.1) это должно предотвратить A / V, предполагая, что 1024 достаточно.2) изучив массивы, вы сможете увидеть, появляется ли мусор.Это указывало бы на то, что они перезаписываются объявлением непосредственно выше.

...