Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: GCC и переполнение стека в многопоточных приложениях
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Puzan
В работе использую 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).
Вместо флага в наверное лучше использовать сигнал, чтобы накладок небыло.

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

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

Обычно при создании нового потока в ОС известна вершина области стека (если растет вниз) и ее размер. При создании потока заполняем эту область какой-нибудь фиксированной моделью, при необходимости проверить переполнение - сканируем стек от "дна" на предмет сравнения с моделью - при несовпадении - считаем что нашли зону максимально использованного стека. Метод не 100-процентный, но зато накладные невелики и практически никакого оверхеда.
Harbour
Последние gcc уже давно имеют опцию -fstack-protector
Puzan
Цитата(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). Так если стек уже был "попорчен" предыдущими функциями (но небыл переполнен), то вылезет ложное переполнение?
Сергей Борщ
Цитата(Puzan @ Sep 6 2007, 10:01) *
Так если стек уже был "попорчен" предыдущими функциями (но небыл переполнен), то вылезет ложное переполнение?
А если в вашем варианте функция разместит на стеке какие-то локальные переменные, попортит ими содержимое памяти за стеком, потом удалит эти переменные и к выходу будет иметь допустимое значение указателя стека - как вы будете отлавливать такую ситуацию?
Puzan
Цитата(Сергей Борщ @ 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>


По этому выделение стека под локальные переменные отлавливается.
Кстати, у меня пока получалась абсолютная точность определения максимального размера стека.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.