2010-06-21 97 views
2

我在我的delphi 2009应用程序中有一个有趣的问题。当在调试器中运行时,我在子例程的Begin关键字和第一个语句之间得到一个AV。我相信那是在设置局部变量的时候。这里是调试器中显示的信息:有趣的堆栈问题?

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 2010-06-21 03:15:14

+0

什么是TDeviceChannel,TDeviceDetector,TStoMeasurement,TDeviceChannelNum,TMyEnum?即:他们是什么SizeOf? – Alex 2010-06-21 07:35:34

+0

>是否有任何类的构造函数的任何对象? 这个对象是在调用这个例程之前在别处构造的,我们(未示出)检索这些对象。 @Alexander:所有这些的大小是4(TDeviceChannel,TDeviceDetector和TStoMeasurement都是对象,因此它们的SizeOf将是指针(4)的SizeOf)。 – 2010-06-21 17:26:19

回答

3

从您的评论(“错误发生在这里”),您的错误弹出设置堆栈空间的循环,所有212 Kb的!它与传递给过程的参数完全无关,与作为参数传递的对象的可行性无关(在那里没有CALL,它只是一个JNZ,它循环到PUSH $ 00直到DEC ECX操作标记ZERO标志,即$ 1a9e9次)。

由于您正在处理使用212Kb堆栈空间的过程,因此您应该尝试增加更多堆栈空间!更好的是,弄清楚为什么你的程序使用了这么多的空间,并且弄清楚其他程序是否处于相同的情况下(注意用作局部变量的大型记录)。

+0

好点;更多关于这个在我添加的答案。 – 2010-06-21 22:18:38

4

我强烈怀疑所涉及的TDeviceModule引用无效。除非方法是虚拟的,在这种情况下方法本身的调用通常会(总是)产生一个AV,否则你不会总是看到任何对坏对象引用调用方法的不良影响。

+0

+1这是恕我直言,最有可能的一个,也没有详细检查。 – 2010-06-21 06:50:22

+0

我记录了这些对象的初始指针值,当它工作并比较自己刚才执行的开始并且它是相同的值。 – 2010-06-21 18:38:51

+0

我也尝试“行使”堆栈之前,发生错误发生的电话: 我:= 0; 而我<100000做 开始 asm 推00 结束;公司(i)公司; 结束; i:= 0; while I <100000 do begin asm pop ecx end;公司(i)公司; 结束; 我可以这样做*正确* *之前*进行调用的错误发生没有问题!我想我即将学习一些重要的东西... 谢谢大家的帮助! – 2010-06-21 18:41:14

2

我会将3个变量中的每一个注释掉,然后每次不注释一个,看看它们中是否有特定的一个正在炸毁。 如果是这样,你刚刚减少了2/3的问题。

3

看到这个问题:Guard page exceptions in Delphi?

通常情况下,你应该得到堆栈溢出异常,当你走你的堆栈。但是如果你的守卫页面被其他人触动,并且异常在没有扩展堆栈的情况下被默默食用 - 那么当你扩展你的堆栈时,你的代码将会随着AV崩溃。

这完全是在你的代码中发生的事情:你展开堆栈,你得到了AV。此汇编程序周期旨在通过触发堆栈触发堆栈扩展。由于guarg页面消失了,但堆栈没有扩展 - 你在这里获得了简单的AV。

请注意,增加堆栈大小不会有帮助,因为堆栈根本不会增长。

你需要找到谁玩你的堆栈。

0

一种可能性是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)通过检查数组,你应该能够看到是否出现垃圾。这表明它们正被上面直接声明覆盖。

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 KB。不仅如此,而且它已被使用了很多次。

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 2010-06-22 11:32:13

+0

是的;我会希望看到堆栈溢出错误,而不是AV。你建议用什么工具来研究这些?我有些担心这是一个问题(即使问题已经“解决”了)......不禁要问,我是否会再次听到这个问题。 我没有使用返回值,所以我完全删除它。 谢谢你的帮助! – 2010-06-22 14:30:43

+0

看看VMMap或类似的工具。 – Alex 2010-06-22 15:21:43

0

对不起,是简单,但...

_DisplayList:TMeasDisplayListAncestor和_DisplayList:TObjectList同时都在范围之内。

所以是两个不同类型的eExposureStatus和两个布尔型的bActiveErrorEnabled。

当您在本地过程中调用_GetMeasurement(ExpInfo,_DisplayList,eChannelNum,eExposureStatus,ctdVal1)时使用哪个变量和类型? TobjectList或TTMeasDisplayListAncestor?

除非我只是比我想象的更醉...... :)

+0

我没有看到TMeasDisplayListAncestor和TObjectList用于任何地方的同一个对象。 我对两个不同类型的eExposureStatus和重叠的变量名称bActiveErrorEnabled没关系。 _DisplayList是TTMeasDisplayListAncestor的后代。 – 2010-06-22 14:23:10