简体   繁体   English

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

[英]How can I check that all my init functions have been called?

I am writing a large C program for embedded use. 我正在编写一个用于嵌入式使用的大型C程序。 Every module in this program has an init() function (like a constructor) to set up its static variables. 该程序中的每个模块都有一个init()函数(如构造函数)来设置其静态变量。

The problem is that I have to remember to call all of these init functions from main() . 问题是我必须记得从main()调用所有这些init函数。 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. 在每个init函数中放置一个宏的行,当你稍后调用check_inited()函数时,如果没有调用所有函数,则向STDOUT发送警告。

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. 我可以增加一个计数器,但我必须在某处保持正确数量的init函数,这也容易出错。

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. 我的目标是确保实际调用所有的init函数。 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. 我不能像Nick D建议的那样自动调用它们,因为它们需要按特定顺序调用。

To accomplish this, a macro included in every module uses the gcc constructor attribute to add the init function name to a global list. 为此,每个模块中包含的宏使用gcc constructor属性将init函数名称添加到全局列表中。

Another macro included in the body of the init function updates the global list to make a note that the function was actually called. init函数体中包含的另一个宏更新全局列表,以记录该函数实际被调用。

Finally, a check function is called in main() after all of the inits are done. 最后,在完成所有的inits之后,在main()调用一个check函数。

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 "nx_lib". 我可重用的实用函数库叫做“nx_lib”。 Thus all the 'nxl' designations. 因此所有'nxl'指定。

  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. pierr向我介绍了constructor属性。

Nick D for demonstrating the ## preprocessor trick and giving me the framework. Nick D用于演示##预处理器技巧并给我框架。

tod frye for a clever linker-based approach that will work with many compilers. tod frye用于基于链接器的聪明方法,可以与许多编译器一起使用。

Everyone else for helping out and sharing useful tidbits. 其他人帮助和分享有用的花絮。

nx_lib_public.h

This is the relevant fragment of my library header file 这是我的库头文件的相关片段

#define NX_FUNC_RUN_CHECK_NAME_SIZE 20

typedef struct _nxl_function_element{
  char func[NX_FUNC_RUN_CHECK_NAME_SIZE];
  BOOL called;
} nxl_function_element;

void nxl_func_run_check_add(char *func_name);
BOOL nxl_func_run_check(void);
void nxl_func_run_check_hit(char *func_name);

#define NXL_FUNC_RUN_CHECK_ADD(function_name) \
  void cons_ ## function_name() __attribute__((constructor)); \
  void cons_ ## function_name() { nxl_func_run_check_add(#function_name); }

nxl_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 nxl_function_element  m_functions[MAX_CHECKED_FUNCTIONS]; 
static int                   m_func_cnt = 0; 


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

  strncpy (m_functions[m_func_cnt].func, func_name, 
           NX_FUNC_RUN_CHECK_NAME_SIZE);

  m_functions[m_func_cnt].func[NX_FUNC_RUN_CHECK_NAME_SIZE-1] = 0;

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

// call from inside the init function
void nxl_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, 
                  NX_FUNC_RUN_CHECK_NAME_SIZE)) {
      m_functions[i].called = TRUE;   
      return;
    }
  }

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

