У меня есть довольно огромная рекурсивная функция (также я пишу на C), и хотя я не сомневаюсь, что сценарий, в котором происходит переполнение стека, крайне маловероятен, это все еще возможно. Интересно, можно ли определить, будет ли стек переполняться в течение нескольких итераций, поэтому вы можете сделать аварийную остановку без сбоя программы.Обнаружение переполнения стека во время выполнения
ответ
Heres простое решение который работает на победу-32. На самом деле похоже на то Wossname уже писал, но менее противный :)
unsigned int get_stack_address(void)
{
unsigned int r = 0;
__asm mov dword ptr [r], esp;
return r;
}
void rec(int x, const unsigned int begin_address)
{
// here just put 100 000 bytes of memory
if (begin_address - get_stack_address() > 100000)
{
//std::cout << "Recursion level " << x << " stack too high" << std::endl;
return;
}
rec(x + 1, begin_address);
}
int main(void)
{
int x = 0;
rec(x,get_stack_address());
}
'std :: cout'? Это не C-код. – kfx
@kfx предоставлен :) – mainactual
Ahh, inline asm для этого теплого, нечеткого, чувство безопасности работы: D Nice. – Wossname
В самом языке программирования C это невозможно. В общем, вы не можете легко знать, что у вас закончились стеки до истечения срока. Я рекомендую вам вместо этого установить настраиваемый жесткий предел на глубину рекурсии в вашей реализации, поэтому вы можете просто прервать, когда глубина будет превышена. Вы также можете переписать свой алгоритм для использования вспомогательной структуры данных вместо использования стека через рекурсию, что дает вам большую гибкость для обнаружения состояния вне памяти; malloc()
сообщает вам, когда он терпит неудачу.
Однако, вы можете получить что-то подобное с процедурой, как это на UNIX-подобных систем:
- Использование
setrlimit
установить мягкий предел стека ниже, чем жесткий предел стека - Создание обработчиков сигналов для обоих
SIGSEGV
иSIGBUS
, чтобы получить уведомление о переполнении стека. Некоторые операционные системы производятSIGSEGV
для них, другиеSIGBUS
. - Если вы получаете такой сигнал и определяете, что он исходит из переполнения стека, поднимите ограничение мягкого стека с помощью
setrlimit
и установите глобальную переменную, чтобы определить, что это произошло. Сделайте переменнуюvolatile
, чтобы оптимизатор не скрутил ваши равнины. - В вашем коде на каждом этапе рекурсии проверьте, установлена ли эта переменная. Если это так, отмените.
Это может не работать повсюду и требовать определенного кода конкретной платформы, чтобы узнать, что сигнал поступает из переполнения стека. Не все системы (особенно ранние системы 68000) могут продолжать нормальную обработку после получения SIGSEGV
или SIGBUS
.
Аналогичный подход использовался оболочкой Bourne для распределения памяти.
Да, аналогичные хаки возможны в Windows, хотя это намного проще, если вы используете язык, который поддерживает исключения, и может поймать исключение переполнения стека Windows. –
@MartinJames Решение не переносится в любом случае, поэтому в Windows просто используйте все, что Windows API решает проблему. – fuz
Вот наивный метод, но это немного неприглядное ...
При вводе функции в первый раз, когда вы могли бы хранить адреса одного из ваших переменных, объявленных в этой функции. Храните это значение вне вашей функции (например, в глобальной сети). В последующих вызовах сравнивается текущий адрес этой переменной с кешированной копией. Чем глубже вы рекурсивете, тем дальше будут эти два значения.
Это, скорее всего, вызовет предупреждения о компиляторе (хранит адреса временных переменных), но при этом дает вам достаточно точный способ узнать, сколько стека вы используете.
Не могу сказать, что я действительно рекомендую это, но это сработает.
#include <stdio.h>
char* start = NULL;
void recurse()
{
char marker = '@';
if(start == NULL)
start = ▮
printf("depth: %d\n", abs(&marker - start));
if(abs(&marker - start) < 1000)
recurse();
else
start = NULL;
}
int main()
{
recurse();
return 0;
}
Not thread-safe :( –
@MartinJames Может использовать локальную переменную потока или просто передать указатель в качестве дополнительного аргумента рабочей функции. Но я не вижу, как это лучше, чем просто подсчет глубины рекурсии. – fuz
@MartinJames, очень верно, но это не было обязательным условием :) – Wossname
Альтернативный способ узнать предел стека в начале программы, и каждый раз, когда в вашей рекурсивной функции, чтобы проверить, был ли подошел этот предел (в пределах какой-то запас прочности, скажем, 64 кб). Если это так, отмените; если нет, продолжайте.
Ограничение стека для систем POSIX можно узнать, используя системный вызов getrlimit
.
Пример кода, который потокобезопасно: (примечание: это код предполагает, что стек растет в обратном направлении, так как на x86
)
#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>
void *stack_limit;
#define SAFETY_MARGIN (64 * 1024) // 64 kb
void recurse(int level)
{
void *stack_top = &stack_top;
if (stack_top <= stack_limit) {
printf("stack limit reached at recursion level %d\n", level);
return;
}
recurse(level + 1);
}
int get_max_stack_size(void)
{
struct rlimit rl;
int ret = getrlimit(RLIMIT_STACK, &rl);
if (ret != 0) {
return 1024 * 1024 * 8; // 8 MB is the default on many platforms
}
printf("max stack size: %d\n", (int)rl.rlim_cur);
return rl.rlim_cur;
}
int main (int argc, char *argv[])
{
int x;
stack_limit = (char *)&x - get_max_stack_size() + SAFETY_MARGIN;
recurse(0);
return 0;
}
Выход:
max stack size: 8388608
stack limit reached at recursion level 174549
10 kB недостаточно, учитывая, сколько дерьма вы найдете в стеке перед 'main()'. Повторите попытку с более длинным списком аргументов, чтобы увидеть, что 10 кБ недостаточно. Мне интересно, однако, наверняка можно будет как-то найти начало стека. – fuz
10 kB - эвристика, которая работает для меня. Изменил его до 64 кб в ответ. Один из способов узнать адреса области памяти стека - прочитать файл '/ proc/
- 1. обнаруживает ошибку переполнения/переполнения во время выполнения?
- 2. Память стека во время выполнения
- 3. Ошибка переполнения стека во время обнаружения столкновения
- 4. ошибка переполнения стека во время цикла
- 5. Обнаружение команды во время выполнения
- 6. Обнаружение сбоя во время выполнения?
- 7. Обнаружение KITL во время выполнения
- 8. Обнаружение зомби во время выполнения
- 9. Изменить значения переполнения во время выполнения?
- 10. Почему стекирование переполнения/переполнения не запускается во время выполнения?
- 11. как переполнение стека проверяется во время выполнения
- 12. Тестирование раскрутки стека во время выполнения оператора
- 13. обнаружения Xamarin HttpClient стека во время выполнения
- 14. Изменение размера стека во время выполнения
- 15. Обнаружение Bluetooth-клавиатуры iOS во время выполнения
- 16. VBA: обнаружение наличия библиотеки во время выполнения
- 17. Java - обнаружение обмена памятью во время выполнения
- 18. Обнаружение других объектов во время выполнения TDD
- 19. Обнаружение нового класса Java во время выполнения
- 20. Ошибка переполнения стека (бесконечный цикл?) Во время компиляции GWT
- 21. Обнаружение переполнения числа в JavaScript во время преобразования строки base10
- 22. Захват/обнаружение всей ошибки во время выполнения в программе
- 23. Обнаружение целочисленного переполнения
- 24. Обработка переполнения стека во встроенных системах
- 25. Обнаружение столкновения во время CAKeyFrameAnimation
- 26. Проверка использования стека во время компиляции
- 27. Обнаружение переполнения
- 28. ошибка переполнения стека стека C++
- 29. Ошибки CardView во время выполнения
- 30. Есть ли способ определить доступное пространство стека во время выполнения?
Я не думаю, что это портативное решение, но если вы не заботитесь о переносимости, вы можете проверить значение указателя стека, используя встроенный ассемблер и выполните экстренный выход, если указатель стека меньше определенного значения. –
Вы также можете просто ограничить глубину рекурсии до максимальной глубины. –
Не совсем дубликат, но связанный с этим: http://stackoverflow.com/questions/199747/how-to-detect-possible-potential-stack-overflow-problems-in-acc-program?rq=1 –