|
Обязательный префикс функции. |
|
|
|
 |
Ответов
(1 - 68)
|
Aug 3 2016, 08:58
|
Местный
  
Группа: Свой
Сообщений: 437
Регистрация: 27-08-04
Пользователь №: 551

|
QUOTE (jcxz @ Aug 3 2016, 11:47)  Т.е. - чтобы после входа в каждую функцию в проекте, после PUSH, он вставлял некий фиксированный код (hook). Возможно это несколько ассемблерных строк или просто вызов функции BL (естественно с предварительным сохранением исходного LR на стеке). Видел такую возможность у какого-то компилятора, не помню точно, но вроде у CCS. В яре такого вроде не было, лично я так и не нашел. Такое точно есть в гцц. Совсем недавно натыкался на бложик с описанием такого механизма. Там его применяли для построения стека вызовов
|
|
|
|
|
Aug 3 2016, 10:47
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(jcxz @ Aug 3 2016, 13:23)  Забитие шаблоном - не лучший метод. Так как стек успеет переполниться, ПО повиснет, а я об этом так и не узнаю. Не говоря уже о ненулевой вероятности совпадения записываемых данных с шаблоном. Был бы в Cortex-M более функциональный MPU, сделал бы на нём. А так: или префикс в начало каждой функции или периодическая проверка в ISR высокочастотного таймера. Других способов не вижу пока. J-Link Pro в IAR показывает полную пирамиду вызовы в реальном времени в окне timeline. И весь перечень вызовов в любой точке останова в окне Call Stack. Если применить MQX то будет отображаться стек вызовов для каждой задачи отдельно. Также будет виден максимальный расход стека для каждой задачи. Также покажет автоматически все повреждения служебных структур RTOS. Покажет и переполнение менеджера памяти и фрагментацию памяти.
|
|
|
|
|
Aug 3 2016, 11:11
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(AlexandrY @ Aug 3 2016, 16:47)  J-Link Pro в IAR показывает полную пирамиду вызовы в реальном времени в окне timeline. И весь перечень вызовов в любой точке останова в окне Call Stack. И что это даст? Мне нужно максимальное использование стека. По всем задачам. Цитата(AlexandrY @ Aug 3 2016, 16:47)  Если применить MQX то будет отображаться стек вызовов для каждой задачи отдельно. Также будет виден максимальный расход стека для каждой задачи. У меня не MQX. И как интересно MQX это вычисляет? Периодически анализируя затёртость шаблона в отдельной сервисной задаче? Плохо, так как это никак не спасает при переполнении стека. Я прекрасно понимаю, что заполняя стек шаблоном, можно с большой долей вероятности оценить глубину использования стека. И собственно так сейчас и делаю. Но это работает, только если нет случаев переполнения стека! Цитата(scifi @ Aug 3 2016, 16:44)  В ассемблерном листинге заменить все BL на запрещённый опкод (уж это, наверное, можно автоматизировать). Далее обрабатывать исключение на invalid opcode. Ну да. А ещё BLX. А ещё и все B проанализировать, так как они тоже могут являться вызовами функций (если такой вызов находится в конце другой функции). Да и что потом с этим листингом делать? Как из него прошивку получить? Заменять тогда уж надо не BL, а все PUSH и SUB SP,#...
|
|
|
|
|
Aug 3 2016, 13:16
|
Профессионал
    
Группа: Свой
Сообщений: 1 123
Регистрация: 8-03-09
Из: Днепр
Пользователь №: 45 848

|
да простят меня ....  переобразовать return в Return по тексту проекта или тамгденадо Код #define RET_SWITCH
#ifndef RET_SWITCH #define Return return #else #define Return SavePC(); SaveStack; \ return(1); \ #endif С аргументом для ret надо вывернуться и для void f() всеравно писать return.
Сообщение отредактировал k155la3 - Aug 3 2016, 13:18
|
|
|
|
|
Aug 3 2016, 13:36
|

неотягощённый злом
     
Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643

