-
Notifications
You must be signed in to change notification settings - Fork 491
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
common: Added CallTrace class and CALL_TRACE macro that generates fun…
…ction call traces.
- Loading branch information
Showing
2 changed files
with
168 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
// | ||
// Copyright (C) 2024 OpenSim Ltd. | ||
// | ||
// SPDX-License-Identifier: LGPL-3.0-or-later | ||
// | ||
|
||
|
||
#include "inet/common/CallTrace.h" | ||
|
||
#ifdef __GNUC__ | ||
|
||
#include <cxxabi.h> | ||
#include <dlfcn.h> | ||
|
||
namespace inet { | ||
|
||
CallTrace::State CallTrace::state; | ||
|
||
CallTrace::CallTrace(const char *name, int maxLevel, const char *filter) | ||
{ | ||
state.enabled = true; | ||
state.level = 1; | ||
state.name = name; | ||
state.maxLevel = maxLevel; | ||
state.filter = std::regex(filter); | ||
printf("TRACE -> %s\n", state.name); | ||
} | ||
|
||
CallTrace::~CallTrace() | ||
{ | ||
printf("TRACE <- %s\n", state.name); | ||
state.enabled = false; | ||
state.level = -1; | ||
state.name = nullptr; | ||
state.maxLevel = -1; | ||
state.filter = std::regex(); | ||
} | ||
|
||
std::string CallTrace::demangle(const char* mangledName) { | ||
int status = -1; | ||
// The abi::__cxa_demangle function allocates memory for the demangled name using malloc, | ||
// so we use a unique_ptr with a custom deleter to ensure that it gets freed. | ||
std::unique_ptr<char, void(*)(void*)> res{ | ||
abi::__cxa_demangle(mangledName, NULL, NULL, &status), | ||
std::free | ||
}; | ||
return (status == 0) ? res.get() : "Error demangling name"; | ||
} | ||
|
||
} // namespace inet | ||
|
||
extern "C" | ||
{ | ||
void __cyg_profile_func_enter(void* func, void* caller) __attribute__((no_instrument_function)); | ||
void __cyg_profile_func_exit(void* func, void* caller) __attribute__((no_instrument_function)); | ||
} | ||
|
||
void __cyg_profile_func_enter(void* func, void* caller) | ||
{ | ||
auto& state = inet::CallTrace::state; | ||
if (state.enabled) { | ||
state.enabled = false; | ||
if (1 <= state.level && state.level <= state.maxLevel) { | ||
Dl_info info; | ||
if (dladdr(func, &info)) { | ||
std::string functionName = inet::CallTrace::demangle(info.dli_sname); | ||
if (functionName != "CallTrace::~CallTrace()" && | ||
std::regex_match(functionName, state.filter)) | ||
{ | ||
printf("TRACE %s-> %s\n", | ||
std::string(state.level * 2, ' ').c_str(), | ||
info.dli_sname ? functionName.c_str() : "?"); | ||
state.level++; | ||
} | ||
} | ||
} | ||
state.enabled = true; | ||
} | ||
} | ||
|
||
void __cyg_profile_func_exit(void* func, void* caller) | ||
{ | ||
auto& state = inet::CallTrace::state; | ||
if (state.enabled) { | ||
state.enabled = false; | ||
if (1 <= state.level && state.level <= state.maxLevel) { | ||
Dl_info info; | ||
if (dladdr(func, &info)) { | ||
std::string functionName = inet::CallTrace::demangle(info.dli_sname); | ||
if (functionName != "CallTrace::~CallTrace()" && | ||
std::regex_match(functionName, state.filter)) | ||
{ | ||
state.level--; | ||
printf("TRACE %s<- %s\n", | ||
std::string(state.level * 2, ' ').c_str(), | ||
info.dli_sname ? functionName.c_str() : "?"); | ||
} | ||
} | ||
} | ||
state.enabled = true; | ||
} | ||
} | ||
|
||
#endif | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// | ||
// Copyright (C) 2024 OpenSim Ltd. | ||
// | ||
// SPDX-License-Identifier: LGPL-3.0-or-later | ||
// | ||
|
||
|
||
#ifndef __INET_CALLTRACE_H | ||
#define __INET_CALLTRACE_H | ||
|
||
#ifdef __GNUC__ | ||
|
||
#include "inet/common/INETDefs.h" | ||
|
||
#include <regex> | ||
|
||
namespace inet { | ||
|
||
/** | ||
* This class supports generating function call traces on the standard output. | ||
* For example, CALL_TRACE(10, "inet::.*") will produce a function call trace | ||
* for a maximum depth limit of 10 levels including only INET functions. | ||
* Here is another example focusing on certain classes and ignoring some functions: | ||
* CALL_TRACE(20, "^inet::(ScenarioManager|LifecycleController|NodeStatus|NetworkInterface|InterfaceTable|Ipv4RoutingTable|Ipv4NetworkConfigurator|Ipv4NodeConfigurator|EthernetMac|EthernetMacBase)::(?!(get|is|has|compute)).*"); | ||
* The output contains one line for entering and one line for leaving a function. | ||
* | ||
* Important: compile INET using GCC with '-finstrument-functions' into an executable. | ||
*/ | ||
class INET_API CallTrace | ||
{ | ||
public: | ||
struct State | ||
{ | ||
public: | ||
bool enabled = false; | ||
int level = -1; | ||
|
||
const char *name = nullptr; | ||
int maxLevel = -1; | ||
std::regex filter; | ||
}; | ||
|
||
public: | ||
static State state; | ||
|
||
public: | ||
CallTrace(const char *name, int maxLevel, const char *filter); | ||
~CallTrace(); | ||
|
||
static std::string demangle(const char* mangledName); | ||
}; | ||
|
||
#define CALL_TRACE(maxLevel, filter) std::string name = CallTrace::demangle(typeid(*this).name()) + "::" + __func__ + "()"; CallTrace callTrace(name.c_str(), maxLevel, filter) | ||
|
||
} // namespace inet | ||
|
||
#else | ||
|
||
#define CALL_TRACE(maxLevel, filter) | ||
|
||
#endif | ||
|
||
#endif |