Skip to content

Commit

Permalink
common: Added CallTrace class and CALL_TRACE macro that generates fun…
Browse files Browse the repository at this point in the history
…ction call traces.
  • Loading branch information
levy committed Feb 23, 2024
1 parent a73fe55 commit bde5738
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 0 deletions.
105 changes: 105 additions & 0 deletions src/inet/common/CallTrace.cc
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

63 changes: 63 additions & 0 deletions src/inet/common/CallTrace.h
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

0 comments on commit bde5738

Please sign in to comment.