spongebob 0 23 апреля, 2012 Опубликовано 23 апреля, 2012 · Жалоба Всем привет! Подскажите, пожалуйста, каков механизм вызова функций в Си++? Имеет ряд вложенных функций (работа с массивами), хочется посчитать какой максимальный объем памяти в стеке они потребуют (передача параметров + сами данные в функциях). Можно как-нибудь "автоматизировать" процесс подсчета? Видимо, читать значение указателя стека в определенные моменты времени? Все ли локальные переменные, объявляемые в функциях, создаются в стеке? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
MrYuran 23 23 апреля, 2012 Опубликовано 23 апреля, 2012 · Жалоба http://stackoverflow.com/questions/6387614...system-with-gcc http://gcc.gnu.org/onlinedocs/gnat_ugn_unw...e-Analysis.html Как-то так Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
spongebob 0 23 апреля, 2012 Опубликовано 23 апреля, 2012 (изменено) · Жалоба http://gcc.gnu.org/onlinedocs/gnat_ugn_unw...e-Analysis.html Вот спасибо, попробуем :) Это ключ компилятора или линкера? ... The qualifier static means that the function frame size is purely static. It usually means that all local variables have a static size. In this case, the second field is a reliable measure of the function stack utilization. ... The qualifier dynamic means that the function frame size is not static. It happens mainly when some local variables have a dynamic size. ... Что значит "все локальные переменные имеют статический размер"? Объявлены как "static" внутри функции или это просто не динамически выделяемая память (из кучи)? Иными словами, если мы не используем динамическую память (кучу), то компилятор с выше озвученным флагом покажет нам сколько от стека отъедает каждая функция? По поводу "bounded" не понял. A unit compiled with -Wstack-usage will issue a warning for each subprogram whose stack usage might be larger than the specified amount of bytes. Тут будут выдаваться предупреждения, если функция использует стека больше, чем сколько? Изменено 23 апреля, 2012 пользователем spongebob Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
MrYuran 23 23 апреля, 2012 Опубликовано 23 апреля, 2012 · Жалоба Вот спасибо, попробуем :) Это ключ компилятора или линкера? Компилятора. Линкер только связывает, ему про стек ничего не известно. Что значит "все локальные переменные имеют статический размер"? Объявлены как "static" внутри функции или это просто не динамически выделяемая память (из кучи)? Иными словами, если мы не используем динамическую память (кучу), то компилятор с выше озвученным флагом покажет нам сколько от стека отъедает каждая функция? значит, что размер однозначно определен на этапе компиляции. Тут будут выдаваться предупреждения, если функция использует стека больше, чем сколько? than the specified amount of bytes чем определенное количество байтов. Где определенное - надо смотреть конкретнее в документации на это сообщение. -Wstack-usage=len Warn if the stack usage of a function might be larger than len bytes. The computation done to determine the stack usage is conservative. Any space allocated via alloca, variable-length arrays, or related constructs is included by the compiler when determining whether or not to issue a warning. The message is in keeping with the output of ‘-fstack-usage’. • If the stack usage is fully static but exceeds the specified amount, it’s: warning: stack usage is 1120 bytes • If the stack usage is (partly) dynamic but bounded, it’s: warning: stack usage might be 1648 bytes • If the stack usage is (partly) dynamic and not bounded, it’s: warning: stack usage might be unbounded Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
spongebob 0 23 апреля, 2012 Опубликовано 23 апреля, 2012 · Жалоба avr-g++.exe -mmcu=atmega1280 -Wall -ffunction-sections -fdata-sections -fstack-usage -DF_CPU=8000000UL -g -c Src\tst.cpp -o Obj\Debug\Src\tst.o cc1plus.exe: error: unrecognized command line option "-fstack-usage" Не понимает такой опции :( Может, староват компилер? C:\WinAVR\bin>avr-g++ -v Using built-in specs. Target: avr Configured with: ../gcc-4.2.2/configure --enable-win32-registry=WinAVR-20071221 --with-gmp=/usr/local --with-mpfr=/usr/local --prefix=/c/WinAVR --target=avr --e nable-languages=c,c++,objc,ada --with-dwarf2 --disable-nls --enable-doc --disabl e-shared --disable-libada Thread model: single gcc version 4.2.2 (WinAVR 20071221) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
spongebob 0 24 апреля, 2012 Опубликовано 24 апреля, 2012 · Жалоба Вот еще интересная ссылочка: http://www.avrfreaks.net/index.php?name=PN...pic&t=52249 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
MrYuran 23 24 апреля, 2012 Опубликовано 24 апреля, 2012 · Жалоба Не понимает такой опции :( Может, староват компилер? gcc version 4.2.2 (WinAVR 20071221) Да уж, много воды за 5 лет утекло... Вот еще интересная ссылочка: http://www.avrfreaks.net/index.php?name=PN...pic&t=52249 Да, но это только в рантайме работает. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
spongebob 0 24 апреля, 2012 Опубликовано 24 апреля, 2012 · Жалоба Касательно вот этого еще вопросы есть: http://www.nongnu.org/avr-libc/user-manual/malloc.html Динамическая память не нужна (куча). На плате установлена внешняя ОЗУ (32 кБ). Ее хотелось бы использовать по-максимуму :) Если не указывать линкеру __heap_end и __heap_star, он под кучу память выделит или нет? Если все-таки выделит, то куда поместит? Сразу за bss? Стек, как я понимаю, всегда лучше размещать во внутренней памяти (скорость выше). Ну а как разместить стек во внешней памяти? Кстати, в какой секции лучше разместить инициализацию внешней памяти (включение и конфигурирование)? В третьей? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ReAl 0 24 апреля, 2012 Опубликовано 24 апреля, 2012 · Жалоба Даже если указывать __heap_start и __heap_end, но не вызывать malloc, память реально не будет использоваться. Если не указать, но вызвать malloc — линкер выругается на их отсутствие. Да, в третьей нормально. До пятой компилятор ещё ничего от себя не вызывает, так что даже если стек во внешней памяти вдруг, всё равно не страшно. Смотрите еще в теме про mega1280 и внешнюю память Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
spongebob 0 24 апреля, 2012 Опубликовано 24 апреля, 2012 · Жалоба Фигня какая-то... поставил 2010 WinAVR, он тоже опции -fstack-usage не знает :( Объясните, пожалуйста, я, может, не понимаю чего? :) Может, для AVRов это не работает? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
spongebob 0 27 апреля, 2012 Опубликовано 27 апреля, 2012 · Жалоба Вопросы по секциям. Читаю http://www.nongnu.org/avr-libc/user-manual, но там как-то очень скупо все описано... прошу помочь разобраться. Записи -Wl,--section-start=.data=0x802000 и -Wl,--section-start,.data=0x802000 эквивалентны? Т. е., синтаксис указания начала расположения секций имеет несколько вариантов? Как на самом деле выглядят секции .data, .bss, .noinit? Как я понимаю, данные из .data копируются в определенный момент времени в ОЗУ, инициализируя переменные. Когда копируются и кем? Нашел нечто в описании секции .init4, но там сказано про контроллеры с ОЗУ > 64 кБ ПЗУ (а это почему?). Для секции .bss аналогичной операцией является заполнение нулями. Когда? А для чего нужна секция .noinit? Ведь есть же .data и .bss. Правильно ли я понимаю, что все глобальные или статические переменные, которые инициализированы программистом попадают в .data, а неинициализированные глобальные и статические переменные попадают в .bss и инициализируются нулями (по стандарту Си)? А что происходит с локальными инициализированными переменными? Они же создаются на стеке, кто их и когда инициализирует? Зачем нужны .finiN, если мы в main() обычно делаем while(1) {};? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 134 27 апреля, 2012 Опубликовано 27 апреля, 2012 · Жалоба Записи -Wl,--section-start=.data=0x802000 и -Wl,--section-start,.data=0x802000 эквивалентны? Т. е., синтаксис указания начала расположения секций имеет несколько вариантов? Да, эквивалентны. Это синтаксис ключа -Wl передачи параметров программой-драйвером (avr-)gcc программе-линкеру (avr-)ld: -Wl,option Pass option as an option to the linker. If option contains commas, it is split into multiple options at the commas. You can use this syntax to pass an argument to the option. For example, `-Wl,-Map,output.map' passes `-Map output.map' to the linker. When using the GNU linker, you can also get the same effect with `-Wl,-Map=output.map'. Как на самом деле выглядят секции .data, .bss, .noinit?Как области памяти. Как я понимаю, данные из .data копируются в определенный момент времени в ОЗУ, инициализируя переменные. Когда копируются и кем?Да, верно понимаете. Копируются библиотечным стартап-кодом, который вызывается по вектору сброса и выполняется перед запуском main(). Этот код является частью avr-libc. Аналогично там же обнуляется секция .bss А для чего нужна секция .noinit? Ведь есть же .data и .bss.Для переменных, которые не нужно обнулять или инициализировать. Например, чтобы восстановить по их содержимому состояние программы после аварийного сброса по собаке или это может быть внешняя ОЗУ с батарейкой. Правильно ли я понимаю, что все глобальные или статические переменные, которые инициализированы программистом попадают в .data, а неинициализированные глобальные и статические переменные попадают в .bss и инициализируются нулями (по стандарту Си)?Да, правильно. Современные компиляторы могут помещать явно инициализированные нулем глобальные переменные в .bss вместо .data, чтобы не хранить их начальные значения и сэкономить таким образом память кода. А что происходит с локальными инициализированными переменными? Они же создаются на стеке, кто их и когда инициализирует?Исполняемый код функции во время ее исполнения, как правило непосредственно перед использованием локальной переменной. А вот место в стеке резервируется сразу под все локальные переменные на входе в функцию, чтобы не дергать указатель стека на каждую переменную. Естественно, компилятор использует одно и то же место на стеке под разные локальные переменные у которых не пересекается время жизни. Зачем нужны .finiN, если мы в main() обычно делаем while(1) {};?На случай, если вы захотите выйти из main(). Если вы из main() не выходите - можете удалить их из линкерного скрипта и сэкономить пару байтов кода. А можете подменить библиотечный exit() выключая в нем питание и тогда сможете выключать прибор выходя из main(). Перед выключением будут штатно и в нужном порядке вызываться деструкторы глобальных объектов. Мало ли, может быть вам это нужно. Например, закрыть файлы и отмонтировать файловую систему, предварительно сделав запись об отключении в деструкторе объекта-системного журнала событий. Программы бывают разные. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
spongebob 0 27 апреля, 2012 Опубликовано 27 апреля, 2012 · Жалоба Да, верно понимаете. Копируются библиотечным стартап-кодом, который вызывается по вектору сброса и выполняется перед запуском main(). Этот код является частью avr-libc. Аналогично там же обнуляется секция .bss По поводу вызова библиотечного стартап-кода. Как я понимаю, все что содержится в секциях .initN до вызова main() - это и есть стартап? :) Т. е., он "размазывается" по секциям .init0 ... .init9 с дальнейшей возможностью коррекции каждой из секций программистом (часть секций менять нельзя, как я понял)? Кстати, когда мы объявляем функцию способом типа void my_init_portb (void) __attribute__ ((naked)) __attribute__ ((section (".init3"))); то это замещает все что было помещено стартапом в эти секции? А если несколько аналогичных выше указанным объявлениям, то все что объявлено разместится в секции .init3? На случай, если вы захотите выйти из main(). Если вы из main() не выходите - можете удалить их из линкерного скрипта и сэкономить пару байтов кода. А можете подменить библиотечный exit() выключая в нем питание и тогда сможете выключать прибор выходя из main(). Перед выключением будут штатно и в нужном порядке вызываться деструкторы глобальных объектов. Мало ли, может быть вам это нужно. Например, закрыть файлы и отмонтировать файловую систему, предварительно сделав запись об отключении в деструкторе объекта-системного журнала событий. Программы бывают разные. Понятно, достаточно удобная вещь получается, если писать на Си++ :) Кстати, можете сказать что-нибудь по поводу избыточности/неизбыточности Си++ по отношению к Си и насколько оптимальным получается код для AVR, ARM? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
spongebob 0 27 апреля, 2012 Опубликовано 27 апреля, 2012 (изменено) · Жалоба Стартап является универсальным для всех контроллеров, поддерживаемых библиотекой и вся необходимая ему информация берется из macros.inc и каких-то других файлов библиотеки? Изменено 27 апреля, 2012 пользователем spongebob Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 134 27 апреля, 2012 Опубликовано 27 апреля, 2012 · Жалоба По поводу вызова библиотечного стартап-кода. Как я понимаю, все что содержится в секциях .initN до вызова main() - это и есть стартап? :)Да. Т. е., он "размазывается" по секциям .init0 ... .init9 с дальнейшей возможностью коррекции каждой из секций программистом (часть секций менять нельзя, как я понял)?Можно и добавлять и замещать. Кстати, когда мы объявляем функцию способом типа void my_init_portb (void) __attribute__ ((naked)) __attribute__ ((section (".init3"))); то это замещает все что было помещено стартапом в эти секции? А если несколько аналогичных выше указанным объявлениям, то все что объявлено разместится в секции .init3?Если имя не совпадает с библиотечными, то добавляется. Если имя совпадает с глобальной библиотечной меткой - то замещает: extern "C" __attribute__((section(".init4"),__naked__)) void __do_clear_bss() { asm volatile ("clr R2"); } extern "C" __attribute__((section(".init4"),__naked__)) void __abyrvalg() { asm volatile ("clr R3"); } 000004bc <__do_clear_bss>: 4bc: 22 24 eor r2, r2 000004be <__abyrvalg>: 4be: 33 24 eor r3, r3 000004c0 <__do_copy_data>: 4c0: 11 e0 ldi r17, 0x01; 1 4c2: a0 e0 ldi r26, 0x00; 0 4c4: b1 e0 ldi r27, 0x01; 1 4c6: e0 ea ldi r30, 0xA0; 160 4c8: f0 e1 ldi r31, 0x10; 16 4ca: 02 c0 rjmp .+4 ; 0x4d0 <__do_copy_data+0x10> 4cc: 05 90 lpm r0, Z+ 4ce: 0d 92 st X+, r0 4d0: a2 30 cpi r26, 0x02; 2 4d2: b1 07 cpc r27, r17 4d4: d9 f7 brne .-10 ; 0x4cc <__do_copy_data+0xc> ................................ Кстати, можете сказать что-нибудь по поводу избыточности/неизбыточности Си++ по отношению к Си и насколько оптимальным получается код для AVR, ARM?Много копий сломано тут на эту тему. Если писать с умом - то ни байта лишнего не будет. Ровно столько же, сколько занял бы делающий то же самое код на голом Си, но при этом со всеми преимуществами, которые дают плюсы: перегрузка операторов, конструкторы-деструкторы, ссылки, инкапсуляция, наследование, шаблоны, enum как отдельный тип, константы и многое другое. Стартап является универсальным для всех контроллеров, поддерживаемых библиотекой и вся необходимая ему информация берется из macros.inc и каких-то других файлов библиотеки?Да, на этапе компиляции библиотеки из этого файла строится куча файлов crtXXX.o, а при линковке gcc подключает нужный в зависимости от ключа -mmcu=. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться