Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: скрипт + gcc
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > GNU/OpenSource средства разработки
antiwin
Народ, такая проблема. Необходимо при компиляции определить сколько раз вызывается функция (foo), а так же передать переменную из препроцессора(скрипта) в код программы.
1111493779.gif
msalov
Код
#!/bin/sh

COUNT=0

for FILE in *.c
do
    CURR=`cpp -E ${FILE} | grep -vv "^#" | grep "foo(" | grep -vv -c "void foo("`
    COUNT=`expr ${COUNT} + ${CURR}`
    echo -e "${FILE}\t:${CURR}\t:${COUNT}"
done

cat > foo_call_count.h << EOF
#ifndef _FOO_CALL_COUNT_H
#define _FOO_CALL_COUNT_H

#define foo_call_count ${COUNT}

#endif /*_FOO_CALL_COUNT_H*/
EOF

Ну а дальше, делаете foo_call_count.h зависимым от всех с-файлов и генерируете его при помощи этого скрипта.

UPD. Улучшенная версия с предварительной обработкой препроцессором (препроцессор убирает комментарии).
antiwin
Спасибо за совет, будем посмотреть
antiwin
Цитата(antiwin @ May 21 2009, 09:45) *
Спасибо за совет, будем посмотреть



Не совсем понятно как формируется выражение
CURR=`cpp -E ${FILE} | grep -vv "^#" | grep "foo(" | grep -vv -c "void foo("`
Как я понял, последние два оператора проверяют синтаксис на наличие foo, но вот что делают первые два? smile3046.gif
antiwin
Цитата(antiwin @ May 21 2009, 12:23) *
Не совсем понятно как формируется выражение
CURR=`cpp -E ${FILE} | grep -vv "^#" | grep "foo(" | grep -vv -c "void foo("`
Как я понял, последние два оператора проверяют синтаксис на наличие foo, но не понятно что значит grep -vv , такое сочетание параметров я не нашел нигде. smile3046.gif
msalov
Первый обрабатывает препроцессором файл, второй отбрасывает служебную инфу препроцессора (может не надо), 3 - выделяет все упоминания функции, 4 - исключает прототипы функции и подсчитывает оставшиеся.
Строчку "echo -e "${FILE}\t:${CURR}\t:${COUNT}"" можно удалить (исключительно для отладочных целей), ещё желательно натравливать препроцессор на файл с полным комплектом ключей компиляции.
antiwin
Спасибо, прояснилось. Как я понимаю этот скрипт надо вписать в makefile?. И еще, давно хотел спросить, запись типа #define _FOO_CALL_COUNT_H определяет файл FOO_CALL_COUNT.H, но как препроцессор знает что перед H надо ставить точку. Почему просто не написать #include <FOO_CALL_COUNT.H>

yeah.gif
antiwin
p.s отвечаю не сразу. т.к дома нет инета.
Сергей Борщ
Цитата(antiwin @ May 25 2009, 09:02) *
запись типа #define _FOO_CALL_COUNT_H определяет файл FOO_CALL_COUNT.H, но как препроцессор знает что перед H надо ставить точку.
Препроцессор ничего не знает и не ставит. Определяется символ _FOO_CALL_COUNT_H и дальше идет проверка именно этого символа. Это программист сам для себя решает, что _FOO_CALL_COUNT_H относится к файлу foo_call_count.h. Кстати, имена, начинающиеся с подчеркивания зарезервированы за компилятором и стандартной библиотекой, во избежание неприятностей свои имена с подчеркивания лучше не начинать.
msalov
Цитата(antiwin @ May 25 2009, 09:02) *
Спасибо, прояснилось. Как я понимаю этот скрипт надо вписать в makefile?. И еще, давно хотел спросить, запись типа #define _FOO_CALL_COUNT_H определяет файл FOO_CALL_COUNT.H, но как препроцессор знает что перед H надо ставить точку. Почему просто не написать #include <FOO_CALL_COUNT.H>

yeah.gif

