vscode visual tutorial studio para online for debug configurar compile compilar code beginners c++ debugging

c++ - visual - debug watch



¿Cómo encuentro el nombre de la función de llamada? (6)

A menos que haya más en la pregunta de la que usted formuló explícitamente, simplemente cambie el nombre de la función y permita que el compilador / vinculador le indique dónde se llama.

He estado utilizando PRETTY_FUNCTION para dar salida al nombre de la función actual; sin embargo, he vuelto a implementar algunas funciones y me gustaría saber qué funciones las están llamando.

En C ++, ¿cómo puedo obtener el nombre de la función de la rutina de llamada?


Aquí hay dos opciones:

  1. Puede obtener una pila completa (incluido el nombre, el módulo y el desplazamiento de la función de llamada) con versiones recientes de glibc con las funciones de rastreo de GNU . Vea mi respuesta aquí para más detalles. Esta es probablemente la cosa más fácil.

  2. Si eso no es exactamente lo que estás buscando, entonces puedes probar libunwind , pero va a requerir más trabajo.

Tenga en cuenta que esto no es algo que pueda conocer de forma estática (como con PRETTY_FUNCTION); en realidad tienes que caminar la pila para descubrir qué función te llamó. Por lo tanto, esto no es algo que valga la pena hacer en los archivos de depuración ordinarios. Sin embargo, si desea realizar depuraciones o análisis más serios, esto podría ser útil para usted.


Aquí hay una solución que puedes usar a menudo. Tiene la ventaja de no requerir cambios en el código de la función real ( no se pueden agregar llamadas a las funciones de apilamiento, cambiar los parámetros para pasar los nombres de las funciones o vincular las bibliotecas adicionales ). Para que funcione, solo necesitas usar un poco de magia de preprocesador:

Ejemplo simple

// orignal function name was ''FunctionName'' void FunctionNameReal(...) { // Do Something } #undef FunctionName #define FunctionName printf("Calling FunctionName from %s/n",__FUNCTION__);FunctionNameReal

Debe cambiar el nombre de su función temporalmente, pero consulte la nota a continuación para obtener más sugerencias. Esto dará como resultado una instrucción printf() en cada punto de invocación de la función. Obviamente, debe hacer algunos arreglos si llama a una función miembro o necesita capturar el valor devuelto ( Como pasar la llamada a la función y __FUNCTION__ a una función personalizada que devuelve el mismo tipo ... ), pero la técnica básica es lo mismo. Es posible que desee utilizar __LINE__ y __FILE__ o algunas otras macros de preprocesador según el compilador que tenga. (Este ejemplo es específicamente para MS VC ++, pero probablemente funcione en otros).

Además, es posible que desee poner algo como esto en su encabezado rodeado de guardias #ifdef para activarlo de manera condicional, que también puede manejar el cambio de nombre de la función real para usted.

ACTUALIZACIÓN [2012-06-21]

Recibí una solicitud para expandir mi respuesta. Como resultado, mi ejemplo anterior es un poco simplista. Aquí hay algunos ejemplos completos de cómo manejar esto, usando C ++.

Ejemplo de fuente completa con un valor de retorno

Usar una class con operator() hace que esto sea bastante directo. Esta primera técnica funciona para funciones independientes con y sin valores de retorno. operator() solo necesita reflejar el mismo retorno que la función en cuestión, y tener argumentos que coincidan.

Puede compilar esto con g++ -o test test.cpp para una versión que no informa y g++ -o test test.cpp -DREPORT para una versión que muestra la información de la persona que llama.

#include <iostream> int FunctionName(int one, int two) { static int calls=0; return (++calls+one)*two; } #ifdef REPORT // class to capture the caller and print it. class Reporter { public: Reporter(std::string Caller, std::string File, int Line) : caller_(Caller) , file_(File) , line_(Line) {} int operator()(int one, int two) { std::cout << "Reporter: FunctionName() is being called by " << caller_ << "() in " << file_ << ":" << line_ << std::endl; // can use the original name here, as it is still defined return FunctionName(one,two); } private: std::string caller_; std::string file_; int line_; }; // remove the symbol for the function, then define a new version that instead // creates a stack temporary instance of Reporter initialized with the caller # undef FunctionName # define FunctionName Reporter(__FUNCTION__,__FILE__,__LINE__) #endif void Caller1() { int val = FunctionName(7,9); // <-- works for captured return value std::cout << "Mystery Function got " << val << std::endl; } void Caller2() { // Works for inline as well. std::cout << "Mystery Function got " << FunctionName(11,13) << std::endl; } int main(int argc, char** argv) { Caller1(); Caller2(); return 0; }

Muestra de salida (Informes)

Reporter: FunctionName() is being called by Caller1() in test.cpp:44 Mystery Function got 72 Reporter: FunctionName() is being called by Caller2() in test.cpp:51 Mystery Function got 169

Básicamente, en cualquier lugar que ocurra FunctionName , lo reemplaza con Reporter(__FUNCTION__,__FILE__,__LINE__) , cuyo efecto neto es que el preprocesador escriba algún objeto instancing con una llamada inmediata a la función operator() . Puede ver el resultado (en gcc) de las sustituciones del preprocesador con g++ -E -DREPORT test.cpp . Caller2 () se convierte en esto:

void Caller2() { std::cout << "Mystery Function got " << Reporter(__FUNCTION__,"test.cpp",51)(11,13) << std::endl; }

Puede ver que __LINE__ y __FILE__ han sido sustituidos. (No estoy seguro de por qué __FUNCTION__ todavía se muestra en el resultado para ser honesto, pero la versión compilada informa la función correcta, por lo que probablemente tenga algo que ver con el preprocesamiento de múltiples pasadas o un error de gcc).

Ejemplo de fuente completa con una función de miembro de clase

Esto es un poco más complicado, pero muy similar al ejemplo anterior. En lugar de simplemente reemplazar la llamada a la función, también estamos reemplazando la clase.

Al igual que en el ejemplo anterior, puede compilar esto con g++ -o test test.cpp para una versión que no informa y g++ -o test test.cpp -DREPORT para una versión que muestra la información de la persona que llama.

#include <iostream> class ClassName { public: explicit ClassName(int Member) : member_(Member) {} int FunctionName(int one, int two) { return (++member_+one)*two; } private: int member_; }; #ifdef REPORT // class to capture the caller and print it. class ClassNameDecorator { public: ClassNameDecorator( int Member) : className_(Member) {} ClassNameDecorator& FunctionName(std::string Caller, std::string File, int Line) { std::cout << "Reporter: ClassName::FunctionName() is being called by " << Caller << "() in " << File << ":" << Line << std::endl; return *this; } int operator()(int one, int two) { return className_.FunctionName(one,two); } private: ClassName className_; }; // remove the symbol for the function, then define a new version that instead // creates a stack temporary instance of ClassNameDecorator. // FunctionName is then replaced with a version that takes the caller information // and uses Method Chaining to allow operator() to be invoked with the original // parameters. # undef ClassName # define ClassName ClassNameDecorator # undef FunctionName # define FunctionName FunctionName(__FUNCTION__,__FILE__,__LINE__) #endif void Caller1() { ClassName foo(21); int val = foo.FunctionName(7,9); // <-- works for captured return value std::cout << "Mystery Function got " << val << std::endl; } void Caller2() { ClassName foo(42); // Works for inline as well. std::cout << "Mystery Function got " << foo.FunctionName(11,13) << std::endl; } int main(int argc, char** argv) { Caller1(); Caller2(); return 0; }

Aquí hay un resultado de muestra:

Reporter: ClassName::FunctionName() is being called by Caller1() in test.cpp:56 Mystery Function got 261 Reporter: ClassName::FunctionName() is being called by Caller2() in test.cpp:64 Mystery Function got 702

Los puntos altos de esta versión son una clase que decora la clase original y una función de reemplazo que devuelve una referencia a la instancia de la clase, lo que permite que el operator() haga la llamada a la función real.

Espero que ayude a alguien!


Con la versión GCC ≥ 4.8, puede usar __builtin_FUNCTION , que no debe confundirse con __FUNCTION__ y similar, parece ser un poco oscuro.

Ejemplo:

#include <cstdio> void foobar(const char* str = __builtin_FUNCTION()){ std::printf("called by %s/n", str); } int main(){ foobar(); return 0; }

salida:

called by main

ejemplo en WandBox


En la primera aproximación, solo grep la base de código para los nombres de las funciones. Luego viene Doxygen, y luego el registro dinámico (ambos discutidos por otros).


Probablemente desee los nombres de todas las funciones que potencialmente podrían llamarlos. Esto es básicamente un conjunto de bordes en el gráfico de llamadas. Doxygen puede generar el gráfico de llamadas, y luego es simplemente una cuestión de mirar los bordes entrantes de su nodo de funciones.