|
Цитата(ig_z @ Aug 3 2016, 11:58)  Такое точно есть в гцц. Да. Это опция компилятора/линкера -pgМожно почитать про mcount и profile hook. Наверняка какой то профайлер и для яра имеется.
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
Aug 3 2016, 15:20
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(k155la3 @ Aug 3 2016, 19:16)  да простят меня ....  переобразовать return в Return по тексту проекта или тамгденадо Ну да: void func1() { func2(); } void func2() { func3(); } void func3() { func4(); } ... А теперь представьте, что переполнение стека произошло ещё в func1(). До func4() (где Вашим способом это можно будет обнаружить) дело уже может и не дойти, так как к этому времени перепахает полпамяти. Да и очень желательно всё-же оставаться в синтаксисе языка си, который не обязывает ставить return в таких функциях. К тому же: не все функции, которые будут в результирующем коде, присутствуют в исходниках (оптимизатор тоже может сам генерить функции). Я уже не говорю, что "вывернуться с аргументом для ret" получится далеко не для всех аргументов. И вообще - надо чтобы си-исходник оставался обычным читаемым си-исходником, а не некоей тарабарщиной на непонятном языке. PS: Вобщем, похоже, что вариант с высокочастотным таймером - безальтернативен для IAR...  Цитата(demiurg_spb @ Aug 3 2016, 19:36)  Наверняка какой то профайлер и для яра имеется. Вот потому и спрашиваю. Прошерстил мануал IAR-а - не нашёл подходящего. Может плохо искал.....
|
|
|
|
|
Aug 3 2016, 18:14
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(AlexandrY @ Aug 3 2016, 22:11)  А идея про вставку хуков довольно наивна. В либы все равно хуки не вставить. Терпимо: у меня в проектах нигде нет либ. Всё только в исходниках. Цитата(AlexandrY @ Aug 3 2016, 22:11)  Да и будет повод для появления ошибок Гейзенберга. Это кто такой? Я его знаю? Ну тогда и не будем его брать в команду раз он ошибки делает
|
|
|
|
|
Aug 4 2016, 02:58
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(AlexandrY @ Aug 4 2016, 01:46)  Т.е. ни printf, ни sprintf, ни какой-нить там strcpy или memcpy ?  Не поверите, но sprintf() не использую нигде в embedded (в IAR и CCS по-крайней мере). В IAR-проектах вместо неё использую _Printf() - она удобнее. И вызываю её чаще всего с переключением на отдельный стек (только ейный) защищённый семафором. Так как sprintf()-подобные функции отъедают много стека, а у меня в большинстве проектов памяти мало - приходится экономить и сериализировать выполнение _Printf(). Хотя согласен - завтра может понадобится и её напрямую вызывать из задач без переключения стека. Но с _Printf() чуть проще - ей в качестве методов вывода передаются указатели на мои собственные функции (putchar()-подобные), которые должны вызываться, я думаю, на самой глубине использования стека. Если между входом в _Printf() и входом в мой метод putchar() стек не успеет переполнится, то уже внутри моего putchar() SP зафиксируется. memcpy() конечно использую, но у неё не очень большая глубина использования стека - массивов на стеке она по-крайней мере не выделяет. PS: В принципе, я думаю, если есть возможность включить такие префиксы, то возможно перекомпилять стандартную библиотеку с этим ключом и будут они и в библиотечных функциях.
|
|
|
|
|
Aug 4 2016, 06:59
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(jcxz @ Aug 4 2016, 09:29)  По этому слову в доке IAR тоже ничего не находится....  Специально залез в доку IAR посмотреть наличие проблемы, потому как немыслимо чтобы IAR не выделял внимания контролю стека. Ну и точно, там море возможностей прецизионно контролировать стек. Читайте EWARM_DevelopmentGuide.ENU.pdf со страницы 92 Цитата(jcxz @ Aug 4 2016, 05:58)  Не поверите, но sprintf() не использую нигде в embedded (в IAR и CCS по-крайней мере). В IAR-проектах вместо неё использую _Printf() - она удобнее. Почему же, верю. В моих проектах можно найти по 3-4 отдельных автономных реализаций sprintf. Ибо каждая RTOS, каждый пакет претендующий на мультиплатформенность содержит файлы с переписанными стандартными библиотеками C-и. Это не удобство, а трагедия, поскольку возникает еще один повод для путаницы.
|
|
|
|
|
Aug 4 2016, 08:23
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(AlexandrY @ Aug 4 2016, 12:59)  Специально залез в доку IAR посмотреть наличие проблемы, потому как немыслимо чтобы IAR не выделял внимания контролю стека. Ну и точно, там море возможностей прецизионно контролировать стек. Читайте EWARM_DevelopmentGuide.ENU.pdf со страницы 92 Именно эту доку я и изучал. На этой странице у меня "CHOOSING A LINKER CONFIGURATION FILE" и др. мало относящиеся к контролю стека главы. Не сочтите за труд указать название главы? Цитата(AlexandrY @ Aug 4 2016, 12:59)  Ибо каждая RTOS, каждый пакет претендующий на мультиплатформенность содержит файлы с переписанными стандартными библиотеками C-и. Это не удобство, а трагедия, поскольку возникает еще один повод для путаницы. Я не гонюсь за мультиплатформенностью. Она малополезна в embedded, когда проект привязан даже не только к ядру, но и более сильно - периферии.
|
|
|
|
|
Aug 4 2016, 08:40
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(jcxz @ Aug 4 2016, 11:23)  Не сочтите за труд указать название главы?
Я не гонюсь за мультиплатформенностью. Она малополезна в embedded, когда проект привязан даже не только к ядру, но и более сильно - периферии. "Stack usage analysis" А кто гонится? Все мы работаем одинаково. Поэтому я и знаю где реальная проблема обсуждается, а где надуманная или несущественная. Открытые проекты с исходниками все поголовно мультиплатформенные, это не нам решать, это их природа такая, иначе они не выживают. Мы их берем и затачиваем. А если не можем заточить, то юзаем GCC и говорим что это круто.
|
|
|
|
|
Aug 4 2016, 10:11
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(AlexandrY @ Aug 4 2016, 14:40)  "Stack usage analysis" Не подходит. Толку от него мало. В коде куча косвенных вызовов функций. И это не подходит: but if there are indirect calls (calls using function pointers) in your application, you must supply a list of possible functions that can be called from each calling function. Так как не я один пишу этот код, а есть товарищи, любящие всякие классы с виртуальными член-функциями и пихающие их куда нужно и не нужно. А заставить их делать это для своего кода - нереально. Да и малоэффективно. Да и опять-же - в случае использования чужого кода (всяких стеков и либ), что делать? Шерстить весь этот код для supply a list of possible functions??? Нужен именно run-time контроль, а не build-time.
|
|
|
|
|
Aug 4 2016, 12:38
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(jcxz @ Aug 4 2016, 13:11)  Не подходит. Толку от него мало. В коде куча косвенных вызовов функций.
Так как не я один пишу этот код, а есть товарищи, любящие всякие классы с виртуальными член-функциями и пихающие их куда нужно и не нужно. А заставить их делать это для своего кода - нереально. Да и малоэффективно. Не куча, а где нибудь в районе сотни. IAR все косвенные вызовы аккуратно показывает в Call graph-е. Если у вас есть к тому же товарищи, то раздать им по десятку таких вызовов и вы за день сделаете управляющий файл для анализатора вызовов. По любому косвенные вызовы надо знать в лицо, как самые граблеопасные места. Про эффективность не понял. Анализатор вызовов абсолютно точно считает стек. И если стоит задача урезать максимально стек лучшего способа не существует.
|
|
|
|
|
Aug 5 2016, 08:28
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(AlexandrY @ Aug 4 2016, 18:38)  Если у вас есть к тому же товарищи, то раздать им по десятку таких вызовов и вы за день сделаете управляющий файл для анализатора вызовов. По любому косвенные вызовы надо знать в лицо, как самые граблеопасные места. Это Вы так считаете. И я. А товарищам объяснить невозможно - они всё равно делают как делали и не хотят ни в чём разбираться. А ПО потом глючить начинает совсем в другом месте, не там где набыдлокодили. Цитата(AlexandrY @ Aug 4 2016, 18:38)  Про эффективность не понял. Неэффективно, так как требует постоянного контроля кода, заполнения чего-то там при каждом изменении/добавлении/убирании этих самых функций. Да и ещё и правильного заполнения. 100% что забудут добавить или неправильно добавят или.... и всё насмарку - опять ищи почему глючит. Механизм контроля должен быть внешний, не требующий постояных трудозатрат на его поддержание и чтобы его один раз реализовал и забыл. И только включать быстро (одним дефайном или ещё чем) когда надо и отключать когда не надо. А иначе проще тогда уж по-старинке: залить стеки шаблоном и глазами проконтролировать затёртость шаблона.
|
|
|
|
|
Aug 5 2016, 10:52
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(jcxz @ Aug 5 2016, 11:28)  Неэффективно, так как требует постоянного контроля кода, заполнения чего-то там ... Т.е. хотите забабахать статистический движок как в линуксах? И стека не жалко, и ресурсов процессорного времени и риалтайма? Я думаю если даже Segger этого не сделал, то это никому и не надо. Старых добрых методов хватает. Да и статистика дело тонкое. Не скажете же заказчику после того как все рухнуло, что вероятность переполнения стека была всего то 0.1% с уровнем доверия 95% при эмпирической гипотезе о распределении.
|
|
|
|
|
Aug 8 2016, 11:24
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(AlexandrY @ Aug 5 2016, 16:52)  Т.е. хотите забабахать статистический движок как в линуксах? И стека не жалко, и ресурсов процессорного времени и риалтайма? Причём тут какой-то движок? Причём тут заказчик??? Речь идёт об отладке ПО и средствах отладки. Идея простейшая: при каждом изменении SP (в си - после входа в функцию и выделения стекового фрейма), проверять SP на допустимость (нахождение в некотором диапазоне адресов). Переменные, хранящие контролируемый диапазон, обновлять при каждом переключении задач (в соответствующем хуке ОС). Это если бы была возможность написать свой пролог функции. Если нет, другой способ: так же контролировать SP на нахождение в диапазоне только периодически, как можно чаще, в высокочастотном таймерном прерывании. Дополнительного стека для этого не нужно. Процессорное время конечно нужно, но этот контроль нужно включать только тогда когда нужно - при отладке, при возникновении подозрения на переполнение стека, при каком-то непонятном поведении ПО, чтобы отбросить вариант переполнения стека. Когда проверять не нужно - просто комментируем соответствующий #define и всё. У меня сейчас так же уже реализована проверка на максимальную длительность запрета прерываний в ПО: запускается ВЧ прерывание и контролируются интервалы времени между прерываниями. Работает отлично - уже не одно место нашёл, где коллеги кривыми ручками надолго запрещали прерывания, нарушая тем самым работу драйверов периферии. Можно конечно по-старинке: просматривать затёртость шаблона заполнения стека. Но это менее удобно и самое главное - высока вероятность пропуска некоторых случаев переполнения стека. Идеально было-бы если бы процессорное ядро имело средства контроля выхода SP за некий диапазон - генерило какое-либо исключение в таком случае. Но разработчики ядра не позаботились об этом, жаль...
|
|
|
|
|
Aug 8 2016, 12:47
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(jcxz @ Aug 8 2016, 14:24)  Идея простейшая: при каждом изменении SP (в си - после входа в функцию и выделения стекового фрейма), проверять SP на допустимость Этот пункт вроде проехали. Это делать нет смысла. Статический анализатор по сравнению с этим гораздо легче настроить. Если, конечно, не брать в расчет неких демонов-товарищей, целенаправленно мешающих настроить анализатор. Цитата(jcxz @ Aug 8 2016, 14:24)  другой способ: так же контролировать SP на нахождение в диапазоне только периодически А это сделано под линуксом, и это именно движок и именно статистический со своим набором переменных, каналом связи и проч. и расходом стека естественно. А про контроль времени выполнения прерываний и их блокировки я уже говорил. J-Link с точностью до такта логирует все! без исключения события прерываний и ведет их статистику (мин. макс. интервалы, длительности и проч. ) Здесь ничего "реализовывать" не требуется.
|
|
|
|
|
Aug 9 2016, 10:55
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(AlexandrY @ Aug 8 2016, 18:47)  Этот пункт вроде проехали. Это делать нет смысла. Статический анализатор по сравнению с этим гораздо легче настроить. Что такое "Статический анализатор" и как его настроить? Цитата(AlexandrY @ Aug 8 2016, 18:47)  А это сделано под линуксом, и это именно движок и именно статистический со своим набором переменных, каналом связи и проч. и расходом стека естественно. Вопрос не про линух. Цитата(AlexandrY @ Aug 8 2016, 18:47)  А про контроль времени выполнения прерываний и их блокировки я уже говорил. J-Link с точностью до такта логирует все! без исключения события прерываний и ведет их статистику (мин. макс. интервалы, длительности и проч. ) Как этим пользоваться? А если длинный запрет прерывания происходит только иногда, достаточно редко, при совпадении неких условий? Просматривать полотенца логов и искать?
|
|
|
|
|
Aug 9 2016, 13:07
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(jcxz @ Aug 9 2016, 14:42)  А каким образом с его помощью можно контролировать SP или доступ к памяти через SP? Да никак, никому это не нужно. Сначала скажите, вы боретесь с ошибкой или с саботажем? Если с ошибкой, то ставите watchpoint на проблемном месте и находите баг за считанные минуты. Тут даже говорит не о чем. Подбирать нужную глубину стека новички учатся на первый год стажировки. Другое дело с конфликтами прерываний. Но не вижу способа чем может помочь в этом еще одно высокочастотное прерывание. Какое бы оно не было высокочастотное оно все равно случится постфактум, когда все уже сбилось и рухнуло. В таких вещах просто вставляется диагностический код, но не тотально везде, а целевой, на поиск только определенной ошибки по известному диагнозу. Скажем засекаем метки времени в своем прерывании и делаем останов когда время затянулось дольше обычного. Правда без таймлайна JLink тут тоже мало толку будет. Таймлайн это все!
|
|
|
|
|
Aug 10 2016, 06:37
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(AlexandrY @ Aug 9 2016, 19:07)  Да никак, никому это не нужно. Сначала скажите, вы боретесь с ошибкой или с саботажем? Если с ошибкой, то ставите watchpoint на проблемном месте и находите баг за считанные минуты. Тут даже говорит не о чем. Я вижу саботаж только здесь. Вы действительно не понимаете сути вопроса или это троллинг? Мне кажется вопрос кристалльно ясен. Ситуация: void f() { char b[100]; b[0] = некое_выражение; ... } Эта функция вызывается когда до границы стека текущей задачи осталось например 50 байт. Внимание вопросы: Что будет со стеком и с SP? Что будет с памятью, находящейся ниже стека текущей задачи? Каким образом при помощи watchpoint (или каких-то других сервисов) обнаружить факт выхода SP за границы стека текущей задачи (и записи по SP) в такой ситуации? При условии что в памяти расположенной ниже стека текущей задачи, могут находиться другие переменные, запись в которые через другие регистры (не SP) - штатна, и запись через SP из других задач - штатна, и не должна вызывать срабатывания механизма обнаружения переполнения стека. В качестве доп. условий: работа идёт в многозадачной среде, есть N задач, каждая со своим стеком. Цитата(AlexandrY @ Aug 9 2016, 19:07)  Другое дело с конфликтами прерываний. Но не вижу способа чем может помочь в этом еще одно высокочастотное прерывание. Какое бы оно не было высокочастотное оно все равно случится постфактум, когда все уже сбилось и рухнуло. Если частота его будет такова, что прерывания будут происходить каждые десяток-два команд, то высока вероятность того, что SP уже сбился и выполнение начало куда-то уходить, но содержимое регистров/памяти ещё не успело разрушиться настолько что невозможно найти место где произошло выполнение или CPU вообще не завис. Т.е. - функция то исправна, и она продолжает выполняться, ну разрушила она чужую память, находящуюся ниже её стека, ну были там адреса возврата, по которым PC улетит в неизвестном направлении, но это случится только когда ОС передаст управление той задаче, чьи данные были разрушены. Это может быть не скоро и до этого успеет сработать ловушка по ВЧ прерыванию, контролирующая SP. Вот был у нас такой реальный факт переполнения стека: Проблемный стек, переполнялся только иногда, когда по протоколу обмена устройству приходили только определённые запросы от клиента в заранее непредсказуемое время, которые обрабатывались задачей обработки протокола обмена сессионного уровня. Ниже проблемного стека находилась область памяти связных блоков данных (построенная типа массива блоков фиксированного размера, выделяемых динамически по запросу драйверов каналов связи). В эти блоки драйвера каналов связи писали входящие кадры протокола обмена (и исходящие тоже). Устройство может работать по нескольким каналам связи одновременно, все драйвера каналов связи асинхронны друг к другу. Симпотмы проявления: иногда почему-то устройство не отвечало на запросы клиента, причём совершенно произвольно - то всё ок, то какие-то запросы почему-то терялись. Хотя канал связи - идеальный, помех не должно быть. И вот как тут можно было решить, что переполняется стек одной из задач? И как обнаружить проблемное место куда надо вставить этот диагностический код? Цитата(AlexandrY @ Aug 9 2016, 19:07)  В таких вещах просто вставляется диагностический код, но не тотально везде, а целевой, на поиск только определенной ошибки по известному диагнозу. Скажем засекаем метки времени в своем прерывании и делаем останов когда время затянулось дольше обычного. Это Вы про то что я писал про контроль длительности запретов прерываний? Ну так у меня это примерно так и работает, только не надо ставить такие метки в каждое прерывание, убирать их и пр. гемор. Просто разрешается некий #define, начинает работать ВЧ прерывание, выполняющееся на высшем приоритете, и оно измеряет временные интервалы между своими вызовами и если есть преывешние - ахтунг! - попадаем в ловушку. В любой момент когда возникло подозрение можно этто define включить и проверить. Это просто. Хочется чтобы и контроль стека включался так же просто.
|
|
|
|
|
Aug 10 2016, 06:57
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(jcxz @ Aug 10 2016, 09:37)  Каким образом при помощи watchpoint (или каких-то других сервисов) обнаружить факт выхода SP за границы стека текущей задачи (и записи по SP) в такой ситуации? При условии что в памяти расположенной ниже стека текущей задачи, могут находиться другие переменные, запись в которые через другие регистры (не SP) - штатна, и запись через SP из других задач - штатна, и не должна вызывать срабатывания механизма обнаружения переполнения стека. В качестве доп. условий: работа идёт в многозадачной среде, есть N задач, каждая со своим стеком. У меня было впечатление, что вы человек достаточно опытный в этих делах. Если не можете сами ответить на эти вопросы - что ж, у меня было неверное впечатление, должно быть.
|
|
|
|
|
Aug 10 2016, 09:22
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(jcxz @ Aug 10 2016, 09:37)  Мне кажется вопрос кристалльно ясен. Ситуация: void f() { char b[100]; b[0] = некое_выражение; ... } Ситуация совершенно не реалистичная. Разработчик под RTOS никогда так не сделает. И вы наверняка так не делаете, ибо эти грабли быстро все понимают. Здесь либо используется динамическое выделение, либо массив делается глобальным, либо разработчик сразу же тестирует этот код на переполнение стека. Причем далее в этой же процедуре стек может сразу вернуться к какой нибудь предыдущей локальной переменной, и ваш самопальный высокочастотный сепервизор может никогда и не увидеть куда стек успел залететь. Цитата И вот как тут можно было решить, что переполняется стек одной из задач? И как обнаружить проблемное место куда надо вставить этот диагностический код? А это делается в несколько ходов. Сначала вставляется лог приема-передачи пакетов. Потом по логу обнаруживается несоответствие поведения. Находится машина состояний ответственная за соответствующее поведение. Потом вставляются в ветви состояний команды типа ITM_EVENT(можно даже с аргументом в виде значение стека) и вставляется код с остановом с идентификацией аномального поведения. И в таймлайне после останова уже смотрят какие прерываний были, сколько длились и какие состояния автомата предшествовали. Если как в вашем случае обнаруживаем искаженные данные, то ставим на них watchpoint. И все! На следующем прогоне видим кто (какая задача, процедура, функция ..) портит данные. По такому алгоритму обнаруживаются все конфликты памяти, а не только возникшие из-за стека. У меня ошибок стека уж не помню сколько лет не было.
|
|
|
|
|
Aug 10 2016, 09:41
|

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

