如何检查我的所有 init 函数是否已被调用?

发布于 2024-08-06 13:05:42 字数 3561 浏览 0 评论 0原文

我正在编写一个用于嵌入式使用的大型 C 程序。该程序中的每个模块都有一个 init() 函数(类似于构造函数)来设置其静态变量。

问题是我必须记住从 main() 调用所有这些 init 函数。如果我出于某种原因将它们注释掉,我还必须记住将它们放回去。

我有什么聪明的办法来确保所有这些函数都被调用吗?类似于在每个 init 函数中放置一个宏,当您稍后调用 check_inited() 函数时,如果未调用所有函数,则会向 STDOUT 发送警告。

我可以增加一个计数器,但我必须在某个地方维护正确数量的初始化函数,这也很容易出错。

想法?

以下是我决定的解决方案,根据该线程中几个人的意见

我的目标是确保我的所有 init 函数实际上都被调用。我想做 这无需维护多个文件中的模块列表或计数。我无法打电话 正如 Nick D 建议的那样自动调用它们,因为它们需要按一定的顺序调用。

为了实现这一点,每个模块中包含的宏使用 gcc constructor 属性来 将 init 函数名称添加到全局列表中。

init 函数体中包含的另一个宏更新全局列表以创建 请注意,该函数实际上被调用了。

最后,在所有初始化完成后,在 main() 中调用检查函数。

注意:

  1. 我选择将字符串复制到数组中。这并不是绝对必要的,因为 在正常使用中,传递的函数名称始终是静态字符串。如果记忆力不够 您可以只存储指向传入字符串的指针。

  2. 我的可重用实用程序函数库称为“my_lib”。因此,所有的“myl”名称都是如此。

  3. 这不是世界上最高效的代码,但它仅称为启动时间,因此 对我来说不重要。

  4. 每个模块需要添加两行代码。如果省略其中任何一个, 检查功能会让您知道。

  5. 您可以将构造函数设为静态,这样就无需为其指定在整个项目中唯一的名称。

  6. 此代码仅经过轻微测试,而且确实已经很晚了,因此请在信任它之前仔细检查。

感谢:

pierr,他向我介绍了 constructor 属性。

Nick D 演示了 ## 预处理器技巧并为我提供了框架。

tod Friede 提供了一种基于链接器的巧妙方法,可以与许多编译器一起使用。

其他人帮助并分享有用的花絮。

my_lib_public.h

这是我的库头文件的相关片段

#define MY_FUNC_RUN_CHECK_NAME_SIZE 20

typedef struct _myl_function_element{
  char func[MY_FUNC_RUN_CHECK_NAME_SIZE];
  BOOL called;
} myl_function_element;

void myl_func_run_check_add(char *func_name);
BOOL myl_func_run_check(void);
void myl_func_run_check_hit(char *func_name);

#define MYL_FUNC_RUN_CHECK_ADD(function_name) \
  void cons_ ## function_name() __attribute__((constructor)); \
  void cons_ ## function_name() { myl_func_run_check_add(#function_name); }

myl_func_run_check.c

这是调用以添加函数名称并稍后检查它们的库代码。

#define MAX_CHECKED_FUNCTIONS 100

static myl_function_element  m_functions[MAX_CHECKED_FUNCTIONS]; 
static int                   m_func_cnt = 0; 


// call automatically before main runs to register a function name.
void myl_func_run_check_add(char *func_name)
{
  // fail and complain if no more room.
  if (m_func_cnt >= MAX_CHECKED_FUNCTIONS) {
    print ("myl_func_run_check_add failed, out of space\r\n");
    return; 
  }

  strncpy (m_functions[m_func_cnt].func, func_name, 
           MY_FUNC_RUN_CHECK_NAME_SIZE);
       
  m_functions[m_func_cnt].func[MY_FUNC_RUN_CHECK_NAME_SIZE-1] = 0;

  m_functions[m_func_cnt++].called = FALSE;
}