Скрипт лучше поместить в отдельный sh-файл и запускать при необходимости (из мейкфайла) (не уверен но может и из makefile-а работать).
Разберем конструкцию по строчкам:
1. #ifndef _FOO_CALL_COUNT_H
проверяем не объявлен ли _FOO_CALL_COUNT_H, если не объявлен - п.2. Если обьявлен - значит этот заголовочный файл включен не в первый раз и значит его содержимое можно не обрабатывать.
2. #define _FOO_CALL_COUNT_H
Это является стандартным обрамлением для заголовочных файлов.
Цитата(Сергей Борщ @ May 25 2009, 10:43) *
Кстати, имена, начинающиеся с подчеркивания зарезервированы за компилятором и стандартной библиотекой, во избежание неприятностей свои имена с подчеркивания лучше не начинать.
Спасибо за замечание, учту.
antiwin
Цитата(Сергей Борщ @ May 25 2009, 11:43) *
Определяется символ _FOO_CALL_COUNT_H и дальше идет проверка именно этого символа. Это программист сам для себя решает, что _FOO_CALL_COUNT_H относится к файлу foo_call_count.h.


То есть, компилятор у себя в "уме" держит некую таблицу в которой, допустим, строке _FOO_CALL_COUNT_H соответствует include <FOO_CALL_COUNT.H>?

Цитата(Сергей Борщ @ May 25 2009, 11:43) *
Скрипт лучше поместить в отдельный sh-файл и запускать при необходимости (из мейкфайла).
Разберем конструкцию по строчкам:
1. #ifndef _FOO_CALL_COUNT_H
проверяем не объявлен ли _FOO_CALL_COUNT_H.

Т.е запуск скрипта с помощью команды sh (скрипт)?
Хотел бы уточнить выражение (объявлен ли _FOO_CALL_COUNT_H), если следовать синтаксису С, то это запись (_FOO_CALL_COUNT_H FOO_CALL_COUNT.H ) или нет? Мне хотелось бы понять сам механизм.

smile.gif
Сергей Борщ
Цитата(antiwin @ May 25 2009, 14:11) *
То есть, компилятор у себя в "уме" держит некую таблицу в которой, допустим, строке _FOO_CALL_COUNT_H соответствует include <FOO_CALL_COUNT.H>?
Нет никакой таблицы. Нет никакого соответствия. вот смотрите:
Код
foo.h:
#ifndef  FOO_H_INCLUDED__
#define FOO_H_INCLUDED__
.....
#endif  //FOO_H_INCLUDED__

abc.c:
#include "foo.h"
....
#include "foo.h"
Что происходит при компиляции abc.c?
1)удаляются все комментарии.
2)весь текст foo.h вставляется в те места, где был #include "foo.h". Все, #include исчезли, на их месте появился текст.
3) Теперь препроцессор проходит весь файл от начала до конца. В месте, где файл foo.h вставлен первый раз, он встречает строку #ifndef FOO_H_INCLUDED__.
4)Он ищет в своих списках символ FOO_H_INCLUDED__ и не находит его. Символ не определен. Условие выполнилось, препроцессор просматривает текст внутри условия.
5) тут он встречает строку #define FOO_H_INCLUDED__. Он вносит такой символ в свои списки - все, символ определен.
6) он просматривает файл дальше и доходит до места, где был второй #include "foo.h". Тут он снова встречает строку #ifndef FOO_H_INCLUDED__.
7) Он снова смотрит в своих списках и теперь уже находит там символ FOO_H_INCLUDED__. Условие не выполнилось, препроцессор выкидывает весь текст до #endif .

Все, содержимое foo.h попало компилятору только один раз. Как мы обозвали этот символ - FOO_H_INCLUDED__ или FOO_H_ или SKIP_THIS_FILE или TIPA_TEST - препроцессору все равно. Это наша забота, чтобы в каждом заголовочном файле был определен уникальный символ. И проще всего это сделать, связав имя символа с именем файла.

Чтобы понять смысл выражения "определен символ" - лучше всего прочитать документацию на препроцессор (не компилятор, а именно препроцессор - до компилятора эти символы не доходят).
antiwin
Цитата(Сергей Борщ @ May 25 2009, 18:33) *
Это наша забота, чтобы в каждом заголовочном файле был определен уникальный символ...

Я правильно понал, об'явление уникального символа предохраняет от повторной компиляции?
А вроде бы у препроцессора есть функции на этот счет?

Цитата(Сергей Борщ @ May 25 2009, 18:33) *
Он ищет в своих списках символ FOO_H_INCLUDED__ ...

