2010-06-21 4 views
2

У меня есть интересная проблема в моем приложении 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 

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

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

+0

Существуют ли какие-либо конструкторы классов для любого из объектов? – code4life

+0

Что такое TDeviceChannel, TDeviceDetector, TStoMeasurement, TDeviceChannelNum, TMyEnum? А именно: что такое SizeOf для них? – Alex

+0

> Существуют ли какие-либо конструкторы классов для любого из объектов? объекты были построены в другом месте до вызова этой процедуры, и мы (не показаны) извлекаем эти объекты. @Alexander: размер всех из них равен 4 (TDeviceChannel, TDeviceDetector и TStoMeasurement - все объекты, поэтому их SizeOf будет SizeOf для указателя (4)). –

ответ

3

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

Поскольку вы имеете дело с процедурой, использующей 212Kb пространства стека, возможно, вам стоит попытаться увеличить пространство стека намного больше! Еще лучше, выясните, почему ваша процедура использует столько места и вычисляет, если другие процедуры находятся в одной и той же ситуации (обратите внимание на большие записи, используемые в качестве локальных переменных).

+0

хорошая точка; подробнее об этом в ответе, который я добавляю. –

4

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

+0

+1 Это ИМХО, скорее всего, тоже, не изучая подробно. –

+0

Я записал начальные значения указателя для тех объектов, когда он работал, и сравнил Self непосредственно перед тем, как он выполнил Begin, и это было то же значение. –

+0

Я также попытался «выполнить» стек перед выполнением вызова, где произошла ошибка: i: = 0; в то время как i <100000 do начало asm push 00 end; inc (i); конец; i: = 0; while i <100000 do начало asm pop ecx end; inc (i); конец; Я могу сделать это * справа * * перед тем, как сделать звонок, где ошибка возникает без проблем! я думаю, что я собираюсь узнать что-то важное ... спасибо всем за вашу помощь! –

2

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

3

Смотрите этот вопрос: Guard page exceptions in Delphi?

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

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

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

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

0

Одна из возможностей состоит в том, что 3 локальные переменные (переменные стека) растут больше, чем ожидалось. Я предполагаю, что это может произойти, если объекты объявлены в блоке, который содержится в другой BPL, и он не перестроен правильно (т. Е. Ваша программа думает, что она меньше, чем есть на самом деле).
Независимо от причины, вы можете экспериментировать и выяснять, происходит ли это. Поместите «буферные» переменные между 3 и после 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) путем изучения массивов, вы должны увидеть, появляется ли мусор. Это означало бы, что они были переписаны декларацией непосредственно выше.

0

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

тренируясь объект, я нашел, что это было здорово.

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

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

Я думаю, вы видите что-то еще. Во-первых: исчерпание пространства стека означает переполнение стека, а не AV. Во-вторых: используйте инструмент или код для изучения ESP/Текущий размер стека и вершину стека для вашего потока. Очень вероятно, что ваш стек далеко не достигает своего максимума. В-третьих: изменение функции на процедуру на самом деле ничего не решает, она может лишь скрыть что-то. Это потому, что функция ABC(): TSomeRecord является действительной процедурой ABC (var Result: TSomeRecord) на двоичном уровне. – Alex

+0

Да; я бы ожидал увидеть ошибку переполнения стека вместо AV. какой инструмент вы предлагаете для изучения этого? Я несколько обеспокоен тем, что это была проблема (даже если проблема была «решена») ... не может не спросить, не услышу ли я снова эту проблему. Я не использовал возвращаемое значение, поэтому я полностью удалил его. Благодарим вас за помощь! –

+0

Посмотрите на VMMap или аналогичные инструменты. – Alex

0

Жаль быть упрощенно, но ...

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

Так что два eExposureStatus разных типов и два bActiveErrorEnabled из булева.

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

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

+0

Я не вижу TMeasDisplayListAncestor и TObjectList, используемые для одного и того же объекта в любом месте. Я обожаю два eExposureStatus разных типов и перекрывающиеся имена переменных bActiveErrorEnabled. _DisplayList является потомком TTMeasDisplayListAncestor. –

Смежные вопросы