// call from inside the init function
void myl_func_run_check_hit(char *func_name)
{
  int i;

  for (i=0; i< m_func_cnt; i++) {
    if (! strncmp(m_functions[i].func, func_name, 
                  MY_FUNC_RUN_CHECK_NAME_SIZE)) {
      m_functions[i].called = TRUE;   
      return;
    }
  }

  print("myl_func_run_check_hit(): error, unregistered function was hit\r\n");
}

// checks that all registered functions were called
BOOL myl_func_run_check(void) {
  int i;
  BOOL success=TRUE;

  for (i=0; i< m_func_cnt; i++) {
    if (m_functions[i].called == FALSE) {
      success = FALSE;
      xil_printf("myl_func_run_check error: %s() not called\r\n", 
                 m_functions[i].func);
     } 
  }
  return success;
}

solo.c

这是需要初始化的模块示例

#include "my_lib_public.h"

MYL_FUNC_RUN_CHECK_ADD(solo_init)
void solo_init(void)
{
  myl_func_run_check_hit((char *) __func__);

  /* do module initialization here */
}

I am writing a large C program for embedded use. Every module in this program has an init() function (like a constructor) to set up its static variables.

The problem is that I have to remember to call all of these init functions from main(). I also have to remember to put them back if I have commented them out for some reason.

Is there anything clever I do to make sure that all of these functions are getting called? Something along the lines of putting a macro in each init function that, when you call a check_inited() function later, sends a warning to STDOUT if not all the functions are called.

I could increment a counter, but I'd have to maintain the correct number of init functions somewhere and that is also prone to error.

Thoughts?

The following is the solution I decided on, with input from several people in this thread

My goal is to make sure that all my init functions are actually being called. I want to do
this without maintaining lists or counts of modules across several files. I can't call
them automatically as Nick D suggested because they need to be called in a certain order.

To accomplish this, a macro included in every module uses the gcc constructor attribute to
add the init function name to a global list.

Another macro included in the body of the init function updates the global list to make a
note that the function was actually called.

Finally, a check function is called in main() after all of the inits are done.

Notes:

  1. I chose to copy the strings into an array. This not strictly necessary because the
    function names passed will always be static strings in normal usage. If memory was short
    you could just store a pointer to the string that was passed in.

  2. My reusable library of utility functions is called "my_lib". Thus all the 'myl' designations.

  3. This isn't the most efficient code in the world but it's only called a boot time so that
    doesn't matter for me.

  4. There are two lines of code that need to be added to each module. If either is omitted,
    the check function will let you know.

  5. you might be able to make the constructor function static, which would avoid the need to give it a name that is unique across the project.

  6. this code is only lightly tested and it's really late so please check carefully before trusting it.

Thank you to:

pierr who introduced me to the constructor attribute.

Nick D for demonstrating the ## preprocessor trick and giving me the framework.

tod frye for a clever linker-based approach that will work with many compilers.

Everyone else for helping out and sharing useful tidbits.

my_lib_public.h

This is the relevant fragment of my library header file

#define MY_FUNC_RUN_CHECK_NAME_SIZE 20

typedef struct _myl_function_element{
  char func[MY_FUNC_RUN_CHECK_NAME_SIZE];
  BOOL called;
} myl_function_element;

void myl_func_run_check_add(char *func_name);
BOOL myl_func_run_check(void);
void myl_func_run_check_hit(char *func_name);

#define MYL_FUNC_RUN_CHECK_ADD(function_name) \
  void cons_ ## function_name() __attribute__((constructor)); \
  void cons_ ## function_name() { myl_func_run_check_add(#function_name); }

myl_func_run_check.c

This is the libary code that is called to add function names and check them later.

#define MAX_CHECKED_FUNCTIONS 100

static myl_function_element  m_functions[MAX_CHECKED_FUNCTIONS]; 
static int                   m_func_cnt = 0; 


