|
IAR CSTACK RSTACK - измерение глубины стека |
|
|
|
Apr 17 2016, 11:59
|
Частый гость
 
Группа: Свой
Сообщений: 151
Регистрация: 19-12-04
Из: Москва
Пользователь №: 1 560

|
Привет! По идеям документа с сайта 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; }
|
|
|
|
|
Apr 17 2016, 17:09
|
практикующий тех. волшебник
    
Группа: Участник
Сообщений: 1 190
Регистрация: 9-09-05
Пользователь №: 8 417

|
Цитата(Mty @ Apr 17 2016, 14:59)  ...Идея в том, чтобы через таймерное прерывание... обычно привязывают к коду или потоку при выполнении которого портится стэк. Либо как тут уже прозвучало выше - иметь аппаратные механизмы отслеживающие выход стэка за тот объём, что ему отведён. Либо ставят анализатор на переключении контекста (в случае ОС). Как превентивная мера - можно замерять сколько осталось(используется) по глубине стэк. И при изменении данного показателя производит запись для дальнейшего анализа и разбора. (круглый)
|
|
|
|
|
Apr 18 2016, 11:05
|
Частый гость
 
Группа: Свой
Сообщений: 151
Регистрация: 19-12-04
Из: Москва
Пользователь №: 1 560

|
Цитата(zltigo @ Apr 17 2016, 19:02)  Для контроля за использованим стеков их заполняют константными заначениями и переодически следят за их целостностью. Смысла в мгновенных сравнениях указателей стеков нет никакого. Ок, спасибо. А есть пример как заполнить стеки константами, и получить указатель на начало в программе на рабочем девайсе (не в эмуляторе)?
|
|
|
|
|
Apr 18 2016, 11:21
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
QUOTE (Mty @ Apr 18 2016, 14:05)  А есть пример как заполнить... Как и любой массив памяти. QUOTE получить указатель на начало в программе на рабочем девайсе (не в эмуляторе)? Поскольку стеки УСТАНАВЛИВАЮТСЯ при инициализации, а не назначаются господом богом, то "узнать" на самом деле означает посмотреть в программе где они устанавливаются (очевидно в cstartup). и/bли посмотреть имена сегментов в конфигурации линкера.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Apr 18 2016, 12:33
|
Местный
  
Группа: Участник
Сообщений: 301
Регистрация: 13-12-15
Из: Харьков
Пользователь №: 89 682

|
Цитата(Mty @ Apr 17 2016, 14:59)  С data stack (CSTACK) все вроде должно получиться без проблем, а вот как получить указатель на текущий RSTACK? Так программно доступный регистр SP и есть указатель на RSTACK.
|
|
|
|
|
Apr 18 2016, 12:37
|
Частый гость
 
Группа: Свой
Сообщений: 151
Регистрация: 19-12-04
Из: Москва
Пользователь №: 1 560

|
Друзья, если у кого то есть рабочий код на С по заполнению стека при стартапе - буду признателен. Пока нашел только это на ASM http://caxapa.ru/301695.html?todo=fullPS: zltigo прошу не утруждать себя ответами.
|
|
|
|
|
Apr 18 2016, 14:09
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
QUOTE (Mty @ Apr 18 2016, 15:37)  Пока нашел только это на ASM Неправда. Там описан и абсолютно правильный путь и для IAR + Си. Только, как писал - "посмотреть имена сегментов в конфигурации линкера" таки придется. Надо только думать, а не просто вопрошать "дайте рабочий код" QUOTE PS: zltigo прошу не утруждать себя ответами. Без проблем.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Apr 18 2016, 17:56
|
Частый гость
 
Группа: Участник
Сообщений: 132
Регистрация: 6-02-16
Из: г. Баку
Пользователь №: 90 364

|
А почему у них там (у IAR) стек расположен не вверху памяти? А вверху памяти расположена куча? Непонятно что-то.
Эскизы прикрепленных изображений
|
|
|
|
|
Apr 18 2016, 18:17
|
Частый гость
 
Группа: Участник
Сообщений: 132
Регистрация: 6-02-16
Из: г. Баку
Пользователь №: 90 364

|
Цитата(zltigo @ Apr 18 2016, 21:59)  А потому, что по барабану как, это раз. И IAR тут ни причем - дело хозяйское - как прикажете линкеру, так и будет. Еще вот как раз недавнюю тему почитал. Да - оказывается и так делают. Теперь надо бы переварить это... Посмотреть бы еще примеры под это дело (arm gcc).
|
|
|
|
|
Apr 19 2016, 07:22
|
Частый гость
 
Группа: Свой
Сообщений: 151
Регистрация: 19-12-04
Из: Москва
Пользователь №: 1 560

|
Цитата(aiwa @ Apr 18 2016, 15:33)  Так программно доступный регистр SP и есть указатель на RSTACK. Спасибо, все оказывается просто  Цитата(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; }
|
|
|
|
|
Apr 19 2016, 07:56
|
Частый гость
 
