views:

306

answers:

5

i wonder if i've found a compiler bug? i was removing some old code from my app and now i get stackoverflow at "begin" (see code & disassembly below).

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;

any ideas?

thank you! mp

+1  A: 

Something's creating a 320 KB buffer. Do you have any of those objects in that chain of calls have a statically allocated huge array inside them? Perhaps it's trying to fit one of the returned objects onto the stack.

Yuliy
+2  A: 

The following code appears to be the problem:

m_SelectedRTMMenuData.ChannelMeasSet.MeasKV.ClearMeasurementData;

It looks like ChannelMeasSet is a huge data structure that the compiler is trying to copy to your stack for some reason. I'm not sure why the compiler would attempt to do that without seeing the rest of your object declaration. Furthermore, the compiler appears to have allocated two separate temporary storage areas on the stack, one for each line where you call this (even though only one will be used at any given time).

You have at least two possible solutions:

  1. Make the stack bigger. There is probably a Delphi directive for this.
  2. Rework your data structures so the compiler isn't inclined to copy huge temporary objects around.
Greg Hewgill
+1  A: 

Yuliy is right: The 320KB buffer is likely an array[20] of whatever is returned by ChannelMeasSet - based on the size of the rep movsd loop.

It looks like you've got some code that is returning a very large data structure by value, rather than just a reference to it. Apart from the stack overflow issue, that's likely to be very inefficient.

Roddy
So the disassembly he's shown is essentially wrong then?
Lasse V. Karlsen
Not at all. I think the compiler is allocating and clearing a 320KB sized space of stack to hold temporary results from one of these intermediate procedure.
Roddy
Ok, then he must be at the very limit of his stack already, since this method isn't recursive.
Lasse V. Karlsen
@lassevk : Did your keyboard get stuck repeating your comment somehow?
Roddy
Unless.... ClearMeasurementData calls this method...
Lasse V. Karlsen
No, I misread the code, it's late at night. I deleted the comments :) I just thought everyone didn't read his assembly code and was hooked up on recursion for some reason.
Lasse V. Karlsen
If he's got this kind of problem with big structures passed by value, you just need to have function a call b call c call d, and you're probably out of stack.
Roddy
Yeah, if all those return/use the same type of data structure, that'd do it.
Lasse V. Karlsen
A: 

thank you for your suggestions! here is the call stack (from 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

am working on more of your comments here right now. come back in a few minutes.

admittedly there are some moderately large structs around. i doubt they're anywhere near that big but i'll get back to you shortly.

X-Ray
A: 

stupid site; it auto refreshed, wiping out my answer. ChannelMeasSet is a record that surprised me that it occupies 1.2 MB!

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

strangely, the app was just now having a bunch of old code being stripped out. i have never seen a stack overflow like this one.

i'll post this comment so it doesn't get destroyed before i can finish!

thank you for your help!

X-Ray
Ah. I think you need to look closely at what changes you have made. I *think* that the code now is calling ClearMeasurementData on the LOCAL TEMPORARY COPY, which probably isn't what you intended. Is ChannelMeaseSet a *property* of the m_SelectedRTMMenuData, or a field??
Roddy
X-Ray