Visual studio stack overflow error

I am getting a StackOverFlowException from Visual Studio C# application when calling a C++ function in a separate DLL.  Please see attached image.
  • Remove From My Forums
  • Question

  • I am getting a StackOverFlowException from Visual Studio C# application when calling a C++ function in a separate DLL.  Please see attached image.

    The exception is caused presumably because the whole of the array dblExptData is being put on the stack.

    Note I am not a C# programmer, this is code belonging to my C# colleague; I am responsible for the C++ DLL he is calling.  I too get an exception from a C++ test harness calling this DLL, but I simply increase the stack size in the C++
    calling program, and it works fine.

    However I don’t know how to do this from C#, and nor does he.  Any pointers?

Answers

  • Arrays in C# are ref types.  Ref types allocate data in the heap.  The only thing stored on the stack is the reference address. It would be similar to if you did this in C++:

    double* arr = new double[1000];

    I took your code and ran it and it works fine.  The only change I needed to make was adding an exact spelling attribute so the function could be found.

    [DllImport(strMapTune1, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]

    The other thing that I did was ensure that my C# app was compiling as x86. By default it’ll run as AnyCPU and if you’re running on a x64 machine that would be x64.  This would cause the DLL to not load.

    Given that you mention the test harness for C++ blows up I believe the issue is with your C++ code and what it is doing and not the C# code. 

    • Marked as answer by

      Monday, February 9, 2015 2:43 AM

    • Edited by
      Viorel_MVP
      Friday, January 30, 2015 5:52 PM
    • Marked as answer by
      Kristin Xie
      Monday, February 9, 2015 2:43 AM

В мире программистов ошибка «stack overflow» очень известна благодаря тому, что этот вид ошибки довольно распространен. А сам термин «stack overflow» известен еще больше, чем ошибка, благодаря одноименному англоязычному ресурсу «StackOverflow». Это сообщество программистов международного масштаба, и еще куча всего интересного. Поэтому не нужно путать ошибку «stack overflow» и веб-ресурс с таким же названием. В нашей статье речь пойдет об ошибке.

Ошибка «stack overflow» связана с переполнением стека. Она появляется в том случае, когда в стеке должно сохраниться больше информации, чем он может уместить. Объем памяти стека задает программист при запуске программы. Если в процессе выполнения программы стек переполняется, тогда возникает ошибка «stack overflow» и программа аварийно завершает работу. Причин возникновения подобной ошибки может быть несколько.

Ошибка «stack overflow»

Нужно отметить, что ошибка «stack overflow» не связана с конкретным языком программирования, то есть она может возникнуть в программах на Java, C++, C, C# и других компилируемых языках.

Причин ее возникновения может быть несколько. К самым распространенным причинам относятся:

  • бесконечная рекурсия;

  • глубокая рекурсия;

  • проблемы с переменными в стеке.

Бесконечная рекурсия и ошибка «stack overflow» 

Бесконечная рекурсия редко возникает самостоятельно и по неизвестным причинам. Обычно программист:

  • забывает прописывать условие для выхода из рекурсии;

  • пишет неосознанную косвенную рекурсию.

Самая частая причина из категории «бесконечной рекурсии» программист забывает прописывать условия выхода или же прописывает, но условия выхода не срабатывают.

Вот как это выглядит на С:

int factorial (int number)

{

  if (number == 0)

    return 1;

  return number * factorial(number — 1);

}

В описанном примере прописаны условия выхода из рекурсии, однако они никогда не сработают, если «number» будет отрицательным. Поэтому через несколько миллионов вызовов стек будет переполнен и возникнет ошибка «stack overflow». В некоторых языках программирования предусмотрена «защита» от таких рекурсий. В них рекурсия из конца функции конвертируется в цикл, что не будет расходовать стековую память. Но подобная «оптимизация» вызовет другую, менее опасную проблему «зацикливание».

Неосознанная бесконечная рекурсия возникает в том случае, если программист по невнимательности распределяет один и тот же функционал программы между разными нагруженными функциями, а потом делает так, что они друг друга вызывают.

В коде это выглядит так:

 int Object::getNumber(int index, bool& isChangeable)

{

  isChangeable = true;

  return getNumber(index);

}

int Object::getNumber(int index)

{

  bool noValue;

  return getNumber(index, noValue);

}

Глубокая рекурсия и ошибка «stack overflow»

Глубокая рекурсия — это такая рекурсия, которая имеет свое окончание через определенное время, поэтому она не бесконечная. Однако памяти стека не хватит для завершения такой рекурсии, поэтому ошибка «stack overflow» обеспечена. Обычно такая ситуация заранее просчитывается программистом,поэтому ее можно решить. Например, можно:

  • отыскать другой программный алгоритм для решения поставленной задачи, чтобы избежать применения рекурсии;

  • «вынести» рекурсию за пределы аппаратного стека в динамический;

  • и другое.

Глубокая рекурсия выглядит так:

void eliminateList(struct Item* that)

{

    if (that == NULL)

        return;

    eliminateList(that->next);

    free(that);

}

Проблемы с переменными в стеке и ошибка «stack overflow»

Если взглянуть на популярность возникновения «stack overflow error», то причина с проблемными переменными в стеке стоит на первом месте. Кроется она в том, что программист изначально выделяет слишком много памяти локальной переменной.

Например:

int function() {

     double b[1000000]

}

В данном случае может возникнуть такая ситуация, что массиву потребуется объем памяти, который стек не способен будет обеспечить, а значит, возникнет ошибка «stack overflow». 

Заключение

Ошибка «stack overflow» возникает довольно часто. Каждый конкретный случай ее возникновения требует собственного решения. Одна причина объединяет возникновение такой ошибки — невнимательность программиста. Если «stack overflow error» возникла, значит, программист где-то что-то упустил или не доглядел.

Warning! Some information on this page is older than 6 years now. I keep it for reference, but it probably doesn’t reflect my current knowledge and beliefs.

Sun

07

Feb 2016

tl;dr: A very long C++ function with multiple local variables, even if they are not very big and they are placed in separate scopes, can reserve as much as hundreds of kilobytes of stack frame, causing «Stack Overflow» even without bugs like infinite recursion. So you better split your long functions into shorter ones.

Can refactoring (or the lack of thereof) cause application crashes? If we understand refactoring as changes in code layout without changing its logic, we might think that it’s just the matter of readability and unreadable code increases chances of introducing bugs. But here is a story in which refactoring actually fixed a bug.

Long time ago in a software project far far away, there was a bug submitted telling that the application crashes with «Stack Overflow» message. It was a Windows app, developed in C++ using Visual Studio. I thought: — I can handle that, it should be easy! Every beginner/intermediate programmer knows about the call stack and surely seen this error at least once when accidentally caused infinite recursion in his code. So my first idea was that infinite recursion happens because of some logical error in the code (that should be easy to fix) or some unfortunate, invalid input data (that should be validated for safety before usage).

As it turned out, this was not the case. After setting up all the test environment and catching the crash in Visual Studio debugger, I looked at Call Stack and noticed that it looks quite normal. Sure the call depth was significant (as for C++, I’m not talking about Java here ;) and there was even some recursion, but 20 or 30 functions is not that much. The stack ended with a call to non-recursive function that seemed correct, so it was not the recursion that caused stack overflow.

My second idea was that some of these functions allocate some big objects (like arrays) by value, as local variables on the stack and this causes the stack to grow too big. I reviewed code of the functions that I found on the stack and used «Immediate Window» panel to quickly check sizeof(xxx) of variables or their types when they used some class, but I didn’t find anything particularly big. Local variable sizes varied from few bytes to at most several hundred bytes and I couldn’t find any big arrays defined in these functions. I also fetched address of some local variable in a function near the bottom of the stack (which looks like 0x000000000009a370), address of a parameter from the function at the top of the stack and subtracted them to see how big the stack grown over all these calls. The result was around 50 KB — not that much.

My third idea was to check maximum size of the stack. It is 1 MB by default, but it can be changed in Visual Studio project settings, in Linker > System tab, as «Stack Reserve Size» parameter. I check my project and I found this parameter not changed from its default value.

OK, now this became more difficult than I thought. After many debugging sessions, where I looked at various pointers, addresses and numbers trying to spot some memory override, stack corruption, out-of-bounds indexing etc., I finally opened «Disassembly» and «Registers» panels. I’m not a fan of such low level stuff, so it took me some time and few Google queries to understand these RSP, RBP registers and make sense of some x86-64 opcodes. While debugging step-by-step in the assembly, I found something interesting. At the beginning of my function, there was a call to mysterious function __chkstk and the crash occurred inside it. That was a clue I could use to ask Google what this all means. I found this: Description of the stack checking for Windows NT-based applications and this: What is the purpose of the _chkstk() function? These articles say that as the stack grows, next 4 KB pages are reserved. Each next page is allocated by the system on first «touch». I could actually see in my debugger that functions which need less than 1 page (4096 B = 1000h) have an instruction at the beginning similar to this:

sub         rsp,0A9h

While my debugged function had this instead:

mov         eax,26B29h
call        __chkstk (018104AA00h)
sub         rsp,rax

The articles say that when reserving more than one page of stack memory, this function must be called to loop over addresses with 4 KB step and «touch» each page. This is really what it does:

--- f:ddvctoolscrtcrtw32startupamd64chkstk.asm ---
sub         rsp,10h
mov         qword ptr [rsp],r10
mov         qword ptr [rsp+8],r11
xor         r11,r11
lea         r10,[rsp+18h]
sub         r10,rax
cmovb       r10,r11
mov         r11,qword ptr gs:[10h]
cmp         r10,r11
jae         cs10+10h (018104AA40h)
and         r10w,0F000h
lea         r11,[r11-1000h]
mov         byte ptr [r11],0
cmp         r10,r11
jne         cs10 (018104AA30h)
mov         r10,qword ptr [rsp]
mov         r11,qword ptr [rsp+8]
add         rsp,10h
ret

Key sentence of the second linked article seems to be: «The parameter in rax is size of data you want to add.» In my case, eax is set to 26B29h = 158505. Wait, what?! This is more than 150 KB! Is it really how much of the stack the function needs?!

It was finally the right conclusion. The function was more than 3000-lines long, with lots of nested conditions and all kinds of stuff, but mostly an all-encompassing switch with dozens of different cases. I refactored it, extracting code from under each case to a separate function. This fixed the «Stack Overflow» crash.

Apparently if you have a long function and define a lot of local variables, even if they are not particularly big and they are placed inside separate scopes like if-s or switch case-s, the function may need as much as 150 KB of stack frame, at least in Debug configuration. This can cause crash with «Stack Overflow» message even without infinite recursion or bugs like that. So please keep this in mind as additional argument for refactoring your code as soon as you see the need for it.

Comments |
#visual studio #c++

Share

Comments

У меня проблема с переполнением стека в приложении C #, написанном с использованием Visual Studio 2010 и библиотеки XNA. Мое приложение продолжает увеличивать использование памяти, пока не выйдет из строя из-за переполнения стека.

Я хотел бы проверить стек, чтобы понять, где ошибка, но я не знаю, как это сделать с помощью VS 2010.

Может ли кто-нибудь направить меня в правильном направлении?

2 ответы

Подключите отладчик до появления проблемы (или запустите приложение под отладчиком)

Тогда самый простой способ обнаружить переполнение стека — это настроить отладчик так, чтобы он останавливался на этом исключении при его возникновении; что можно сделать из окна «Исключения» (с моими привязками клавиш это можно вызвать с помощью CTRL+ALT+E, но также находится в меню «Отладка»).

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

ответ дан 01 дек ’11, 08:12

ответ дан 01 дек ’11, 08:12

Не тот ответ, который вы ищете? Просмотрите другие вопросы с метками

c#
visual-studio-2010
xna
stack-overflow

or задайте свой вопрос.

Понравилась статья? Поделить с друзьями:
  • Vipnet ошибка открытия почтовой базы c program files infotecs vipnet client msarch
  • Vipnet ошибка инициализации транспортного каталога
  • Vipnet ошибка 238
  • Vipnet выдает ошибку при установке
  • Vipnet csp сертификат отсутствует как исправить