通過(guò) __attribute( (section(x)) ) 來(lái)對(duì)函數(shù)進(jìn)行快速測(cè)試
先從簡(jiǎn)單代碼開始毫炉,以下代碼通過(guò)宏 EXPORT
來(lái)幫助我們快速定義一個(gè) myfun_t 變量, 所以在EXPORT(hello);
就定義了一個(gè) _hello
變量, 于是我們可以在 main 中進(jìn)行訪問(wèn)
#include <stdio.h>
typedef struct
{
void (*fun)(void);
const char *desc;
} myfun_t;
#define EXPORT(x) \
static myfun_t _##x = { \
.fun = x \
}
void hello(void)
{
printf("Hello\n");
}
EXPORT(hello);
int main(int argc, char const *argv[])
{
_hello.fun();
return 0;
}
__attribute 宏用來(lái)設(shè)置編譯屬性, __attribute( (section(x)) )
可以指定編譯器將變量存放到指定的內(nèi)存區(qū)域, 之后我們就可以使用
extern type_t __start_<section_name>
extern type_t __stop_<section_name>
來(lái)進(jìn)行訪問(wèn), __start_<section_name>
代表段的起始, 是一個(gè)內(nèi)置標(biāo)簽, 就像 int a = 12; 中的 a 一樣, 它的數(shù)據(jù)類型取決于我們?nèi)绾慰创? 和很多變量標(biāo)簽一樣, 通過(guò) &
可以訪問(wèn)變量所在的地址, 所以訪問(wèn)段的的起始地址為 &__start_mysection
#include <stdio.h>
#define MYSECTION __attribute((used, section("mysection")))
char mystr[] MYSECTION = "Good";
// int myvalue MYSECTION = 125;
int main(int argc, char const *argv[])
{
extern char __start_mysection;
printf("%s\n", &__start_mysection);
// extern int __start_mysection;
// printf("%d\n", myvalue); // 125
return 0;
}
測(cè)試代碼
有了上面的基礎(chǔ), 就可以嘗試寫快速測(cè)試的代碼了,一般來(lái)說(shuō)我們希望快速測(cè)試一個(gè)函數(shù), 編寫完后在函數(shù)下面通過(guò)一個(gè)宏定義 TEST_FUNC_ADD 就可以將函數(shù)插入到測(cè)試代碼的行列。
test.h
#pragma once
typedef struct
{
void (*fun)(void);
const char *name;
const char *desc;
char _tmp[8]; /* 32 位對(duì)齊 */
} test_command_t;
#define SECTION __attribute((used, section("myfun_section")))
#define EXPORT_TEST_COMMAND(f, n, d) \
static test_command_t _##f SECTION = { \
.fun = f, \
.name = n, \
.desc = d, \
}
#define TEST_FUNC_ADD(f, n, d, ...) \
static void _##f(void) \
{ \
int r = f(__VA_ARGS__); \
if (r == 0) \
printf("Func %s TEST OK\n", n); \
else \
printf("%s TEST ERR %d\n", n, r); \
return; \
} \
EXPORT_TEST_COMMAND(_##f, n, d)
test.c
#include "test.h"
#include <stdio.h>
#include <string.h>
/* 獲取函數(shù)列表所在的內(nèi)存區(qū)間 */
extern test_command_t __start_myfun_section;
extern test_command_t __stop_myfun_section;
test_command_t *myfun_section_begin = &__start_myfun_section;
test_command_t *myfun_section_end = &__stop_myfun_section;
#define foreach_command(item) \
for (test_command_t *item = myfun_section_begin; \
item != myfun_section_end; item++)
void func1(void)
{
printf("func1: hello\n");
}
EXPORT_TEST_COMMAND(func1, "print", "To print hello");
int func_test(int max)
{
if (max < 10)
return 0;
return -1;
}
TEST_FUNC_ADD(func_test, "less", "Check less than 10", 8);
int main(int argc, char const *argv[])
{
char command[256];
while (1)
{
printf("\nsh# ");
gets(command);
if (strncmp(command, "help", 4) == 0)
{
foreach_command(entry)
{
printf(" %-15s\t -- %s\n", entry->name, entry->desc);
}
continue;
}
foreach_command(entry)
{
if (strcmp(entry->name, command) == 0)
{
entry->fun();
break;
}
}
}
return 0;
}
然而上述功能只能對(duì) GCC 平臺(tái)有效, 如果是 ARMCC 或是其他平臺(tái), 因?yàn)榫幾g器不同, 方法可能不一樣, 為了跨平臺(tái), 就不得不添加平臺(tái)檢測(cè)的宏, 比如將下面的代碼替換獲取 myfun_section 所在的內(nèi)存區(qū)間部分即可支持 ARMCC 平臺(tái)溜在。
#ifdef __ARMCC_VERSION /* ARM C Compiler */
extern test_command_t myfun_section$$Base;
extern test_command_t myfun_section$$Limit;
test_command_t *myfun_section_begin = &(myfun_section$$Base);
test_command_t *myfun_section_end = &(myfun_section$$Limit);
#elif defined (__GNUC__)
extern test_command_t __start_myfun_section;
extern test_command_t __stop_myfun_section;
test_command_t *myfun_section_begin = &__start_myfun_section;
test_command_t *myfun_section_end = &__stop_myfun_section;
#else
#error "The platform is not supported"
#endif