Значит, что то вроде таблицы все та ки есть...
За ссылку спасибо, как почитаю отпишусь
немного отвлеченный вопрос.
AT (ADDR (.text) + SIZEOF (.text))
SIZEOF вроде понятно-это размер секции, тогда ADDR-это начало? и что это за оператор AT?.
И еще, в WinAvr есть папка с man, как вот эти man прочесть? Я думаю, что масса вопросов сразу бы отпала.
Сергей Борщ
Цитата(antiwin @ May 26 2009, 09:27) *
Я правильно понал, об'явление уникального символа предохраняет от повторной компиляции?
А вроде бы у препроцессора есть функции на этот счет?
Есть, но они не стандартизованы (насколько я помню) и обладают рядом недостатков. Вот на этом форуме второй ответ описывает их. А обертка из #ifndef #define #endif работает везде. Вот неплохое описание
Цитата(antiwin @ May 26 2009, 09:27) *
Значит, что то вроде таблицы все та ки есть...
Как это устроено внутри - не знаю, да это и не принципиально. Важно, что никаких таблиц соответствия имени символа и имени файла нет.
Цитата(antiwin @ May 26 2009, 09:27) *
немного отвлеченный вопрос.
AT (ADDR (.text) + SIZEOF (.text))
Дежа-вю
antiwin
Привет всем, такой вот проблем: как можно узнать адрес в ОЗУ выше которого стек не "перерастет"? Это нужно сделать на этапе компиляции.
И еще, можно ли в теле функции узнать тип возвращаемого значения т.е допустим определена функция void *foo(...), далее где то вызывается int *ptr=foo(...), как в теле foo узнать что это указатель на int?
alx2
Цитата(antiwin @ Jun 29 2009, 12:49) *
Привет всем, такой вот проблем: как можно узнать адрес в ОЗУ выше которого стек не "перерастет"? Это нужно сделать на этапе компиляции.
В общем случае это невозможно.
Цитата(antiwin @ Jun 29 2009, 12:49) *
И еще, можно ли в теле функции узнать тип возвращаемого значения т.е допустим определена функция void *foo(...), далее где то вызывается int *ptr=foo(...), как в теле foo узнать что это указатель на int?
Что "это"? smile.gif foo выполняется до присваивания, поэтому во время ее выполнения ptr еще не существует. Функция "знает" только то, что передается ей в качестве аргументов. Что потом произойдет с возвращаемым ей значением, она знать не может.
А позвольте полюбопытствовать, для чего нужно "знать" внутри функции, какому объекту будет потом присваиваться возвращаемое ей значение? Такое желание мне кажется довольно странным, и наводит на мысль, что на самом деле задача состоит в другом - обеспечении более строгой типизации. На C++ это решается множеством различных способов - например, вместо типа void* функция может возвращать экземпляр класса, содержащего методы преобразования указателя к нужному типу - но это никак не будет влиять на код функции. Или инстанцированием функции из шаблона - тут код функции будет сразу генериться с учетом нужного типа, но тип придется при каждом вызове указывать явно...
Troll
Цитата(antiwin @ Jun 29 2009, 13:49) *
И еще, можно ли в теле функции узнать тип возвращаемого значения т.е допустим определена функция void *foo(...), далее где то вызывается int *ptr=foo(...), как в теле foo узнать что это указатель на int?

А для чего это надо?
И если уж работаете с указателями, то почему бы просто не выполнить явное преобразование типа
Код
int *ptr = (int*) foo();
antiwin
Цитата(alx2 @ Jul 3 2009, 23:08) *
В общем случае это невозможно.

Да в общем случае ненадо, можно ли найти данные(в файлах lst, map...) или где еще позволяющие расчитать вершину стека.?

Цитата(alx2 @ Jul 3 2009, 23:08) *
А позвольте полюбопытствовать, для чего нужно "знать" внутри функции, какому объекту будет потом присваиваться возвращаемое ей значение?


Вобщем все просто, хочется написать альтернативу malloc ибо представляется она мне очень "глупой", ну не видит она где heap, а где stack. Кроме простого выделения памяти хочеться организовать минимальный менеджер памяти, способный дефрагментировать память. Конечно, об универсальном проекте речь не идет, но хотябы для "mega" с ОЗУ>500 байт. smile.gif

Цитата(Troll @ Jul 8 2009, 09:48) *
А для чего это надо?
И если уж работаете с указателями, то почему бы просто не выполнить явное преобразование типа
Код
int *ptr = (int*) foo();


В том и проблема, так как можно char* foo(), и int* foo(), и uin32_t* foo(), т.е заранее неизвестно какой тип указателя нужен.
msalov
 
