أسهل طريقة لحساب حجم تابع في لغة سي
إن حساب حجم تابع function ليس بسهولة استخدام العملية خلال compile-time وقت الترجمة ()sizeof . هناك طرق متعددة لحساب حجم التابع خلال التنفيذ run-time ولكن لكي نبقي الأمور سهلة فمن الأفضل التحقق من حجم التابع من الرابط linker والذي يجب أن تتوفر لديه هذه المعلومة.
لنتعرف على الأدوات التي سنستخدمها:
1- الملف “map.”المولّد بواسطة GCC/G++ linker:
يحتوي هذا الملف على الرموز symbols التي لدينا في الكود/الفيرموير سواءً في الفلاش أو الرام أو أي منطقة ذاكرة معرّفة أخرى. إن الاقتباس التالي هو من ترجمة compiling واحد من الأمثلة الموجودة في nRF52 SDK. إذا كنت تريد التجريب كل ما عليك فعله هو استخدام الأمر make default من المسار التالي examples\peripheral\gpiote\pca10040\blank\armgcc. سنجد في العمود الأول عنوان البدء وفي العمود الثاني الحجم بالنظام الست عشري وفي العمود الثالث اسم الملف الغرض object file الذي يحوي هذا الرمز.
.text.bsp_board_led_invert 0x00000380 0x24 _build/nrf52832_xxaa/boards.c.o 0x00000380 bsp_board_led_invert .text.bsp_board_init 0x000003a4 0x5c _build/nrf52832_xxaa/boards.c.o 0x000003a4 bsp_board_init
يمكنك البحث في هذا الملف على اسم الرمز حتى نجده، مثلاً نجد أن التابع bsp_board_led_invert بطول 36 بايت.
2- الملف “out.”المولّد بواسطة GCC/G++ linker:
قد من الصعب أحياناً أن نجد رمز معيّن في الملف “map.” وهنا تأتي الأداة nm الموجودة في GCC tool-chain والتي تعرض جدولاً بجميع الرموز ولأجل هذا الغرض يجب استعمال الملف “out.” أو “elf.”. هذه الأداة بإمكانها عرض الرموز وترتيبها حسب الحجم أيضاً. لدينا الملف “out.” من خرج ترجمة المشروع المشار له سابقاً ولعرض الرموز وأحجامها بالترتيب فإنه يمكننا استخدام التعليمة التالية (انتبه لاستخدام التعليمة بناءً على حالتك، أستخدم ARM GCC في حالتي)
arm-none-eabi-nm -S --size-sort -t d nrf52832_xxaa.out
وهذا اقتباس من الخرج
00001024 00000030 T app_error_handler_bare 00000896 00000036 T bsp_board_led_invert 00003460 00000040 T exit 00000720 00000040 T Reset_Handler 00001056 00000052 W app_error_fault_handler 00000780 00000052 t gpioIntHandler 00000832 00000064 T main 00003500 00000076 T __libc_init_array 536871056 00000076 b m_cb 00000932 00000090 T bsp_board_init 536870916 00000096 d impure_data 00001108 00000136 T nrfx_gpiote_init 00001568 00000196 T nrfx_gpiote_in_event_enable 00002448 00000200 T TIMER0_IRQHandler 00001244 00000324 T nrfx_gpiote_in_init 00000000 00000512 T __isr_vector 00001764 00000684 T GPIOTE_IRQHandler 00002648 00000812 T SystemInit 536871144 00008192 N __HeapBase
في العمود الأول العنوان بشكل عشري وثم الحجم أيضاً بشكل عشري وثم اسم الرمز.
أسماء للرموز غريبة؟
لو وجدت أسماء غريبة للرموز وهي غالباً مجموعة من المحارف التي تسبق وتلي اسم الرمز المعروف لديك فهذا ما يسمى بـ mangling على سبيل المثال إن تابع مثل get_event سيصبح كالتالي:
__ZN2os9get_eventENS_17enum_task_id_typeE
لماذا؟ هذا لأن المترجم يحتاج لأن تكون أسماء التوابع والمتغيرات فريدة خاصة عندما نستعمل المتحولات والتوابع الـstatic والتي قد تستخدم نفس الاسم ولكن بتوابع مختلفة أو ملفات مختلفة فعلى المترجم أن يميّز بينها. ونفس الفكرة تنطبق على الـ overloading في ++C كذلك.
لاستعادة الاسم الاصلي de-mangle تستخدم الأداة c++filt
arm-none-eabi-c++filt __ZN2os9get_eventENS_17enum_task_id_typeE