реклама на сайте
подробности

 
 
 
Reply to this topicStart new topic
> GCC и переполнение стека в многопоточных приложениях
Puzan
сообщение Sep 5 2007, 14:53
Сообщение #1


Участник
*

Группа: Новичок
Сообщений: 30
Регистрация: 16-12-05
Пользователь №: 12 295



В работе использую GCC. Искал метод определения размера стека для потока в многопоточном приложении. Собственно ничего толкового не нашел, по этому пришлось придумывать самому. Вот решил поделиться.

Первоначально хотел написать анализатор стека, но не стал этого делать, потому что некогда (может потом сделаю). По этому пошел другим путем.
В GCC есть опция -finstrument-functions, по которой компилятор вставляет в начало каждой функции вызов void __cyg_profile_func_enter( void*, void* ), а в конец вызов void __cyg_profile_func_exit( void*, void* ). Определяем эти функции и контролируем в них регистр sp. Вот примерно так:

Код
void __attribute__((no_instrument_function)) __cyg_profile_func_enter (void *this_fn, void *call_site) {
    register UINT32 sp asm("r13");
    register UINT32 cpsr asm("r2");
    asm(    "mrs        r2,cpsr" :: );
    if( (cpsr & 0x1F) == 0x1F )        // находимся в SYSMODE?
        if( cur_thd->sp_min > sp ) {
            cur_thd->sp_min = sp;
            cur_thd->sp_change = TRUE;
        }
}


где cur_thd - указатель на текущий поток,
cur_thd->sp_min - минимальное значение SP (глубина стека, стек растет вниз),
cur_thd->sp_change - флаг изменения cur_thd->sp_min.

Поясню: я использую свою ОС, по этому определил в структуре потока необходимые мне поля. Если не хочется лезть в исходники ОС, можно определить отдельно для каждого потока sp_min и пр.
У меня все потоки (и только потоки) выполняются в SYSMODE.
Атрибут __attribute__((no_instrument_function)) используется для того, чтобы функции не контролировали сами себя. Так же можно определить функции, которые никогда не вызываются из потоков (не используют потоковый стек), например обработчики прерываний, или ядерные функции.

Далее, добавил поток, в котором по sp_change контролирую выход за границу стека, а за одно и максимальный размер стека для потока (чтобы зря не тратить память) и вывожу это все в консоль (через UART).
Вместо флага в наверное лучше использовать сигнал, чтобы накладок небыло.

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

Если кто-то знает другие методы, поделитесь.
Go to the top of the page
 
+Quote Post
VslavX
сообщение Sep 5 2007, 20:14
Сообщение #2


embarrassed systems engineer
*****

Группа: Свой
Сообщений: 1 083
Регистрация: 24-10-05
Из: Осокорки
Пользователь №: 10 038



Цитата(Puzan @ Sep 5 2007, 17:53) *
В работе использую GCC. Искал метод определения размера стека для потока в многопоточном
...
Поясню: я использую свою ОС, по этому определил в структуре потока необходимые мне поля. Если не

Обычно при создании нового потока в ОС известна вершина области стека (если растет вниз) и ее размер. При создании потока заполняем эту область какой-нибудь фиксированной моделью, при необходимости проверить переполнение - сканируем стек от "дна" на предмет сравнения с моделью - при несовпадении - считаем что нашли зону максимально использованного стека. Метод не 100-процентный, но зато накладные невелики и практически никакого оверхеда.
Go to the top of the page
 
+Quote Post
Harbour
сообщение Sep 6 2007, 04:25
Сообщение #3


Местами Гуру
*****

Группа: Validating
Сообщений: 1 103
Регистрация: 5-12-04
Пользователь №: 1 323



Последние gcc уже давно имеют опцию -fstack-protector
Go to the top of the page
 
+Quote Post
Puzan
сообщение Sep 6 2007, 07:01
Сообщение #4


Участник
*

Группа: Новичок
Сообщений: 30
Регистрация: 16-12-05
Пользователь №: 12 295



Цитата(VslavX @ Sep 6 2007, 00:14) *
Обычно при создании нового потока в ОС известна вершина области стека (если растет вниз) и ее размер. При создании потока заполняем эту область какой-нибудь фиксированной моделью, при необходимости проверить переполнение - сканируем стек от "дна" на предмет сравнения с моделью - при несовпадении - считаем что нашли зону максимально использованного стека. Метод не 100-процентный, но зато накладные невелики и практически никакого оверхеда.


Да, можно и так. Правда сложно определить, в каком месте произошло переполнение. Вот если совместно с
Цитата
Последние gcc уже давно имеют опцию -fstack-protector

то можно и место определить.

Как это я пропустил эту опцию? blink.gif

Кстати, с -finstrument-functions можно сделать то-же самое, что делает -fstack-protector, но несколько более универсально. Хотя из-за того, что __cyg_profile_func_enter имеет два аргумента, компилятору часто приходися сохранять в стеке r0 и r1, что вызывает дополнительный расход.

И я наварное недопонял, как работает stack-protector.
Как я вижу из дизассемблера, перед восстановлением регистров, удалением локального фрейма и выходом из функции верхушка стека проверяется на соответствие шаблону (__stack_chk_guard). Так если стек уже был "попорчен" предыдущими функциями (но небыл переполнен), то вылезет ложное переполнение?
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Sep 6 2007, 14:34
Сообщение #5


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



Цитата(Puzan @ Sep 6 2007, 10:01) *
Так если стек уже был "попорчен" предыдущими функциями (но небыл переполнен), то вылезет ложное переполнение?
А если в вашем варианте функция разместит на стеке какие-то локальные переменные, попортит ими содержимое памяти за стеком, потом удалит эти переменные и к выходу будет иметь допустимое значение указателя стека - как вы будете отлавливать такую ситуацию?


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
Puzan
сообщение Sep 6 2007, 14:49
Сообщение #6


Участник
*

Группа: Новичок
Сообщений: 30
Регистрация: 16-12-05
Пользователь №: 12 295



Цитата(Сергей Борщ @ Sep 6 2007, 18:34) *
А если в вашем варианте функция разместит на стеке какие-то локальные переменные, попортит ими содержимое памяти за стеком, потом удалит эти переменные и к выходу будет иметь допустимое значение указателя стека - как вы будете отлавливать такую ситуацию?


__cyg_profile_func_enter вызывается после сохранения регистров и выделения стека под фрейм локальных переменных.
Например так:
Код
func:
    stmdb    sp!, {r4, r5, r6, r7, lr}
    mov      r1, lr
    sub      sp, sp, #56
    ldr      r0, [pc, #324]
    mov      r7, lr
    bl       10c <__cyg_profile_func_enter>


По этому выделение стека под локальные переменные отлавливается.
Кстати, у меня пока получалась абсолютная точность определения максимального размера стека.
Go to the top of the page
 
+Quote Post

Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 22nd July 2025 - 11:09
Рейтинг@Mail.ru


Страница сгенерированна за 0.01375 секунд с 7
ELECTRONIX ©2004-2016