Цитата(antiwin @ Jul 14 2009, 14:08) *
В том и проблема, так как можно char* foo(), и int* foo(), и uin32_t* foo(), т.е заранее неизвестно какой тип указателя нужен.


Так делайте явное преобразование типа по месту, как это сделано в malloc

Код
void * my_malloc(size_t length);

char *a = (char*)my_malloc(2);

int *b = (int*)my_malloc(2);

long *c = (long*)my_malloc(2);
Сергей Борщ
Цитата(antiwin @ Jul 14 2009, 14:08) *
менеджер памяти, способный дефрагментировать память.
Интересно, как вы собираетесь это реализовать? Выделил ваш менеджер участок с адреса, скажем, 110. Вернул указатель. Вы его запомнили. Потом он в процессе дефрагментации переместил этот участок на адрес, скажем, 100. Как он сообщит вашей программе, что старый указатель больше не действителен? А если вы его уже успели скопировать в три других указателя?
AHTOXA
smile.gif Наверное речь идёт о дефрагментации свободных участков. По крайней мере, я не слышал о менеджерах памяти, дефрагментирующих выделенные блоки.
msalov
Цитата(AHTOXA @ Jul 14 2009, 16:05) *
smile.gif Наверное речь идёт о дефрагментации свободных участков. По крайней мере, я не слышал о менеджерах памяти, дефрагментирующих выделенные блоки.

Ну, если память состоит только их занятых и свободных блоков, то дефрагментация одних эквивалентна дефрагментации других. Или там ещё бывают участки в "третьем" состоянии? Просветите, я не в курсе sad.gif
Troll
Цитата(gotty @ Jul 15 2009, 13:22) *
Ну, если память состоит только их занятых и свободных блоков, то дефрагментация одних эквивалентна дефрагментации других.
НЕТ. maniac.gif
Для хранения всех локальных переменных (переменных объявленных внутри функций) используется стек. Для дефрагментации занятых блоков все указатели (абсолютно все) должны быть видны вашему менеджеру памяти. См. пост №20.

Дефрагментация свободных блоков - это уже другая задача. На свободные участки указатели уже не хранят значения, а если такое и случается это уже ошибка программиста.
Менеджер памяти хранит списки свободных и занятых блоков и после некоторого количества операций по выделению/освобождению памяти получается, что два и более последовательных блока свободны и их можно объединить в один большой свободный блок. Это и есть дефрагментация свободной памяти.

Цитата
ну не видит она где heap, а где stack
Размер стека определяется программистом и известен на этапе компиляции программы. Почему бы не написать функцию для проверки свободного места, которая будет знать размер стека? и выполняйте ее перед каждым вызовом malloc.
antiwin
Цитата(Сергей Борщ @ Jul 14 2009, 15:41) *
Интересно, как вы собираетесь это реализовать?

Просто, в ячейку указателя записывается новый адрес и все. Адрес самого указателя то не меняется.
Что бы видеть все указатели, в менеджере предусмотрена специальная структура в которой каждому указателю(его адресу) соответствует адрес блока данных. Эта вынужденая мера, так как при работе с указателем его значение меняется, а в этой структуре он постоянен. Когда же приходит время удалить блок начало этого бока берется в структуре.


Цитата(Сергей Борщ @ Jul 14 2009, 15:41) *
А если вы его уже успели скопировать в три других указателя?

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

Цитата(gotty @ Jul 14 2009, 15:30) *
Так делайте явное преобразование типа по месту, как это сделано в malloc

К стати, пытался найти описание malloc и других встроенных функций, но кроме определения не нашел.
Где же они все таки описанны.?
aesok
Цитата(antiwin @ Jul 16 2009, 12:42) *
Просто, в ячейку указателя записывается новый адрес и все. Адрес самого указателя то не меняется.
Что бы видеть все указатели, в менеджере предусмотрена специальная структура в которой каждому указателю(его адресу) соответствует адрес блока данных.


А какой размер этого масива? Три? Десять? Сто? А.... Вы озадачите нас вопросом: Как во время компиляции узнать количество используемых указателей в работуещей программе?!

Цитата(antiwin @ Jul 16 2009, 12:42) *
Ну тут ничего не сделаешь, как и в любом компиляторе рано или поздно приходиться самому контролировать.
К тому же не вижу практического смысла таких действий, потенциальный источник ошибок.

Вы никогда не слашали про такую вещь как Двусвязный список?