// call automatically before main runs to register a function name.
void myl_func_run_check_add(char *func_name)
{
  // fail and complain if no more room.
  if (m_func_cnt >= MAX_CHECKED_FUNCTIONS) {
    print ("myl_func_run_check_add failed, out of space\r\n");
    return; 
  }

  strncpy (m_functions[m_func_cnt].func, func_name, 
           MY_FUNC_RUN_CHECK_NAME_SIZE);
       
  m_functions[m_func_cnt].func[MY_FUNC_RUN_CHECK_NAME_SIZE-1] = 0;

  m_functions[m_func_cnt++].called = FALSE;
}

// call from inside the init function
void myl_func_run_check_hit(char *func_name)
{
  int i;

  for (i=0; i< m_func_cnt; i++) {
    if (! strncmp(m_functions[i].func, func_name, 
                  MY_FUNC_RUN_CHECK_NAME_SIZE)) {
      m_functions[i].called = TRUE;   
      return;
    }
  }

  print("myl_func_run_check_hit(): error, unregistered function was hit\r\n");
}

// checks that all registered functions were called
BOOL myl_func_run_check(void) {
  int i;
  BOOL success=TRUE;

  for (i=0; i< m_func_cnt; i++) {
    if (m_functions[i].called == FALSE) {
      success = FALSE;
      xil_printf("myl_func_run_check error: %s() not called\r\n", 
                 m_functions[i].func);
     } 
  }
  return success;
}

solo.c

This is an example of a module that needs initialization

#include "my_lib_public.h"

MYL_FUNC_RUN_CHECK_ADD(solo_init)
void solo_init(void)
{
  myl_func_run_check_hit((char *) __func__);

  /* do module initialization here */
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

过去的过去 2024-08-13 13:05:42

您可以使用 gcc的扩展 __attribute__((constructor)) 如果 gcc 适合您的项目。

#include <stdio.h>
void func1() __attribute__((constructor));
void func2() __attribute__((constructor));

void func1()
{
    printf("%s\n",__func__);
}

void func2()
{
    printf("%s\n",__func__);
}

int main()
{
    printf("main\n");
    return 0;
}

//the output
func2
func1
main

You can use gcc's extension __attribute__((constructor)) if gcc is ok for your project.

#include <stdio.h>
void func1() __attribute__((constructor));
void func2() __attribute__((constructor));

void func1()
{
    printf("%s\n",__func__);
}

void func2()
{
    printf("%s\n",__func__);
}

int main()
{
    printf("main\n");
    return 0;
}

//the output
func2
func1
main
听闻余生 2024-08-13 13:05:42

我不知道下面的内容看起来有多难看,但我还是发布了:-)

(基本思想是注册函数指针,就像 atexit 函数可以。
当然 atexit 实现是不同的)

在主模块中我们可以有这样的东西:

typedef int (*function_t)(void);

static function_t  vfunctions[100]; // we can store max 100 function pointers
static int         vcnt = 0; // count the registered function pointers

int add2init(function_t f)
{
  // todo: error checks
  vfunctions[vcnt++] = f;
  return 0;
}
...

int main(void) {
 ...
 // iterate vfunctions[] and call the functions
 ...
}

...在其他模块中:

typedef int (*function_t)(void);
extern int add2init(function_t f);
#define M_add2init(function_name)  static int int_ ## function_name = add2init(function_name)

int foo(void)
{
   printf("foo\n");
   return 0;
}
M_add2init(foo); // <--- register foo function

I don't know how ugly the following looks but I post it anyway :-)

(The basic idea is to register function pointers, like what atexit function does.
Of course atexit implementation is different)

In the main module we can have something like this:

typedef int (*function_t)(void);

static function_t  vfunctions[100]; // we can store max 100 function pointers
static int         vcnt = 0; // count the registered function pointers

int add2init(function_t f)
{
  // todo: error checks
  vfunctions[vcnt++] = f;
  return 0;
}
...

int main(void) {
 ...
 // iterate vfunctions[] and call the functions
 ...
}

... and in some other module:

typedef int (*function_t)(void);
extern int add2init(function_t f);
#define M_add2init(function_name)  static int int_ ## function_name = add2init(function_name)

int foo(void)
{
   printf("foo\n");
   return 0;
}
M_add2init(foo); // <--- register foo function
水染的天色ゝ 2024-08-13 13:05:42

为什么不编写一个后处理脚本来为您进行检查。然后运行该脚本作为构建过程的一部分...或者更好的是,将其作为您的测试之一。您正在编写测试,对吗? :)

例如,如果每个模块都有一个头文件 modX.c。如果您的 init() 函数的签名是“void init()”...

让您的脚本 grep 遍历所有 .h 文件,并创建需要 init() 的模块名称列表。然后让脚本检查 main() 中的每个模块确实调用了 init() 。

Why not write a post processing script to do the checking for you. Then run that script as part of your build process... Or better yet, make it one of your tests. You are writing tests, right? :)

For example, if each of your modules has a header file, modX.c. And if the signature of your init() function is "void init()"...

Have your script grep through all your .h files, and create a list of module names that need to be init()ed. Then have the script check that init() is indeed called on each module in main().

离旧人 2024-08-13 13:05:42

如果您的单个模块表示“类”实体并具有实例构造函数,则可以使用以下构造:

static inline void init(void) { ... }
static int initialized = 0;
#define INIT if (__predict_false(!initialized)) { init(); initialized = 1; }
struct Foo *
foo_create(void)
{
    INIT;
    ...
}

其中“__predict_false”是编译器的分支预测提示。创建第一个对象时,模块会自动初始化(一次)。

If your single module represents "class" entity and has instance constructor, you can use following construction:

static inline void init(void) { ... }
static int initialized = 0;
#define INIT if (__predict_false(!initialized)) { init(); initialized = 1; }
struct Foo *
foo_create(void)
{
    INIT;
    ...
}

where "__predict_false" is your compiler's branch prediction hint. When first object is created, module is auto-initialized (for once).

与风相奔跑 2024-08-13 13:05:42

Splint(可能还有其他 Lint 变体)可以对已定义但未调用的函数发出警告。

有趣的是,大多数编译器都会警告您未使用的变量,但不会警告您未使用的函数。

Splint (and probably other Lint variants) can give a warning about functions that are defined but not called.

It's interesting that most compilers will warn you about unused variables, but not unused functions.

温暖的光 2024-08-13 13:05:42

更长的运行时间不是问题

您可以想象为每个模块实现一种“状态机”,其中函数的操作取决于模块所处的状态。该状态可以设置为 BEFORE_INIT 或 INITIALIZED。

例如,假设我们有模块 A,其函数为 foo 和 bar。

函数的实际逻辑(即它们实际做什么)将像这样声明:

void foo_logic();
void bar_logic();

或者无论签名是什么。

然后,模块的实际函数(即声明的实际函数 foo())将对模块的条件执行运行时检查,并决定做什么:

void foo() {
       if (module_state == BEFORE_INIT) {
           handle_not_initialized_error();
       }
       foo_logic();
}

对所有函数重复此逻辑。

需要注意的几点:

  1. 这显然会导致性能方面的巨大损失,因此
    可能不是一个好主意(我发布了
    无论如何,因为你说运行时是
    不是问题)。
  2. 这不是一个真正的状态机,因为只有两个状态可以使用基本的 if 来检查,而没有某种智能的通用逻辑。
  3. 当您使用单独的线程/任务时,这种“设计模式”非常有效,并且您调用的函数实际上是使用某种 IPC 来调用的。
  4. 状态机可以很好地用 C++ 实现,可能值得阅读。可以想象,同样的想法可以用函数指针数组用 C 语言编写,但这几乎肯定不值得您花时间。

Larger running time is not a problem

You can conceivably implement a kind of "state-machine" for each module, wherein the actions of a function depend on the state the module is in. This state can be set to BEFORE_INIT or INITIALIZED.

For example, let's say we have module A with functions foo and bar.

