【问题标题】:Interesting stack overflow! compiler bug?有趣的堆栈溢出!编译器错误?
【发布时间】:2008-12-22 21:40:47
【问题描述】:

我想知道我是否发现了编译器错误?我从我的应用程序中删除了一些旧代码,现在我在“开始”处得到了 stackoverflow(参见下面的代码和反汇编)。

procedure TfraNewRTMDisplay.ShowMeasurement;
var
  iDummy, iDummy2, iDummy3:integer;
begin    // STACK OVERFLOW BEFORE MY CODE STARTS
  iDummy:=0;
  iDummy2:=0;

  case iDummy2 of
    1:
      case iDummy of
        1:m_SelectedRTMMenuData.ChannelMeasSet.MeasKV.ClearMeasurementData;
        2:m_SelectedRTMMenuData.ChannelMeasSet.MeasKV.ClearMeasurementData;
      end;
  end;  
end;

NewRTMDisplay.pas.1601: begin
00983BF8 55               push ebp
00983BF9 8BEC             mov ebp,esp
00983BFB B9D4E40400       mov ecx,$0004e4d4
00983C00 6A00             push $00             // stack overflow loop
00983C02 6A00             push $00             // stack overflow loop 
00983C04 49               dec ecx              // stack overflow loop  
00983C05 75F9             jnz $00983c00        // stack overflow loop
00983C07 56               push esi
00983C08 57               push edi
00983C09 8945FC           mov [ebp-$04],eax
00983C0C 8D856005FFFF     lea eax,[ebp-$0000faa0]
00983C12 8B153C789A00     mov edx,[$009a783c]
00983C18 E88B35A8FF       call @InitializeRecord
00983C1D 8D85D00AFEFF     lea eax,[ebp-$0001f530]
00983C23 8B153C789A00     mov edx,[$009a783c]
00983C29 E87A35A8FF       call @InitializeRecord
00983C2E 33C0             xor eax,eax
00983C30 55               push ebp
00983C31 68F73C9800       push $00983cf7
00983C36 64FF30           push dword ptr fs:[eax]
00983C39 648920           mov fs:[eax],esp
NewRTMDisplay.pas.1602: iDummy:=0;
00983C3C 33C0             xor eax,eax
00983C3E 8945F8           mov [ebp-$08],eax
NewRTMDisplay.pas.1603: iDummy2:=0;
00983C41 33C0             xor eax,eax
00983C43 8945F4           mov [ebp-$0c],eax
NewRTMDisplay.pas.1605: case iDummy2 of
00983C46 8B45F4           mov eax,[ebp-$0c]
00983C49 48               dec eax
00983C4A 7571             jnz $00983cbd
NewRTMDisplay.pas.1607: case iDummy of
00983C4C 8B45F8           mov eax,[ebp-$08]
00983C4F 48               dec eax
00983C50 7405             jz $00983c57
00983C52 48               dec eax
00983C53 7436             jz $00983c8b
00983C55 EB66             jmp $00983cbd
NewRTMDisplay.pas.1608: 1:m_SelectedRTMMenuData.ChannelMeasSet.MeasKV.ClearMeasurementData;
00983C57 8D951872EBFF     lea edx,[ebp-$00148de8]
00983C5D 8B45FC           mov eax,[ebp-$04]
00983C60 8B80F0020000     mov eax,[eax+$000002f0]
00983C66 E895DBE9FF       call TRTMMenuData.ChannelMeasSet
00983C6B 8DB52072EBFF     lea esi,[ebp-$00148de0]
00983C71 8DBD6005FFFF     lea edi,[ebp-$0000faa0]
00983C77 B9A43E0000       mov ecx,$00003ea4
00983C7C F3A5             rep movsd 
00983C7E 8D856005FFFF     lea eax,[ebp-$0000faa0]
00983C84 E81B3F0200       call TDeviceMeas.ClearMeasurementData
00983C89 EB32             jmp $00983cbd
NewRTMDisplay.pas.1609: 2:m_SelectedRTMMenuData.ChannelMeasSet.MeasKV.ClearMeasurementData;
00983C8B 8D9560D9D8FF     lea edx,[ebp-$002726a0]
00983C91 8B45FC           mov eax,[ebp-$04]
00983C94 8B80F0020000     mov eax,[eax+$000002f0]
00983C9A E861DBE9FF       call TRTMMenuData.ChannelMeasSet
00983C9F 8DB568D9D8FF     lea esi,[ebp-$00272698]
00983CA5 8DBDD00AFEFF     lea edi,[ebp-$0001f530]
00983CAB B9A43E0000       mov ecx,$00003ea4
00983CB0 F3A5             rep movsd 
00983CB2 8D85D00AFEFF     lea eax,[ebp-$0001f530]
00983CB8 E8E73E0200       call TDeviceMeas.ClearMeasurementData
NewRTMDisplay.pas.1612: end;

有什么想法吗?

谢谢! mp