Цитата(antiwin @ Jul 16 2009, 12:42) *
К стати, пытался найти описание malloc и других встроенных функций, но кроме определения не нашел.
Где же они все таки описанны.?


Не сочтите за рекламу: www.google.com

Анатолий.
Сергей Борщ
Цитата(antiwin @ Jul 16 2009, 11:42) *
К тому же не вижу практического смысла таких действий, потенциальный источник ошибок.
Простите, а как же тогда передавать этот массив в функции? Параметр функции передается по значению, т.е. создается временная переменная, в которую копируется значение параметра, в нашем случае - указателя. Да и просто ходить по массиву удобнее двигая указатель, а не индексируясь каждый раз отностительно указателя на начало. Опять же, компилятор имеет право втайне от вас завести временный указатель для таких целей.
antiwin
Цитата(aesok @ Jul 16 2009, 13:34) *
А какой размер этого масива? Три? Десять? Сто? А.... Вы озадачите нас вопросом:

Кол-во указателей (размер массива) определяется на этапе компиляции, путем подсчета вызовов определенной функции, отвечающей за выделение памяти.

Цитата(Сергей Борщ @ Jul 16 2009, 13:54) *
Простите, а как же тогда передавать этот массив в функции?

А почему бы это не делать в функции? Ведь так гораздо проще контролировать для чего выделяется память.
Но даже если придется, то просто в дело вступает еще один указатель указываеющий на "исходный" а тот-на массив (его начало).
Поправте, если ошибаюсь.

Цитата(aesok @ Jul 16 2009, 13:34) *
Не сочтите за рекламу: www.google.com

Гуглился битый час, может не тот гугль, но так или иначе отсылает к avr-libc. А там ничего.
Дайте нужную ссылку пожалуйста.
aesok
Цитата(antiwin @ Jul 16 2009, 15:58) *
Кол-во указателей (размер массива) определяется на этапе компиляции, путем подсчета вызовов определенной функции, отвечающей за выделение памяти.


объясните мне: как во время компиляции можно узнать сколько раз будет вызвана функция во время работы программы?

Цитата(antiwin @ Jul 16 2009, 15:58) *
Гуглился битый час, может не тот гугль, но так или иначе отсылает к avr-libc. А там ничего.
Дайте нужную ссылку пожалуйста.


http://ru.wikipedia.org/wiki/Malloc
http://www.nongnu.org/avr-libc/user-manual...678e5251dfd3ebd
http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1124.pdf - начиная со стриницы 313

Анатолий.
antiwin
Цитата(aesok @ Jul 16 2009, 17:19) *
объясните мне:

Не во время компиляции, а как бы перед ней, т.е скриптом (или иначе) анализируется каждый файл (*.с) проекта
где и подсчитывается сколько раз вызывается функция. Потом это число записывается в файл и далее идет сама компиляция. Просто весь процесс я обозвал компиляцией.



Цитата(aesok @ Jul 16 2009, 17:19) *

Под описанием я имел ввиду не описание в человеческом смысле, а в рамках С99, т.е тело функции. А в предложенных ссылках (за них спасибо) описанние именно в первом смысле.


Цитата(Troll @ Jul 15 2009, 14:28) *
Размер стека определяется программистом...

Смысл задуманного как раз в том чтобы освободить программиста от такого "счастья".
smac
Цитата(antiwin @ Jul 17 2009, 13:05) *
Не во время компиляции, а как бы перед ней, т.е скриптом (или иначе) анализируется каждый файл (*.с) проекта
где и подсчитывается сколько раз вызывается функция. Потом это число записывается в файл и далее идет сама компиляция. Просто весь процесс я обозвал компиляцией.

Если ф-я вызывается в цикле, то Ваш анализ будет условия циклов проверять и т. д.? Если условие цикла зависит от железа (перефирийных блоков)? Эмулировать все будете?
У меня конечно нет точных сведений, но по-моему, такого хорошего эмулятора (кроме реального железа конечно) не существует.
Troll
Цитата
Смысл задуманного как раз в том чтобы освободить программиста от такого "счастья".

От какого счастья Вы собрались освободить программиста таким способом?
antiwin
Цитата(smac @ Jul 17 2009, 14:19) *
Если ф-я вызывается в цикле, если условие цикла зависит от железа ?

Условия проверяться не будут, т.к кол-во комбинаций=n! очень велико уже при n=10, где n-кол-во условных операторов. Просто будет собираться статистика вызовов каждой функции, анализироваться есть ли вложенные функции.? По поводу циклов не понял, уточните, а лучше пример в коде. На счет железа, это если что то из порта приходит? В любом случае условия не проверяются см. выше.



Цитата(Troll @ Jul 17 2009, 14:48) *
От какого счастья Вы собрались освободить программиста таким способом?

Освободить от "счастья" самому руками задавать стек путем написания скрипта, результаты работы которого передавать в проект до компиляции.
P.S на связь выду только в Пндк.
alx2
Прошу прощения за молчание - я в отпуске, сюда заглядываю редко...
Цитата(antiwin @ Jul 14 2009, 16:08) *
Да в общем случае ненадо, можно ли найти данные(в файлах lst, map...) или где еще позволяющие расчитать вершину стека.?
Можно смотреть сгенерированный компилятором ассемблерный текст, находить все манипуляции со стеком и считать. А потом пересчитывать при каждой модификации исходника... Муторно очень. Я такой "подвиг" совершаю только для коротеньких фрагментов (типа обработчика прерывания), изначально целиком написанных на ассемблере...
Есть,например, такой метод: изначально весь стек заполняется какой-то константой, затем процесс запускается на выполнение (в симуляторе или живом устройстве), причем по возможности стараются прогнать выполнение по всем возможным веткам программы. После прогона смотрят, какая часть области стека осталась заполнена константой, а какая была использована (переписана). По результатам такого измерения корректируется размер выделенной под стек области памяти (с некоторым запасом).
Цитата(antiwin @ Jul 16 2009, 13:42) *
К стати, пытался найти описание malloc и других встроенных функций, но кроме определения не нашел. Где же они все таки описанны.?

Цитата(antiwin @ Jul 17 2009, 14:05) *
Под описанием я имел ввиду не описание в человеческом смысле, а в рамках С99, т.е тело функции. А в предложенных ссылках (за них спасибо) описанние именно в первом смысле.
В терминах C99 есть понятия "объявление" (declaration), для функций также называемое прототипом, и "определение" (definition), где содержится собственно код тела функции. Прототип malloc находится в <stdlib.h>, а где находится ее определение, зависит от используемой Вами библиотеки.
Troll
Цитата(antiwin @ Jul 17 2009, 17:59) *
Освободить от "счастья" самому руками задавать стек путем написания скрипта, результаты работы которого передавать в проект до компиляции.

Так для этого надо считать не указатели, а операции со стеком.

постом выше, рассказали как определить размер стека и почему этого не надо делать при каждой компиляции. А на этапе отладки всегда можно посмотреть вершину стека и определить похерил ее кто-нибудь или нет.
smac
Цитата(antiwin @ Jul 17 2009, 14:59) *
Условия проверяться не будут, т.к кол-во комбинаций=n! очень велико уже при n=10, где n-кол-во условных операторов. Просто будет собираться статистика вызовов каждой функции, анализироваться есть ли вложенные функции.? По поводу циклов не понял, уточните, а лучше пример в коде. На счет железа, это если что то из порта приходит? В любом случае условия не проверяются см. выше.

Пример конечно гипотетический, но
Код
int i;
       char *p;
       for (i=0; i<MAX; i++) //запускаем цикл по i (ну допустим количество опросов датчика или т. п.)
            if (SOME_PIN_IS_LOW&&(i&0x01)) // если  нужная нога - 0  и номер опроса нечетный, то просим памяти
                p= (char*)malloc(SOME_MEMORY);

С одной стороны, если функция вызывается и возвращается обратно, то стек она освободит (допустим мы заведомо знаем сколько стека она отожрет), но в данном примере число вызовов malloc() и соответсвенно количество требуемой памяти будет определяться временем сохранения истинных условий, естественно количество вызовов может варьировться от 0 до MAX/2 раз.
Определить хватит ли памяти можно только прогоном программы иначе никак. Сомневаюсь что Ваш анализатор справится с задачей "просто заглянув исходник". Конечно данный пример не показывает как можно "перелезть" через вершину стека, однако я думаю что он показателен в смысле того что нельзя заранее определить сколько памяти требуется.
Можно также упомянуть вложенные прерывания (хотя перед использованием этого приема нужно крепко подумать), оценить частоту и количество вызовов, а также уровень вложенности, которых по C исходнику с помощью машины тоже вряд-ли реально.
Наконец стоит вспомнить о рекурсивных функциях - какая глубина рекурсии потребуется можно сказать только при полностью известных исходных данных, т. е. в том случае когда собственно программа обрабатывает заранее известную информацию, и результат ее (программы) работы в принципе тоже известен.
Например
Код
void some_recursive_func(int i, long int a, long int b); // некая рекурсивная функция
       char read_some_port(void); // функция чтения порта, возвращающая практически случайное число от 0 до 256
       int main (void){
       int i;
       long int a;
       i = (int)read_some_port(); // читаем в i значение порта
       some_recursive_func(i, a, a); // вызываем нашу рекурсивную функцию
       return;
       }
       void some_recursive_func(int i, long int a, long int b){ // собственно функция
          if (i>0){ // если i положительная, то уменьшаем ее
            --i;
            b=a++; // делаем что-то
            some_recursive_func (i, a, b); // вызываем функцию . Вопрос лишь в том соклько раз она вызовется (и соответсвенно сколько стека отъест)?
          }
       }

