Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: IAR CSTACK RSTACK - измерение глубины стека
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Mty
Привет!

По идеям документа с сайта IAR
https://www.iar.com/support/resources/artic...em-reliability/
хочу напрямую смотреть использование стека в процессе выполнения программы

Идея в том, чтобы через таймерное прерывание периодически замерять указатель на автоматическую переменную, и мониторить сколько стека в использовании.
С data stack (CSTACK) все вроде должно получиться без проблем, а вот как получить указатель на текущий RSTACK?

C data stack идея такова -
Код
char *highStack, *lowStack;
int main(int argc, char *argv[])
{
highStack = (char *)&argc;
// ...
printf("Current stack usage: %d\n", highStack - lowStack);
}

void sampling_timer_interrupt_handler(void)
{
char* currentStack;
int a;
currentStack = (char *)&a;
if (currentStack < lowStack) lowStack = currentStack;
}


zltigo
QUOTE (Mty @ Apr 17 2016, 14:59) *
Идея в том, чтобы через таймерное прерывание периодически замерять указатель на автоматическую переменную, и мониторить сколько стека в использовании.

Для контроля за использованим стеков их заполняют константными заначениями и переодически следят за их целостностью. Смысла в мгновенных сравнениях указателей стеков нет никакого.

IgorKossak
Цитата(zltigo @ Apr 17 2016, 19:02) *
Для контроля за использованим стеков их заполняют константными заначениями и переодически следят за их целостностью. Смысла в мгновенных сравнениях указателей стеков нет никакого.

+1 Тем более, что этот метод не является в прямом смысле "мгновенным", т. к. не позволяет уличить момент порчи стека, а в случае с AVR и никакой другой метод этого не позволит - AVR ядро не содержит таких аппаратных возможностей.
kolobok0
Цитата(Mty @ Apr 17 2016, 14:59) *
...Идея в том, чтобы через таймерное прерывание...


обычно привязывают к коду или потоку при выполнении которого портится стэк.
Либо как тут уже прозвучало выше - иметь аппаратные механизмы отслеживающие выход стэка за тот объём, что ему отведён.
Либо ставят анализатор на переключении контекста (в случае ОС).

Как превентивная мера - можно замерять сколько осталось(используется) по глубине стэк. И при изменении данного показателя производит запись
для дальнейшего анализа и разбора.

(круглый)
Mty
Цитата(zltigo @ Apr 17 2016, 19:02) *
Для контроля за использованим стеков их заполняют константными заначениями и переодически следят за их целостностью. Смысла в мгновенных сравнениях указателей стеков нет никакого.


Ок, спасибо.
А есть пример как заполнить стеки константами, и получить указатель на начало в программе на рабочем девайсе (не в эмуляторе)?
zltigo
QUOTE (Mty @ Apr 18 2016, 14:05) *
А есть пример как заполнить...

Как и любой массив памяти.
QUOTE
получить указатель на начало в программе на рабочем девайсе (не в эмуляторе)?

Поскольку стеки УСТАНАВЛИВАЮТСЯ при инициализации, а не назначаются господом богом, то "узнать" на самом деле означает посмотреть в программе где они устанавливаются (очевидно в cstartup). и/bли посмотреть имена сегментов в конфигурации линкера.
aiwa
Цитата(Mty @ Apr 17 2016, 14:59) *
С data stack (CSTACK) все вроде должно получиться без проблем, а вот как получить указатель на текущий RSTACK?


Так программно доступный регистр SP и есть указатель на RSTACK.
Mty
Друзья, если у кого то есть рабочий код на С по заполнению стека при стартапе - буду признателен.

Пока нашел только это на ASM
http://caxapa.ru/301695.html?todo=full

PS: zltigo прошу не утруждать себя ответами.
zltigo
QUOTE (Mty @ Apr 18 2016, 15:37) *
Пока нашел только это на ASM

Неправда. Там описан и абсолютно правильный путь и для IAR + Си. Только, как писал - "посмотреть имена сегментов в конфигурации линкера" таки придется.
Надо только думать, а не просто вопрошать "дайте рабочий код"
QUOTE
PS: zltigo прошу не утруждать себя ответами.

Без проблем.

AleksBak
А почему у них там (у IAR) стек расположен не вверху памяти? А вверху памяти расположена куча? Непонятно что-то.



zltigo
QUOTE (AleksBak @ Apr 18 2016, 20:56) *
А почему у них там (у IAR) стек расположен не вверху памяти?

А потому, что по барабану как, это раз. И IAR тут ни причем - дело хозяйское - как прикажете линкеру, так и будет.
AleksBak
Цитата(zltigo @ Apr 18 2016, 21:59) *
А потому, что по барабану как, это раз. И IAR тут ни причем - дело хозяйское - как прикажете линкеру, так и будет.