The actual logic of the functions (i.e., what they actually do) would be declared like so:

void foo_logic();
void bar_logic();

Or whatever the signature is.

Then, the actual functions of the module (i.e., the actual function declared foo()) will perform a run-time check of the condition of the module, and decide what to do:

void foo() {
       if (module_state == BEFORE_INIT) {
           handle_not_initialized_error();
       }
       foo_logic();
}

This logic is repeated for all functions.

A few things to note:

  1. This will obviously incur a huge penalty performance-wise, so is
    probably not a good idea (I posted
    anyway because you said runtime is
    not a problem).
  2. This is not a real state-machine, since there are only two states which are checked using a basic if, without some kind of smart general logic.
  3. This kind of "design-pattern" works great when you're using separate threads/tasks, and the functions you're calling are actually called using some kind of IPC.
  4. A state machine can be nicely implemented in C++, might be worth reading up on it. The same kind of idea can conceivably be coded in C with arrays of function pointers, but it's almost certainly not worth your time.
濫情▎り 2024-08-13 13:05:42

您可以使用链接器部分按照这些思路做一些事情。每当定义 init 函数时,请将指向它的指针放置在链接器部分中,仅用于 init 函数指针。那么你至少可以知道编译了多少个init函数。

如果调用 init 函数的顺序并不重要,并且它们都具有相同的原型,那么您可以在 main 的循环中调用它们。

确切的细节我不记得了,但它的工作原理是这样的::
在模块文件中...

//this is the syntax in GCC..(or would be if the underscores came through in this text editor)
initFuncPtr thisInit __attribute((section(.myinits)))__= &moduleInit;

void moduleInit(void)
{
// so init here
}

这会将指向模块 init 函数的指针放置在 .myinits 部分中,但将代码保留在 .code 部分中。所以 .myinits 部分只不过是指针。您可以将其视为模块文件可以添加到的可变长度数组。

然后您可以从 main 访问该部分的开始和结束地址。然后从那里开始。

如果 init 函数都具有相同的原型,您可以迭代此部分,全部调用它们。

实际上,这就是在 C 中创建您自己的静态构造函数系统。

如果您正在做一个大型项目,并且您的链接器至少没有功能齐全,那么您可能会遇到问题...

you can do something along these lines with a linker section. whenever you define an init function, place a pointer to it in a linker section just for init function pointers. then you can at least find out how many init functions have been compiled.

and if it does not matter what order the init functions are called, and the all have the same prototype, you can just call them all in a loop from main.

the exact details elude my memory, but it works soemthing like this::
in the module file...

//this is the syntax in GCC..(or would be if the underscores came through in this text editor)
initFuncPtr thisInit __attribute((section(.myinits)))__= &moduleInit;

void moduleInit(void)
{
// so init here
}

this places a pointer to the module init function in the .myinits section, but leaves the code in the .code section. so the .myinits section is nothing but pointers. you can think of this as a variable length array that module files can add to.

then you can access the section start and end address from the main. and go from there.

if the init functions all have the same protoytpe, you can just iterate over this section, calling them all.

this, in effect, is creating your own static constructor system in C.

if you are doing a large project and your linker is not at least this fully featured, you may have a problem...

月下伊人醉 2024-08-13 13:05:42

我可以回答我的问题吗?

我的想法是让每个函数将其名称添加到全局函数列表中,就像 Nick D 的解决方案一样。

然后我将运行 -gstab 生成的符号表,并查找任何尚未调用的名为 init_* 的函数。

这是一个嵌入式应用程序,因此我在闪存中方便地保存了精灵图像。

然而我不喜欢这个想法,因为这意味着我总是必须在二进制文件中包含调试信息。

Can I put up an answer to my question?

My idea was to have each function add it's name to a global list of functions, like Nick D's solution.

Then I would run through the symbol table produced by -gstab, and look for any functions named init_* that had not been called.

This is an embedded app so I have the elf image handy in flash memory.

However I don't like this idea because it means I always have to include debugging info in the binary.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文