// checks that all registered functions were called
BOOL nxl_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("nxl_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 "nx_lib_public.h"

NXL_FUNC_RUN_CHECK_ADD(solo_init)
void solo_init(void)
{
  nxl_func_run_check_hit((char *) __func__);

  /* do module initialization here */
}

You can use gcc 's extension __attribute__((constructor)) if gcc is ok for your project. 如果gcc适用于您的项目,则可以使用gcc的扩展名__attribute__((constructor))

#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

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. 例如,如果每个模块都有一个头文件modX.c. And if the signature of your init() function is "void init()"... 如果你的init()函数的签名是“void init()”......

Have your script grep through all your .h files, and create a list of module names that need to be init()ed. 让您的脚本浏览所有.h文件,并创建需要init()编辑的模块名称列表。 Then have the script check that init() is indeed called on each module in main(). 然后让脚本检查是否确实在main()中的每个模块上调用了init()。

Splint (and probably other Lint variants) can give a warning about functions that are defined but not called. Splint(可能还有其他Lint变体)可以对已定义但未调用的函数发出警告。

It's interesting that most compilers will warn you about unused variables, but not unused functions. 有趣的是,大多数编译器会警告您未使用的变量,但不会使用未使用的函数。

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. (基本思想是注册函数指针,就像atexit函数那样。
Of course atexit implementation is different) 当然atexit实现是不同的)

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

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. 其中“ __predict_false ”是编译器的分支预测提示。 When first object is created, module is auto-initialized (for once). 创建第一个对象时,模块会自动初始化(一次)。

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. 无论何时定义init函数,只需为init函数指针在链接器部分中放置一个指向它的指针。 then you can at least find out how many init functions have been compiled. 那么你至少可以找出已经编译了多少个init函数。

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. 如果调用init函数的顺序无关紧要,并且all都具有相同的原型,则可以在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. 这会在.myinits部分中放置一个指向模块init函数的指针,但将代码保留在.code部分中。 so the .myinits section is nothing but pointers. 所以.myinits部分只不过是指针。 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. 如果init函数都具有相同的protoytpe,你可以迭代这一部分,全部调用它们。

this, in effect, is creating your own static constructor system in C. 实际上,这是在C中创建自己的静态构造函数系统。

if you are doing a large project and your linker is not at least this fully featured, you may have a problem... 如果你正在做一个大型项目并且你的链接器至少不是这个功能齐全,你可能会遇到问题......

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. 您可以想象为每个模块实现一种“状态机”,其中函数的动作取决于模块所处的状态。该状态可以设置为BEFORE_INIT或INITIALIZED。

For example, let's say we have module A with functions foo and bar. 例如,假设我们有模块A,其函数为foo和bar。

The actual logic of the functions (ie, 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 (ie, the actual function declared foo()) will perform a run-time check of the condition of the module, and decide what to do: 然后,模块的实际功能(即,声明为foo()的实际函数)将执行模块状态的运行时检查,并决定该做什么:

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. 这不是一个真正的状态机,因为只有两个状态使用基本if检查,没有某种智能通用逻辑。
  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. 当你使用单独的线程/任务时,这种“设计模式”很有用,而你调用的函数实际上是使用某种IPC调用的。
  4. A state machine can be nicely implemented in C++, might be worth reading up on it. 状态机可以很好地用C ++实现,可能值得一读。 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. 可以想象,同样的想法可以在C中用函数指针数组编码,但几乎肯定不值得你花时间。

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. 我的想法是让每个函数将其名称添加到全局函数列表中,例如Nick D的解决方案。

Then I would run through the symbol table produced by -gstab, and look for any functions named init_* that had not been called. 然后我将遍历-gstab生成的符号表,并查找尚未调用的名为init_ *的任何函数。

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. 但是我不喜欢这个想法,因为这意味着我总是要在二进制文件中包含调试信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 我有2个非常相似的C函数,分别从python和Java调用。 如何将2种库合并为1种可以从两种语言调用的库? - I have 2 very similar C functions being called from python and Java. How can I combine the 2 libraries into 1 that can be called from both languages? 我可以在init和ctor中看到函数吗 - Can I see functions in init and ctors atexit()注册了多少个函数? - How many functions have been registered by atexit()? 如何实现字谜和回文功能来检查用户输入的单词? - How can I implement my anagram and palindrome functions to check the words input by a user? C 中的每个 function 是否应该有自己的.c 文件? 或者我可以将我程序的所有功能放在一个.c 文件中吗? - Should every function in C have its own .c file ? or can i put all the functions of my program in one .c file? 如何通过内核在文件外部调用模块中的静态init和exit函数? - How static init and exit functions in a module are called outside the file by the kernel? C:如何检查是否已写入 memory 地址? - C: How can I check if a memory address has been written? 如何检查我是否正确实现了 C 库函数? - How can I check if I implement C library functions correctly? 如何检查从哪个文件调用此函数? - How can i check this function is called from which file? 不调用my_udf_init - my_udf_init not called
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM