How to determine the background function called in critical functions

I am working on a very large C ++ project, it has many critical functions in real time, as well as many slow background functions. These background functions should not be called from critical time functions. So is there a way to detect that these background functions are being called from critical functions? compilation time would be good, but anyway I like to detect in front of these background functions. More information, both slow and critical functions, are part of the same class and have the same header.

Additional information: critical functions work with a faster stream (> = 10 kHz) more slowly, it works under another slow stream (<= 1 kHz). Class member variables are protected by critical sections in slow functions, since both use the same class member variables. This reason, which causes slow functions in critical functions, will slow down overall system performance. For this reason, I would like to find all these functions automatically, and not manually.

Thanks....

+7
source share
4 answers

Obtaining a definition of compilation time other than that proposed by Nicholas Wilson will be extremely difficult, if not impossible, but assuming that the "background" really refers to functions, and not to multiple threads (I did not see any mention of threads in the question, so I assume that this is just a weird wording), you can trivially use the global flag and the locker, as well as assert or throw an exception. Or display a debug message. Of course, this will only be the lead time, but you must isolate the criminals very quickly. It will also have very little overhead for debugging collections (it is almost guaranteed to work from the L1 cache), but is not created for releases.

Using CaptureStackBackTrace , you must catch the address of the intruder function, which tool like addr2line (or any other MS equivalent) can be directly translated to a line in your code. There is probably even an instrumental help function that can directly execute this translation (although I don’t know).

So something like this (untested!) Might do the trick:

 namespace global { int slow_flag = 0; } struct slow_func_locker { slow_func_locker() { ++global::slow_flag; } ~slow_func_locker(){ --global::slow_flag; } }; #indef NDEBUG #define REALTIME if(global::slow_flag) \ { \ void* backtrace; \ CaptureStackBackTrace(0, 1, &backtrace, 0); \ printf("RT function %s called from %08x\n", __FUNCTION__, backtrace); \ } #define SLOW_FUNC slow_func_locker slow_func_locker_; #else #define REALTIME #define SLOW_FUNC #endif foo_class::some_realtime_function(...) { REALTIME; //... }; foo_class::some_slow_function(...) { SLOW_FUNC; //... some_realtime_function(blah); // this will trigger }; 

The only real drawback (besides compilation time) is that you should mark every slow and real function with any marker, but since the compiler cannot magically know what it is, there is no choice anyway.

Note that the global “flag” is indeed a counter, not a flag. The reason for this is that a slow function can immediately call another slow function that returns and clears the flag - incorrectly setting the fast function now (the critical section approach suggested by xgbi might be blocked in this case!). A counter prevents this. If you have threads, you can also replace int with std::atomic_int .

EDIT:
Since it is now clear that there really are 2 threads , and it matters only that one of them (the “fast” thread) never calls the “slow” function, there is another simple one (an example using the Win32 API, but can be executed with POSIX anyway):

When a “fast” stream starts (a “slow” stream should not do this), save the stream identifier somewhere, either as a global variable or as a member of an object that contains all the fast / slow functions — anywhere:

 global::fast_thread_id = GetCurrentThreadId(); 

A macro to make "unwanted" function calls might look like this:

 #define CHECK_FAST_THREAD assert(GetCurrentThreadID() != global::fast_thread_id) 

This macro is then added to any “slow” function that should never be called from a “fast” thread. If the fast thread calls a function that it should not call, the assert triggers and it is known which function is called.

+3
source

You need to use the linker. Separate the "real" and slow functions into two modules and connect them in the correct order.

For example, split the files into two directories. Create a library from each directory (work with the object files), then link your final application using:

 c++ -o myapp main.o lib1/slowfns.a lib2/realtime.a 

If you try to call something from slowfns.a in realtime.a, depending on the compiler, it will not be able to reference (some compilers may require options for enforcement).

In addition, it makes it easy to manage compile-time declarations: make sure that headers from the slowfns library are not included in the inclusion path when compiling the library of real-time functions for added protection.

+4
source

I don’t know how to do this at compile time, but perhaps use mutexes for execution?

 static Mutex critical_mutex; #define CALL_SLOW( f ) if( critical_mutex.try_lock() == FAIL) \ printf("SLOW FUNCTION " #f" called while in CRITICAL\n");\ f #define ENTER_CRITICAL() critical_mutex.lock() #define EXIT_CRITICAL() critical_mutex.unlock() 

Whenever you use a slow function while in a critical section, trylock does not work.

 void slow_func(){ } ENTER_CRITICAL(); CALL_SLOW( slow_func() ); EXIT_CRITICAL(); 

It will be printed:

 SLOW FUNCTION slow_func() called while in CRITICAL 

If you need speed, you can implement your lightweight mutex with interlockedincrement in windows or __ sync * on linux.
Preshing has an amazing set of blog posts about it HERE .

+3
source

If you can freely change the code as you see fit, there is a system-level type solution that involves adding some kind of template.

Basically, you create a new SlowFunctionToken class. Each slow function in your program references a SlowFunctionToken. You will then create the default private SlowFunctionToken constructors and copies.

Now only functions that already have a SlowFunctionToken can call slow functions. How do you get a SlowFunctionToken? Add friend announcements to SlowFunctionToken; in particular, attach stream input functions to streams that are allowed to use slow functions. Then create local SlowFunctionToken objects and skip them.

 class SlowFunctionToken; class Stuff { public: void FastThread(); void SlowThread(); void ASlowFunction(SlowFunctionToken& sft); void AnotherSlowFunction(SlowFunctionToken& sft); void AFastFunction(); }; class SlowFunctionToken { SlowFunctionToken() {} SlowFunctionToken(const SlowFunctionToken&) {} friend void Stuff::SlowThread(); }; void Stuff::FastThread() { AFastFunction(); //SlowFunctionToken sft; doesn't compile //ASlowFunction(???); doesn't compile } void Stuff::SlowThread() { SlowFunctionToken sft; ASlowFunction(sft); } void Stuff::ASlowFunction(SlowFunctionToken& sft) { AnotherSlowFunction(sft); AFastFunction(); // works, but that function can't call slow functions } 
+1
source

All Articles