|
QUOTE (scifi @ Aug 10 2016, 09:57)  У меня было впечатление, что вы человек достаточно опытный в этих делах. Если не можете сами ответить на эти вопросы - что ж, у меня было неверное впечатление, должно быть. Ну Вы прямо, как в анекдоте с объявлением на столбе - "Могу обучать игре на баяне, английскому языку, использованию watchpoint, но не хочу". QUOTE (AlexandrY @ Aug 10 2016, 12:22)  Ситуация совершенно не реалистичная. Разработчик под RTOS никогда так не сделает. И вы наверняка так не делаете, ибо эти грабли быстро все понимают. Здесь либо используется динамическое выделение, либо массив делается глобальным... Соглашусь. Проектировать задачи так, что на стеке выделяются здоровенные куски памяти это системная ошибка  . Нежели задачи и функции изначально написаны более-менее ровненько по отношению к использованию стека, то не будет ни пиковых потреблений стека ни проюлем с их ловлей в неведомые моменты.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Aug 10 2016, 10:34
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(AlexandrY @ Aug 10 2016, 15:22)  Ситуация совершенно не реалистичная. Разработчик под RTOS никогда так не сделает. И вы наверняка так не делаете, ибо эти грабли быстро все понимают. Не понимаю... В чём её нереалистичность??? Под RTOS нельзя объявлять массивы на стеке? Цитата(AlexandrY @ Aug 10 2016, 15:22)  Причем далее в этой же процедуре стек может сразу вернуться к какой нибудь предыдущей локальной переменной, и ваш самопальный высокочастотный сепервизор может никогда и не увидеть куда стек успел залететь. Попробуйте иногда заглядывать в результирующий асм-код после компилятора. При выделении стекового фрейма на входе функцию, компилятор удаляет этот фрейм только при выходе из функции. По-крайней мере и IAR и CCS так генерят. И такой супервизор может пропустить это только если выход случится быстро после входа, что маловероятно (хотя вполне возможно - такой способ не даёт 100%-гарантии обнаружения). Цитата(AlexandrY @ Aug 10 2016, 15:22)  А это делается в несколько ходов. ... По такому алгоритму обнаруживаются все конфликты памяти, а не только возникшие из-за стека. Очень сложно и громоздко. Представьте что этот сбой происходил не каждый раз при тестировании, а только иногда редко, выявлялся установкой на прогон нескольких устройств и их кргулосуточной работой. Просто он проявлялся при попадании определённых запросов от клиента в определённые времена работы ПО, что происходило редко и недетерминированно по времени. Это придётся кучу логов перелопатить и watchpoint-ы не помогут - много устройств - неужто на каждый комп с JTAG-ом ставить?  Цитата(zltigo @ Aug 10 2016, 15:41)  Соглашусь. Проектировать задачи так, что на стеке выделяются здоровенные куски памяти это системная ошибка  . char b[100] - это только для примера. Если там будет char b[10] - легче что-ли? Или например - просто вызов sprintf() Цитата(scifi @ Aug 10 2016, 16:12)  Разве не очевидно? Настраивать watchpoint на область памяти некоторого разумного размера за стеком, куда полезут данные при переполнении. Настраивать в переключалке задач, чтобы контролировать актуальный стек. Стеки разместить с нужным выравниванием, чтобы всё это было возможным. Какой для этого J-Link нужен? Мой J-Link не умеет "Настраивать watchpoint на область памяти", а только на конкретный адрес. Ну можно конечно выровнять стек на границу степени 2 и настроить на адреса ниже воспользовавшись маской адреса. Но это тогда куча ОЗУ на это понадобится дополнительного. А её просто нет. У нас проект уже большой, там ОЗУ не хватит уже даже на массив из нескольких десятков байт, не то что выровнять все стеки всех задач (их около 10) да ещё выделить ниже каждого по приличному куску памяти. Если-б у меня было столько памяти, я бы просто задействовал MPU: при входе в задачу стек в отдельный регион с разрешением для него, для BSS - другой регион с защитой и т.п. И все регионы на расстоянии друг от друга. Я об этом писал ещё в самом начале топика. И во-2-ых - события переполнения могут происходить редко и приходится ставить несколько устройств на длительный прогон. И нет возможности сидеть над каждым устройством с JTAG-ом.
|
|
|
|
|
Aug 10 2016, 11:43
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(scifi @ Aug 10 2016, 17:10)  О господи. Эти самые watchpoint можно настраивать через регистры без всяких жтагов. Хорошо. Но как быть с ОЗУ? Цитата(AlexandrY @ Aug 10 2016, 17:42)  Не сочиняйте. При 10 задачах у вас должно быть море лишней памяти если вы не делали статический анализ стека. Не вижу моря. Вам видно виднее сколько памяти в моём проекте.
|
|
|
|
|
Aug 10 2016, 12:53
|

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