Конечно приведенные примеры сильно утрированы, но они простые, а в реальности могут быть на порядки сложнее, поэтому в общем случае мое мнение таково - ни один анализатор не сможет оценить реальные потребности в памяти по C исходникам при сколько-либо сложной задаче (т. е. в том случае где это нужно).
antiwin
Цитата(alx2 @ Jul 17 2009, 16:52) *
А потом пересчитывать при каждой модификации исходника...

То есть просто в рукопашную, один на один с исходником?


Цитата(alx2 @ Jul 17 2009, 16:52) *
изначально весь стек заполняется какой-то константой, затем процесс запускается на выполнение (в симуляторе или живом устройстве), причем по возможности стараются прогнать выполнение по всем возможным веткам программы.

Вот именно этот процесс (прогон по всем веткам) я и хочу автоматизировать, и уже есть соображения как. В любом случае это бутет консоль (на Visual Studio 6.0) вызываемая из makefile которой будет передоваться в командной стролке имя и путь к файлу *.lss. Попутный вопрос, как в makefile считать код возврата компилятора дабы не вызывать лишний раз консоль, да и файла тогда еще нет? smile3046.gif

Цитата(Troll @ Jul 17 2009, 17:58) *
Так для этого надо считать не указатели, а операции со стеком.

Конечно, подсчет указателей нужен для определеня размера структуры в менеджере памяти. Это делается еще до компиляции, а для стека нужно учитывать след. команды: push, st,std,call... в *.lss. Можно правда избавиться от лишней компиляции (надо же получить *.lss) если анализировать файлы *.c & *.h, но огда придеться считаться с гораздо сложным синтаксисом.
Troll
Цитата(antiwin @ Jul 21 2009, 17:05) *
Вот именно этот процесс (прогон по всем веткам) я и хочу автоматизировать, и уже есть соображения как.
Можно, конечно, построить дерево вызова процедур. Но возникают вопросы:
- как будете учитывать рекурсивные вызовы?
- вызовы прерываний, особенно если у них разные приоритеты?
alx2
Цитата(antiwin @ Jul 21 2009, 15:05) *
Попутный вопрос, как в makefile считать код возврата компилятора
Что значит "считать"? make и так проверяет код возврата каждой выполняемой команды на равенство нулю дабы прекратить выполнение в случае ошибки. Если в зависимости от кода возврата компилятора требуется выполнить какое-то ветвление, делайте это срадствами шелла, а не make'а.
antiwin
Цитата(alx2 @ Jul 21 2009, 23:52) *
Если в зависимости от кода возврата компилятора требуется выполнить какое-то ветвление, делайте это срадствами шелла, а не make'а.

Да, именно это. Просто я в шелле пока не очень, не могли бы прислать примерчик?
alx2
Цитата(antiwin @ Jul 22 2009, 13:29) *
Да, именно это. Просто я в шелле пока не очень, не могли бы прислать примерчик?
Например вот так:
Код
gcc -c test.c && echo "Ура!!! Нет ошибок!" || echo "Ой, ошибка при компиляции!"
antiwin
Цитата(smac @ Jul 17 2009, 19:33) *
Сомневаюсь что Ваш анализатор справится с задачей "просто заглянув исходник".