Еще вот как раз недавнюю тему почитал. Да - оказывается и так делают. Теперь надо бы переварить это... Посмотреть бы еще примеры под это дело (arm gcc).
zombi
Цитата(Mty @ Apr 17 2016, 14:59) *
хочу напрямую смотреть использование стека в процессе выполнения программы

Вам правильно посоветовали использовать предварительное заполнение области стека константой с дальнейшим анализом содержимого оной.
Этот метод даст максимально точный результат. Хотя и не 100%.
А насколько точный результат глубины заполнения стека Вы хотите получить?
Mty
Цитата(aiwa @ Apr 18 2016, 15:33) *
Так программно доступный регистр SP и есть указатель на RSTACK.


Спасибо, все оказывается просто sm.gif


Цитата(zombi @ Apr 19 2016, 02:37) *
Вам правильно посоветовали использовать предварительное заполнение области стека константой с дальнейшим анализом содержимого оной.
Этот метод даст максимально точный результат. Хотя и не 100%.
А насколько точный результат глубины заполнения стека Вы хотите получить?


Большой точности не надо, просто приблизительная оценка на реальной работе системы.

Способ с заполнением хорош, только вот примера на С рабочего нет. В примере по ссылке, про который я писал имена сегментов выставлены правильные, в map файле они так и называются CSTACK и RSTACK но пример нерабочий.

Код
char __low_level_init()
{
    #pragma segment="CSTACK"
    char* p = (char*)__segment_begin("CSTACK");
    size_t len = (size_t)__segment_end("CSTACK") - (size_t)__segment_begin("CSTACK");
    
    while( len-- )
        *p++ = 'C';
    
    p = (char*)__segment_begin("RSTACK");
    len = (size_t)__segment_end("RSTACK") - (size_t)__segment_begin("RSTACK");
    
    while( len-- )
        *p++ = 'R';

    return 1;
}
zltigo
QUOTE (Mty @ Apr 19 2016, 10:22) *
CODE
return 1;

Смотреть на "1". И думать что происходит при возврате 1 функцией __low_level_init

P.S.
Mty вышенаписаное читать не надо. Это только для тех, кому интересно, как это может не работать.
Mty
Цитата(zltigo @ Apr 19 2016, 10:33) *
Смотреть на "1". И думать что происходит при возврате 1 функцией __low_level_init


Хорошо в EWAVR_CompilerReference.pdf

Код
The value returned by __low_level_init  determines whether or  not data segments  should be initialized by  the system startup code. If the function returns
0 , the data  segments will not be initialized.


Намекаете на то что при возврате 1 сегмент инициализируется 0 по выходе из функции?
А как же работает ASM вариант, который тоже возвращает 1 по той же ссылке?

И раз уж пошел конструктивный диалог, давайте предположим что может дело в том что в С варианте затирается весь RSTACK
а в ASM (SIZEOF RSTACK)-10)
Может в этом дело?
zltigo
QUOTE (Mty @ Apr 19 2016, 10:56) *
Намекаете на то что при возврате 1 сегмент инициализируется 0 по выходе из функции?

Или не затирается, зависит от того в какой секции линкеру указано размещать стеки.
QUOTE
Может в этом дело?

Есественно, что из сишной (как и любой другой) функции которую ВЫЗВАЛИ и даже не первой в цепочке, полностью затирать стек ВОЗВРАТОВ нельзя. Затирать стек, инициализируя полностью можно только в стартапе, о чем сразу и писал. Чем вызвано Ваше такое дикое нежелание дописать несколько строк на ASM в стартапе?
aiwa
Осторожнее надо быть с RSTACKом, потому что такой код его может подпортить:

Код
p = (char*)__segment_begin("RSTACK");
    len = (size_t)__segment_end("RSTACK") - (size_t)__segment_begin("RSTACK");
    
    while( len-- )
        *p++ = 'R';


когда стартовый код вызывает __low_level_init, он заносит адрес возрата в RSTACK.
отступите от __segment_end("RSTACK") на величину адреса возврата.

И второе,
Код
char* p = (char*)__segment_begin("CSTACK");

использование локальных переменных может быть проблематичным и скорее всего будет.
Наверняка при создании этой переменной компилятор задействует CSTACK
aiwa
Цитата
Намекаете на то что при возврате 1 сегмент инициализируется 0 по выходе из функции?
А как же работает ASM вариант, который тоже возвращает 1 по той же ссылке?

В старапе код эквивалентен такому:
Код
if(_low_level_init()!=0) _segment_init(); // отредактирована ошибка

Т.е. если _low_level_init вернула 0, то это означает, что программист желает, чтобы IAR произвел инициализацию сегментов,
в противном случае программист берет эти процедуры на себя.
zltigo
QUOTE (aiwa @ Apr 19 2016, 14:17) *
Т.е. если _low_level_init вернула 0, то это означает, что программист желает, чтобы IAR произвел инициализацию сегментов,