|
QUOTE (jcxz @ Aug 10 2016, 13:34)  char b[100] - это только для примера. Если там будет char b[10] - легче что-ли? Из личного стиля и опыта - много легче. Одно дело, когда те же задачи имеют типично имеют по несколько переменных да несколько вызовов. Понятно, что стек расходуется немного, пиковых нагрузок нет, ну и выделил с небольшим запасом, на этипе тестирования можно и побольше запас сделать и последить за расходом. Если стеки начнут потреблять память сотнями байт при вызове разных функций в произвольные моменты, то тогда уже начнет маячить нехватка памяти, желание урезать стеки по максимуму и начать бороться с возможными последстиями.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Aug 10 2016, 13:44
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(zltigo @ Aug 10 2016, 18:53)  Из личного стиля и опыта - много легче. Одно дело, когда те же задачи имеют типично имеют по несколько переменных да несколько вызовов. Понятно, что стек расходуется немного, пиковых нагрузок нет, ну и выделил с небольшим запасом, на этипе тестирования можно и побольше запас сделать и последить за расходом. Если стеки начнут потреблять память сотнями байт при вызове разных функций в произвольные моменты, то тогда уже начнет маячить нехватка памяти, желание урезать стеки по максимуму и начать бороться с возможными последстиями. Во-во! А у нас в некоторых задачах по нескольку КБ стеки уже. Не в моих задачах - я стараюсь экономить память. Но я не один участвую в проекте. Это хорошо как Вы пишете распланировать когда ты сам себе хозяин и только один пишешь ПО. Но как трудно объяснить людям почему нежелательно использовать sprintf() или что, если например внутри switch в одном case на стеке используется временная структура1, а в другом - временная структура2, по неск. десятков байт каждая, то очень желательно их объединить в union. Ну и т.п. И что ещё - хорошо, например сейчас определили, что в задаче1 вызывается такая-то функция, которая ест много стека. Хорошо - выделили этой задаче побольше. Но потом, когда уже подзабыли немного, что эта функция много ест при определённых условиях, и сделали её вызов из другой задачи2, имеющей мало стека. И писал эту функцию совсем другой человек. И отладили, проверили - всё ок, работает ок. А ведь эта функция много ест стека только при определённых редких условиях и на этапе отладки эта ветка выполнения кода не случилась, а вылезет оно потом много позже и неожиданно. Вот поэтому и очень желательно иметь такой механизм контроля стека, который можно просто включить, когда возникают хоть малейшие подозрения, когда прога ведёт себя как-то неадекватно и подозрительно. И чтобы не надо было лазить вручную проверяя каждый стек или ставя какие-то watchpoint-ы то на один стек, то на другой - трудоёмко это и трудно будет локализовать место проблемы.
|
|
|
|
|
Aug 11 2016, 01:58
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(scifi @ Aug 11 2016, 02:06)  Пока я вижу такой настрой: "Вы тут мне предлагайте всякое разное, а я придумаю 1000 и одну причину, почему мне это не подходит". Хозяин - барин, конечно, но выглядит это всё довольно комично. Вообще-то вопрос был конкретный: про вставку пролога в функции в IAR. Ни одного дельного предложения по этому вопросу не было. И причины я никакие не придумываю: причины все существуют, вот они передо мной, в проектах. Не понимаю - что именно комично? Объём кода большой (около 70 тыс. строк), памяти впритык (почти во всех проектах так), написателей кода - куча, задач - куча. И ПО ведёт себя как-то неадекватно. И что в этом случае делать-то прикажете? При каждой проблеме всё вручную пепрепроверять? Во всех режимах работы и функциях и их комбинациях коих тьма? И делать это после каждого внесения изменений в код каждым разработчиком? И что именно проверять? Как быстро определить где искать проблему??? Для такого тестирования у нас есть полигон, где стоит несколько десятков устройств и работают круглосуточно в реальных режимах работы. И нужно чтобы залить в них ПО и чтобы оно там крутилось долго и когда проявится проблема - вылетело на ловушку, сохранив состояние, по которому можно локализовать проблему. Вот и хочется для наиболее типичных вариантов проблем (в частности - переполнение стека) сделать какие-то методы быстрой локализации, чтобы в случае поиска проблемы, быстро отсечь эти варианты. Решение проблемы в тепличных условиях, когда весь код можно за неск. минут проглядеть и весь он свой и известен и куча свободных ресурсов, и проблема проявляется стабильно - мне неинтересна, ибо тут всё очевидно.
|
|
|
|
|
Aug 11 2016, 06:30
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(jcxz @ Aug 11 2016, 04:58)  И причины я никакие не придумываю: причины все существуют, вот они передо мной, в проектах... 70 тыс. строк это несложный проект. В такой размер помещается простейшая RTOS и с пару десятков модулей с логикой и аппаратной поддержкой. Такой проект должен делать один человек. Переполнение стека это не типичная ошибка. Косвенных вызовов в таком проекте будет не более десятка. У вас все решения лежат на виду. Проблема чисто организационная. Либо у вас проблема с выбором платформы и отладочных инструментов. Про "круглосуточно в реальных режимах работы" тоже можете не плакаться. У нас в Австралийской пустыне и на Тайване техподдержка через teamwiewer собирает логи и апгрейдит программу. В Саудовской Аравии специалист спокойно сидел на объекте и JTAG-ом ловил баги. Китайцы, так те самостоятельно отреверсили наши протоколы, присобачили беспроводные модули и сделали круглосуточное наблюдение через облака. Не далее как вчера мне пришла ссылка на хорошую статью про выбор платформы - http://www.edn.com/electronics-blogs/embed...ampaignId=29190Сдается мне у вас скверно выбрана платформа ( ну кроме того, что еще и скверная команда)
|
|
|
|
|
Aug 11 2016, 08:23
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(AlexandrY @ Aug 11 2016, 12:30)  70 тыс. строк это несложный проект. В такой размер помещается простейшая RTOS и с пару десятков модулей с логикой и аппаратной поддержкой. Такой проект должен делать один человек. Переполнение стека это не типичная ошибка. Косвенных вызовов в таком проекте будет не более десятка. У вас все решения лежат на виду. Ну да-да - рассказывайте сколько у меня косвенных вызовов - Вам конечно виднее. И какая разница - сколько их? Что от этого меняется? И про простейшую ОС - тоже. В каком-то ином мире Вы живёте видно. У нас ОС занимает менее 13тыс. строк. И это с кучей комментов и портом под ядро. А в 70тыс.строк я ОС не включал. И эти 70тыс. - это почти без комментов. И про объём это Вы похоже слабо владеете предметом. Вероятно у Вас ПО состоит из набора откуда-то стянутых чужих кусков. И для Вас писать ПО - слепить эти куски воедино. Так и 700тыс. - не проблема. У нас же в этих 70тыс.строках нет ни строки чужого кода. Цитата(AlexandrY @ Aug 11 2016, 12:30)  У нас в Австралийской пустыне и на Тайване техподдержка через teamwiewer собирает логи и апгрейдит программу. В Саудовской Аравии специалист спокойно сидел на объекте и JTAG-ом ловил баги. И что Вы этим хотели сказать??? У нас тоже основное ПО устройства обновляется удалённо через любой доступный канал связи по рабочему протоколу и без всяких тимвьюверов и безопасно. И даже с рестрансляцией через несколько устройств (если целевое устройство не поключено к глобальному каналу связи). И не только основное встроенное ПО можно обновить, но и встроенное ПО любого из входящих в состав устройства связных модулей (ZigBee, RF, GSM, PLC). И тоже безопасно. Только какое это имеет отношение к вопросу??? Почему не получится отловить JTAG-ом, я тут уже несколько раз объяснил. Если не поняли - извините. PS: Пустой трёп про китайцев в облаках поскипан.
|
|
|
|
|
Aug 12 2016, 03:35
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(AlexandrY @ Aug 11 2016, 15:15)  Видите ли, поскольку вы хотите обсуждать здесь коня в вакууме и при этом утверждаете, что у этого коня очень частые проблемы со стеком, то и получаете ответы по поводу таких же коней. Сейчас мы знаем кое-что больше о вашем коне. Я не обсуждаю здесь лошадей, не обсуждаю и проект. Это Вы всё время стараетесь стащить тему во флейм. Вопрос был конкретный - в заголовке. Или более расширенно - о методах контроля стеков задач в многозадачной среде в общем случае, а не в частных типа: имеем вагон+тележка памяти и можем каждой задаче по мегабайту на стек выделить. И чтобы это было удобно сделано - при малейшем подозрении на проблему со стеком: включил чекбокс/define, проконтролировал работу девайса в течение длительного времени на нескольких устройствах. И не надо было при каждом подозрении на стек, делать кучу ручной работы (ставить watchpoint-ы, снимать, просматривать стеки задач и т.п.). Я думаю это полезно будет не только в моих проектах. Недостатки проектов, в которых я участвую, более здесь обсуждать я не намерен. Я их сам прекрасно знаю и чьё-либо мнение здесь на этот счёт меня не интересует. Если нечем заняться и нечего сказать по существу - лучше пройдите мимо.
|
|
|
|
|
Aug 12 2016, 08:48
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Kabdim @ Aug 12 2016, 13:54)  Может попробовать использовать MPU? Расставить за концами стеков области с запретом на доступ... Я уже писал про такой вариант. Во-первых: нужно довольно много доп. памяти, а её в обрез; во-вторых: задач около 10 (даже больше), а в MPU регионов всего 8 и большую часть из них я уже использую. Цитата(Kabdim @ Aug 12 2016, 13:54)  Хотя кмк с такими запросами пора переходить на чип толще, с защитой адресного пространства задач. Ну или программистов строить - устроить кодревью репозитория с прошивкой. Это точно! Но не получается у меня так, проще уволиться самому...  На следующей работе обязательно для такой задачи буду требовать чип с MMU. Цитата(ViKo @ Aug 12 2016, 11:25)  В Кейловской РТОС можно включить контроль переполнения стека, одной галочкой в настройке. Если случится, вылетаем в os_error, и там можно определить, из какой задачи прилетели. Как это реализовано? Скорей всего - периодический контроль затёртости шаблона в стеке. Именно так сделано в uCOS. Это малоэффективно. Я уже писал почему. Цитата(AlexandrY @ Aug 12 2016, 14:08)  TC похоже этим еще не интересовался даже. Ну-ну продолжайте острить дальше. Память у Вас видно коротка (а не только в моём проекте), так хотя-бы почитайте весь тред.
|
|
|
|
|
Aug 12 2016, 09:59
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(jcxz @ Aug 12 2016, 12:45)  хм... странно... А как тогда работает? По какому алгоритму. У меня IAR, Keil-а нет. Мануал намекает, что проверка стека производится во время переключения задачи: Цитата Enabled Stack Checking slightly decreases the kernel performance because on every task switch the kernel needs to execute additional code for stack checking.
|
|
|
|
|
Aug 12 2016, 10:47
|

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