Конечно, приведенные примеры поставят в тупик анализатор, но если он истпользует уже скомпилированный файл
т.е асмовый текст. При использовании исходников подобные проблемы я думаю решаемы. Конечно, и в этом случае алгоритм анализа получится довольно сложным, а в более общих случаях достаточно приблизительным, помимо этого придется анализировать все include и определенные даные . По большому счету это уже компилятор (препроцессор). Но все же... От использования исходных файлов останавливает то, что не у всех функций можно получить описание, таких как: malloc, sin random ( т.е встроенных-библиотечных)....Я эту тему чуть выше поднимал, но честно пока так и не разобрался где лежит их описание.
P.S Буду очень благодарен за внесение ястности в этот вопрос.
aesok
Цитата(antiwin @ Jul 23 2009, 12:31) *
.....что не у всех функций можно получить описание, таких как: malloc, sin random ( т.е встроенных-библиотечных)....Я эту тему чуть выше поднимал, но честно пока так и не разобрался где лежит их описание.
P.S Буду очень благодарен за внесение ястности в этот вопрос.


http://ftp.twaren.net/Unix/NonGNU/avr-libc/

PS: Учитесь пользоваться гуглем, это очень пригодиться Вам в жизни.

Анатолий.
smac
Цитата(antiwin @ Jul 23 2009, 12:31) *
Конечно, приведенные примеры поставят в тупик анализатор, но если он истпользует уже скомпилированный файл
т.е асмовый текст. При использовании исходников подобные проблемы я думаю решаемы. ...

Не вижу разници между асмовым текстом и сишным, ибо количество вызовов ни по одному ни по другому в тех примерах не определить, дальше обсуждать это бессмысленно.
Просто мне кажется, что написание того анализатора что Вы задумываете - гораздо более сложная задача, чем написание хорошего программного эмулятора (симулятора). Если решать ее в общем случае (например для семейства процессоров), то ресурсов (имеются ввиду "человеко-часы") уйдет очень много. С другой стороны решение этой задачи в частном случае - оценка потребляемого конкретным кодом стека, тоже потребует приличного времени, при том что в частном случае она может быть решена предлагаемыми в теме способами с меньшими потерями "человеко-часов".
antiwin
Цитата(smac @ Jul 23 2009, 14:55) *
Не вижу разници между асмовым текстом и сишным, ибо количество вызовов ни по одному ни по другому в тех примерах не определить...

Разница огромна, работать с асмовым текстом все равно что глядя на кучу кирпичей гадать что это было, в случае с исходниками ситуация более ясная. Так как "глядя" на синтаксис можно очень многое узнать о текущей операции и даже если нет конкретных данных (приходят из порта), все равно можно хотя бы узнать минимальное и максимальное значение переменной, анализируя формат данных (char, int, long int...) и тип арифметической или логической операции.
aesok
Цитата(antiwin @ Jul 24 2009, 12:46) *
Разница огромна, работать с асмовым текстом все равно что глядя на кучу кирпичей гадать что это было, в случае с исходниками ситуация более ясная.


Анализируя С код вы ни когда не узнаете размер frame-буфура и сколько call-used регистров будут сохранено в стеке. Точнее узеаете , но только в одном случае когда Ваш скрипт будет работать точно также как компилятор.

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

Анатоллий.
aesok
call-used call-saved
antiwin
Цитата(aesok @ Jul 24 2009, 13:26) *
Анализируя С код вы ни когда не узнаете размер frame-буфура и сколько call-used регистров будут сохранено в стеке.
Анатоллий.

На сколько я понимаю, frame-буфер это и есть стек для функции где хранятся все переменные и прочие данные.
Почему же в таком случае анализируя С код нельзя определить эту величину(может стек формируется иначе чем я думаю)? Конечно такие сложности как рекурсия и не определенные данные я пока опускаю, хотя и в этом случае ,как мне кажется, есть решение.

Цитата(aesok @ Jul 23 2009, 14:16) *

Как я понял все эти библиотеки устанавливаются nix-овым образом, как же их под маздай(WinAvr) установить?
За ранее извиняюсь за возможно детский вопрос, просто всегда довольствовался тем, что качал с WinAvr.
cool.gif
Сергей Борщ
Цитата(antiwin @ Jul 27 2009, 11:52) *
Почему же в таком случае анализируя С код нельзя определить эту величину
Потому что в этом случае вам придется написать программу, которая работает по тем же самым алгоритмам, что и компилятор. И даже если у вас это получится, вам придется ее править с выходом каждой следующей версии компилятора. Ибо компилятор может для разных переменных (использующихся в разное время) зарезервировать одну и ту же ячейку памяти.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.