Группа: Свой
Сообщений: 151
Регистрация: 19-12-04
Из: Москва
Пользователь №: 1 560

|
Цитата(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) Может в этом дело?
|
|
|
|
|
Apr 19 2016, 08:04
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
QUOTE (Mty @ Apr 19 2016, 10:56)  Намекаете на то что при возврате 1 сегмент инициализируется 0 по выходе из функции? Или не затирается, зависит от того в какой секции линкеру указано размещать стеки. QUOTE Может в этом дело? Есественно, что из сишной (как и любой другой) функции которую ВЫЗВАЛИ и даже не первой в цепочке, полностью затирать стек ВОЗВРАТОВ нельзя. Затирать стек, инициализируя полностью можно только в стартапе, о чем сразу и писал. Чем вызвано Ваше такое дикое нежелание дописать несколько строк на ASM в стартапе?
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Apr 19 2016, 08:30
|
Местный
  
Группа: Участник
Сообщений: 301
Регистрация: 13-12-15
Из: Харьков
Пользователь №: 89 682

|
Осторожнее надо быть с 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 - Apr 19 2016, 08:40
|
|
|
|
|
Apr 19 2016, 11:17
|
Местный
  
Группа: Участник
Сообщений: 301
Регистрация: 13-12-15
Из: Харьков
Пользователь №: 89 682

|
Цитата Намекаете на то что при возврате 1 сегмент инициализируется 0 по выходе из функции? А как же работает ASM вариант, который тоже возвращает 1 по той же ссылке? В старапе код эквивалентен такому: Код if(_low_level_init()!=0) _segment_init(); // отредактирована ошибка Т.е. если _low_level_init вернула 0, то это означает, что программист желает, чтобы IAR произвел инициализацию сегментов, в противном случае программист берет эти процедуры на себя.
Сообщение отредактировал aiwa - Apr 19 2016, 11:46
|
|
|
|
|
Apr 19 2016, 13:29
|
Местный
  
Группа: Участник
Сообщений: 301
Регистрация: 13-12-15
Из: Харьков
Пользователь №: 89 682

|
Цитата(zltigo @ Apr 19 2016, 15:01)  Так и сделано  Что-то у меня с утра не заладилось. Я имел в виду возврат ненулевого значения означает знак ИАРу, что инициализация сегементов уже выполнена, а возврат 0 - выполнение инициализации ИАРом. Наподобие стандартной процедуры callback-функций, когда при возврате 0 производится стандартная процедура по умолчанию. Цитата(Mty @ Apr 19 2016, 15:32)  Насколько я понимаю он при инициализации сегментов не трогает стеки, а только загружает в DATA переменные? Порядок следующий: 1. в SP заносится значение конца RSTACKа, 2. в Y заносится значение конца СSTACKа, если разрешена внешняя память, то соответствующая аппаратная инициализация. Далее вызывается _low_level_init. Если она вернула ненулевое значение, то вызывается процедура инициализации сегментов _segment_init (Эта процедура уже написана на С.) И потом вызывается процедура построения статистических объектов классов.
|
|
|
|
|
Apr 20 2016, 00:12
|
Местный
  
Группа: Участник
Сообщений: 301
Регистрация: 13-12-15
Из: Харьков
Пользователь №: 89 682

|
Заинтересовало, по быстрому написал первоначальный вариант, но не проверял. 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 - Apr 20 2016, 00:37
|
|
|
|
|
Apr 20 2016, 08:57
|
Местный
  
Группа: Участник
Сообщений: 301
Регистрация: 13-12-15
Из: Харьков
Пользователь №: 89 682

|
Посмотрел с утра - код работать не будет. Не учел, что переменные ИАР располагает в сегментах от конца к началу, и что адресация флеш-памяти вдвое больше. Этот код прошел в отладчике: 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';
Сообщение отредактировал aiwa - Apr 20 2016, 10:09
|
|
|
|
|
Apr 20 2016, 11:33
|
Частый гость
 
Группа: Свой
Сообщений: 151
Регистрация: 19-12-04
Из: Москва
Пользователь №: 1 560

|
Спасибо, круто! А вот интересно register char* point=(char*)SP; SP указывает на ПУСТУЮ ячейку или на ячейку с предыдущими данными? Иными словами при PUSH сначала копируется данные, а потом инкрементируется SP или наоборот?
Сообщение отредактировал IgorKossak - Apr 20 2016, 17:21
Причина редактирования: бездумное цитирование
|
|
|
|
|
Apr 20 2016, 13:27
|
Местный
  
Группа: Участник
Сообщений: 301
Регистрация: 13-12-15
Из: Харьков
Пользователь №: 89 682

|
Цитата(Mty @ Apr 20 2016, 14:33)  register char* point=(char*)SP; SP указывает на ПУСТУЮ ячейку или на ячейку с предыдущими данными? На пустую. Иначе бы затирался адрес возврата.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|