C точностью до наоборот sad.gif
aiwa
Да, наоборот. Подправлю.

Кстати, нетрадиционное решение ИАРовцы приняли.
Логичней было бы пропускать инициализацию при возрате нуля.
zltigo
QUOTE (aiwa @ Apr 19 2016, 14:44) *
Кстати, нетрадиционное решение ИАРовцы приняли.
Логичней было бы пропускать инициализацию при возрате нуля.

Так и сделано sm.gif
Mty
Насколько я понимаю он при инициализации сегментов не трогает стеки, а только загружает в DATA переменные?
aiwa
Цитата(zltigo @ Apr 19 2016, 15:01) *
Так и сделано sm.gif

Что-то у меня с утра не заладилось. Я имел в виду возврат ненулевого значения означает знак ИАРу, что инициализация сегементов уже выполнена,
а возврат 0 - выполнение инициализации ИАРом. Наподобие стандартной процедуры callback-функций, когда при возврате 0 производится стандартная
процедура по умолчанию.

Цитата(Mty @ Apr 19 2016, 15:32) *
Насколько я понимаю он при инициализации сегментов не трогает стеки, а только загружает в DATA переменные?


Порядок следующий:
1. в SP заносится значение конца RSTACKа,
2. в Y заносится значение конца СSTACKа,
если разрешена внешняя память, то соответствующая аппаратная инициализация.
Далее вызывается _low_level_init.
Если она вернула ненулевое значение, то вызывается процедура инициализации сегментов _segment_init (Эта процедура уже написана на С.)
И потом вызывается процедура построения статистических объектов классов.
zltigo
QUOTE (Mty @ Apr 19 2016, 15:32) *
Насколько я понимаю он при инициализации сегментов не трогает стеки, а только загружает в DATA переменные?

Я уже писал - зависит от того, в каком сегменте память под стеки указана. Распределение смотреть у линкера.
aiwa
Заинтересовало, по быстрому написал первоначальный вариант, но не проверял.
CODE
#pragma segment="RSTACK"
static __no_init unsigned char a[1] @ "RSTACK";
typedef char ( *point_to_lowlevelinit)(void);

char __low_level_init()
{
register unsigned int count=__segment_size("RSTACK")-1;
count-=sizeof(point_to_lowlevelinit);

while(count)
{
a[count]='R';
count--;
}
return 1;
}


Небольшие комментарии:
1. переменная а служит лишь меткой, для того чтобы компилятор занес в пару регистров начало RSTACKа
По хорошему нужно брать значение из переменной SP, которая уже отступила от вершины стека на 1 адрес.
2. переменная count описана как register чтобы надеяться, что компилятор не трогал софт-стек.
3. определение point_to_lowlevelinit введено лишь для получения размера адреса возврата который уже занесен
в стек - нужно отнять от это количество от count чтобы не затереть этот адрес.
Здесь в документации лучше бы найти более подходящий способ, наверняка должны быть какие-то системные константы.

Для заполнения софт-стека действуем аналогично.
Еще бы нужно добавить через pragma отключение оптимизации для этой функции.
aiwa
Посмотрел с утра - код работать не будет. Не учел, что переменные ИАР располагает в сегментах от конца к началу, и что адресация флеш-памяти вдвое больше.

Этот код прошел в отладчике:
CODE
char __low_level_init()
{
register char* point=(char*)SP;
register char* pointtest=(char*)__segment_begin("RSTACK");

while(1)
{
*point='R';
point--;
if(point<pointtest) break;
}
return 1;
}

Для частных случаев работоспособен.

P.S. оказалось, что первоначальный вариант тоже работал, сбило то , под сегмент стека ИАР выделил "страницу" памяти и сам стек занимал в нем лишь половину. В том примере нужно лишь добавть инициализацию a[0]='R';
Mty
Спасибо, круто!

А вот интересно
register char* point=(char*)SP;
SP указывает на ПУСТУЮ ячейку или на ячейку с предыдущими данными?
Иными словами при PUSH сначала копируется данные, а потом инкрементируется SP или наоборот?
zltigo
QUOTE (Mty @ Apr 20 2016, 14:33) *
Иными словами при PUSH сначала копируется данные, а потом инкрементируется SP или наоборот?

Все четыре варианта на выбор sm.gif F(full)D(descending)/FA(ascending)/E(empty)D/EA если ARM sm.gif
У AVR на вскидку не помню, но скорее на Empty указывает.

aiwa
Цитата(Mty @ Apr 20 2016, 14:33) *
register char* point=(char*)SP;
SP указывает на ПУСТУЮ ячейку или на ячейку с предыдущими данными?

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