【问题讨论】:

  • 你能告诉我们函数 ClearMeasurementData 和 ChannelMeasSet 是如何定义的 - 以及它们操作和返回的对象的类型。
  • TDeviceMeasSet=类私有公共 MeasKV:TDeviceMeas; // 还有大约 18 个结束; TDeviceMeas=记录私有公共过程ClearMeasurementData;结尾; TDeviceMeas=记录私有公共端;

标签: delphi delphi-2009


【解决方案1】:

某些东西正在创建一个 320 KB 的缓冲区。调用链中的这些对象中是否有一个静态分配的巨大数组?也许它正在尝试将返回的对象之一放入堆栈。

【讨论】:

    【解决方案2】:

    以下代码似乎是问题所在:

    m_SelectedRTMMenuData.ChannelMeasSet.MeasKV.ClearMeasurementData;
    

    看起来 ChannelMeasSet 是一个巨大的数据结构,编译器出于某种原因试图将其复制到您的堆栈中。我不确定为什么编译器会在没有看到您的对象声明的其余部分的情况下尝试这样做。此外,编译器似乎在堆栈上分配了两个单独的临时存储区域,一个用于您调用它的每一行(即使在任何给定时间只会使用一个)。

    您至少有两种可能的解决方案:

    1. 使堆栈更大。这可能有一个 Delphi 指令。
    2. 重做您的数据结构,使编译器不倾向于复制巨大的临时对象。

    【讨论】:

      【解决方案3】:

      Yuliy 是对的:320KB 缓冲区可能是 ChannelMeasSet 返回的任何内容的数组 [20] - 基于 rep movsd 循环的大小。

      看起来您有一些代码按值返回一个非常大的数据结构,而不仅仅是对它的引用。除了堆栈溢出问题之外,这可能是非常低效的。

      【讨论】:

      • 一点也不。我认为编译器正在分配和清除 320KB 大小的堆栈空间来保存这些中间过程之一的临时结果。
      • 好的,那么他一定已经到了他的堆栈的极限,因为这个方法不是递归的。
      • @lassevk :您的键盘是否卡在以某种方式重复您的评论?
      • 不,我看错了代码,已经很晚了。我删除了 cmets :) 我只是以为每个人都没有阅读他的汇编代码,并且由于某种原因而迷上了递归。
      • 如果他遇到这种传值大结构的问题,你只需要函数a call b call c call d,你可能出栈了。
      【解决方案4】:

      感谢您的建议!这是调用堆栈(来自 delphi 2009)。

      NewRTMDisplay.TfraNewRTMDisplay.ShowMeasurement
      NewRTMDisplay.TfraNewRTMDisplay.DetectorSelectionChange($46552D0,drmOneAtATime)
      NewRTMDisplay.TfraNewRTMDisplay.pumOnDetectorSelectionChange($46129E0)
      Menus.TMenuItem.Click
      Menus.TMenu.DispatchCommand(???)
      Menus.TPopupList.WndProc((273, 386, 0, 0, 386, 0, 0, 0, 0, 0))
      Menus.TPopupList.MainWndProc(???)
      Classes.StdWndProc(15403740,273,386,0)
      :7e418734 USER32.GetDC + 0x6d
      :7e418816 ; C:\WINDOWS\system32\USER32.dll
      :7e4189cd ; C:\WINDOWS\system32\USER32.dll
      :7e418a10 USER32.DispatchMessageW + 0xf
      Forms.TApplication.ProcessMessage(???)
      :0051e31c TApplication.ProcessMessage + $F8
      

      我现在正在这里研究更多的 cmets。几分钟后回来。

      诚然,周围有一些中等大小的结构。我怀疑它们有那么大,但我会尽快回复你。

      【讨论】:

        【解决方案5】:

        愚蠢的网站;它自动刷新,抹去了我的答案。 ChannelMeasSet 是一个让我惊讶的记录,它占用了 1.2 MB!

        m_SelectedRTMMenuData    a small object
        ChannelMeasSet           a record
        MeasKV                   a record with method ClearMeasurementData( );
        

        奇怪的是,该应用程序刚刚删除了一堆旧代码。我从未见过像这样的堆栈溢出。

        我会发布此评论,以免在我完成之前它被破坏!

        感谢您的帮助!

        【讨论】:

        • 啊。我认为您需要仔细查看您所做的更改。我认为代码现在正在本地临时副本上调用 ClearMeasurementData,这可能不是您想要的。 ChannelMeaseSet 是 m_SelectedRTMMenuData 的 属性 还是字段??
        • ChannelMeasSet 是 m_SelectedRTMMenuData 的函数。它返回这些大记录之一。目前我已经确定 ChannelMeasSet 有一种难闻的气味——在一些公众​​成员中是一个很大的记录。因此,我将其转换为对象而不是记录。谢谢您,并会告知结果。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-19
        • 2014-01-26
        • 2019-02-16
        • 2011-09-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多