|
QUOTE (scifi @ Aug 12 2016, 12:59)  Мануал намекает, что проверка стека производится во время переключения задачи: Тогда это делается очень просто, но больше для галочки, ибо если задача переключается не в момент "преступления", то ей за это ничего не будет  . Контроль по маркерам много более инфрмативен, нежели такое.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Aug 12 2016, 13:36
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(jcxz @ Aug 12 2016, 11:48)  Ну-ну продолжайте острить дальше. Память у Вас видно коротка (а не только в моём проекте), так хотя-бы почитайте весь тред. Ладно, допустим вы даже читали про MPU. В STM32F4 и STM32F7 оно действительно есть, хоть там дальше все равно идут нюансы с картой памяти, атрибутами доступа её областей и кэшированием свойственные только STM-ам. А в Kinetis сделанных на том же Cortex-M4 ARM-овского MPU нет, а сделано свое MPU. Или вот обычная практика для задач выделять стек в динамической памяти (ну для динамических задач), и эта же задача должна иметь доступ ко всей динамической памяти. Т.е. задача не может взять и изолировать кусок в памяти поскольку должна иметь полное право писать и выше и ниже стека. И все! Красивая идея стухла и превратилась в костыль.
|
|
|
|
|
Aug 13 2016, 08:16
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(AlexandrY @ Aug 12 2016, 19:36)  А в Kinetis сделанных на том же Cortex-M4 ARM-овского MPU нет, а сделано свое MPU. Пока Kinetis и STM не интересуют, интересуют TI и NXP главным образом. Цитата(AlexandrY @ Aug 12 2016, 19:36)  Или вот обычная практика для задач выделять стек в динамической памяти (ну для динамических задач), и эта же задача должна иметь доступ ко всей динамической памяти. Т.е. задача не может взять и изолировать кусок в памяти поскольку должна иметь полное право писать и выше и ниже стека. В динамической памяти стеки задачам никогда не выделяю, все задачи живут постоянно - нет удаления задач. При избытке памяти и "изолировать" ничего не мешает - выделить кусок заведомо большего размера, внутри него - выровненный на степени 2 регион1, закрыть его от доступа через MPU, внутри региона1 - регион2 меньшего размера - разрешить его для доступа (с бОльшим приоритетом чем у региона1). Регион2 расположен внутри региона1 так, чтобы между началом региона1 и региона2 и между их концами были некоторые защитные интервалы памяти. Всё. Но я уже сказал - облегчённые частные случаи типа: избыток памяти, избыток свободных регионов в MPU и пр. - не рассматриваю. Цитата(zltigo @ Aug 12 2016, 16:47)  Тогда это делается очень просто, но больше для галочки, Это точно.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|