2016-07-03 18 views
2

In dem Projekt, an dem ich gerade arbeite, haben wir uns darauf geeinigt, nur Funktionen zu verwenden, die Fehlercodes zurückgeben (keine Ausnahmen), um Fehler zu behandeln.Instrumentierung bei Argumentwert

Um meinen Code nicht mit Debug-Meldungen zu "verschmutzen", arbeitete ich an einer Lösung basierend auf Instrumentierung (inspiriert von this post).

Statt Ints als Fehlercode zu verwenden, ich bin Einkapseln es in einer Klasse wie:

// File rc.h 
class rc 
{ 
public: 
    rc(int) __attribute__((no_instrument_function)); 
    rc& operator=(rc); 
private: 
    int value; 
}; 

Dann diese Operatoren definieren und die Instrumentierung Funktionen wie:

// File rc.cc 
#include <stdio.h> 
#include <time.h> 
#include "rc.h" 

static FILE *fp_trace; 
static int isError;  

extern "C" 
{ 

    void __attribute__ ((constructor)) trace_begin (void) 
    { 
     fp_trace = fopen("trace.out", "w"); 
     isError = 0; 
    } 

    void __attribute__ ((destructor)) trace_end (void) 
    { 
     if(fp_trace != NULL) { 
     fclose(fp_trace); 
     } 
    } 

    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) 
    { 
    } 

    void __cyg_profile_func_exit (void *func, void *caller) 
    { 
    if ((fp_trace != NULL) && (isError == 1)) { 
     fprintf(fp_trace, "x %p %p %lu\n", func, caller, time(NULL)); 
     isError = 0; 
    } 
    } 
} 

rc::rc(int valueIn) : 
    value(valueIn) 
{ 
} 

rc& rc::operator=(rc rcIn) 
{ 
    value = rcIn.value; 
    if (value != 0) 
    { 
     isError = 1; 
     fprintf(fp_trace, "%d\n", value); 
    } 
    return *this; 
} 

jedoch in Um nicht zu viele Dinge zu drucken, möchte ich keine Funktionsaufrufe protokollieren, die den Rückgabecode 0 zurückgeben, also das isError Flag.

#include <cstdlib> 
#include <ctime> 
#include "rc.h" 

rc bar(void) 
{ 
    rc result(std::rand() % 3); 
    return result; 
} 

int main(void) 
{ 
    std::srand (std::time(NULL)); 
    rc returnCode(0); 
    for (int i=0; i<10; ++i) 
    { 
    returnCode = bar(); 
    } 
    return 0; 
} 

Compile und laufen mit

g++ -finstrument-functions -g -c -o rc.o rc.cc 
g++ -g -c -o test.o test.cc 
g++ test.o rc.o -o a.out 
./a.out 

Lesen der Ausgabe mit dem in the previously mentioned post wie in etwas führt gegeben Skript:

Error code 2 
rc::operator=(rc) at 2016-07-03T18:32:09+0200, called from main (test.cc:32) 

Dies kann mit dem folgenden Beispiel verwendet werden,

Das ist fast, was ich mir gewünscht hätte. Aber im Vergleich zu einer Lösung, wo ich einfach einen Test in rc foo(void) hinzufügen würde, um zu testen, ob die Ausgabe ungleich Null ist und dann den Fehler protokollieren, wird nur Overhead hinzugefügt (plus den Overhead aufgrund der Überprüfung), falls ein Fehler auftritt (hoffentlich nicht zu oft), werde ich Overhead aufgrund der Instrumentierung hinzufügen (plus den möglichen Overhead aufgrund der RC-Wrapper, aber ich bin damit OK) bei jedem Anruf ...

Gibt es eine Lösung, die ich nicht könnte Ich dachte nicht dran, das würde das operator= in dem Fall nicht instrumentieren, wo das Argument Null ist? Da rc::value nur zur Laufzeit bekannt ist, glaube ich nicht, dass eine Vorlage mit Argument den Wert hätte, der sich auf value=0 spezialisieren würde und würde das Instrument nicht funktionieren. Würde es?

Es gibt eine zusätzliche Einschränkung: Es ist wichtig, dass die instrumentierte Funktion der Zuweisungsoperator ist, da ich den Aufrufer kennen möchte, daher kann ich keine zusätzliche Proxy-Ebene hinzufügen.


bearbeiten auf diesen letzten Punkt habe ich zur Herstellung der Zuweisungsoperator Inline gedacht (und nicht instrumentiert) und rufen Sie den Return-Code eine instrumentierte Funktion in dem Fall nicht Null ist, aber das kann nicht arbeiten wie finstrument-function doc page besagt, dass:

Diese Instrumentierung ist auch für Funktionen in anderen Funktionen inline erweitert. Die Profilierungsaufrufe zeigen an, wo die Inline-Funktion konzeptionell eingegeben und beendet wird.

So dass es nicht auf den echten Anrufer zeigte.

+0

Sie wissen, dass Sie wahrscheinlich trotzdem mit Ausnahmen in Ihrem Programm enden werden, es sei denn, Sie schreiben den gesamten Code von Grund auf neu und verwenden niemals C++ - Bibliotheken? –

+0

Ja, ich bin mir dessen bewusst. Aber wir schreiben eine Menge Dinge um ... – Vser

+0

Ein guter Tipp ist die Verwendung negativer Zahlen als Fehlercodes und positiver Werte für die Ergebnisse (0 enthalten). Das ist, was Linux-Systemaufrufe tun. –

Antwort

0

Die Lösung beruht nicht auf der Instrumentierung, sondern auf der __builtin_return_address(unsigned int)-Funktion, die von der Instrumentierung verwendet wird.

Mit dieser Funktion kann die Rückgabezeiger des Anrufers erhalten:

rc& rc::operator=(rc rcIn) 
{ 
    value = rcIn.value; 
    if (value != 0) 
    { 
     fprintf(fp_trace, "%d %p\n", value, __builtin_return_address(1)); 
    } 
    return *this; 
} 

Auf diese Weise brauchen Sie nicht auf Instrumentierung verlassen, um die Anrufer-Adresse zu erhalten und einen wirklich wenig Aufwand hinzufügen über mit einfach ein int.