Forum: PC-Programmierung Wie kann ich die addresse der gerade ausgeführten methode ermitteln (C++)


von Alex2.2 (Gast)


Lesenswert?

Vorab: Ich habe es mit einem embbeded system zu tun, dass wir in C++ 
programmieren wollen. Damit haben wir beschränkten Speicher, keine 
Festplatte und im echten Betrieb auch kein Dumpterminal oder 
Dergleichen. Angeschlossen an die Cpu ist nur ein Flashspeicher, auf dem 
seriell Datenrecords gespeichert werden. Nun - Leider ist es ja bislang 
kaum jemanden gelungen, Programme absolut absturzsicher zu schreiben. 
Ich habe mir aber überlegt im Falle eines Abstruzes (Watchdog reset, 
etc) zumindest eine kurze Nachricht zurückzulassen. Dafür habe ich einen 
kleinen nicht initialisierten RAM - Bereich vorgesehen. In diesem sollen 
pointer auf Instanz und die lezte ausgeführte Methode und optional ein 
darin enthaltener Watchpoint sowie gegebenenfalls relevante 8, 16,32 bit 
Daten gespeichert werden.

und nun mein Problem:
Wie kann ich auf möglich einfache Weise die addresse einer gerade 
ausgeführten Methode ermitteln?

für konstruktive Beiträge schonmal vorab danke

von Sebastian V. (sebi_s)


Lesenswert?

Dafür gibt es keine allgemeine Lösung in C++. Wenn du uns mitteilst um 
welche spezielle Hardware es geht, kann man vielleicht dafür eine Lösung 
finden. Einige Mikrocontroller sichern den PC im Fehlerfall und springen 
einen entsprechenden Interrupt an. Dort könnte man den PC und andere 
Infos in den RAM schreiben um diese später auszuwerten.

von Georg (Gast)


Lesenswert?

Alex2.2 schrieb:
> Wie kann ich auf möglich einfache Weise die addresse einer gerade
> ausgeführten Methode ermitteln?

Willst du die denn wirklich wissen? Nicht vielmehr welche Methode?

Eine mühsame Möglichkeit besteht darin, zu Anfang jeder Funktion bzw. 
Methode eine Debug-Variable auf einen bestimmten Wert zu setzen (und 
dafür zu sorgen, dass das nicht wegoptimiert wird).

Georg

von ffff (Gast)


Lesenswert?

am einfachsten wäre wohl:

- eine globale, statische variable
- ein Header file, wobei du dir folgende Makros definierst:
 - #MAIN_FUNC 0
   #IWAS_FUNC MAIN_FUNC + 1
...

in jedem Funktionsaufruf schreibst dann in die statische Variable einen 
zugehörigen Wert. Allerdings haut dir dein Watchdog-Reset auch die 
statische Variable weg, weswegen du entweder irgendwie externen RAM 
brauchst oder ein EEPROM...

von Programmierer (Gast)


Lesenswert?

Speichere doch einfach den aktuellen Program Counter. Mit dem 
Disassembly kommt man so leicht an die Funktion und sogar die genaue 
Stelle.

von (prx) A. K. (prx)


Lesenswert?

Wenns der GCC ist, oder mindestens C99, dann schau dich hier um:
https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html

von Klaus W. (mfgkw)


Lesenswert?

Wenn du weisst, in welcher methode du bist (z.B. getInfo()), dann kannst 
du natürlich auch mit dem Adressoperator die Adresse holen (also 
&getInfo beispielsweise).

PS: für die aktuelle Funktion hat der gcc wiederum ein Makro, 
wahrscheinlich siehe Link oben von A.K. oder in der Nähe davon.

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Falls GCC:
1
__builtin_return_address (0)

https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html

Das liefert nicht die Adresse der Funktion, sondern die Stelle, von der 
aus die (effektive) Funktion aufgerufen wurde.  (bzw. die Adresse nach 
dem CALL).  Es liefert also sogar mehr Info als gewünscht, da der Callee 
einfach aus der Call-Site ermittelt werden kann.

"effektiv" bedeutet nach Inlining und Function Cloning und nur modulo 
Tail-Calling.

Generell ist die Anschaffung von Debug-Hardware engezeigt, z.B. mit OCDS 
o.ä.

https://de.wikipedia.org/wiki/OCDS

Nur so kann nicht-invasiv debuggt werden.  printf, cout und Konsorten zu 
verwenden ist einerseits invasiv und andererseits kein Debugging, 
sondern Logging.

von Sebastian V. (sebi_s)


Lesenswert?

Klaus W. schrieb:
> PS: für die aktuelle Funktion hat der gcc wiederum ein Makro,
> wahrscheinlich siehe Link oben von A.K. oder in der Nähe davon.

Wollte ich auch erst vorschlagen. Es gibt ein _FUNCTION_ (wie krieg 
ich das Unterstrichene hier weg?) Macro das den Namen der aktuellen 
Funktion enthält. Blöderweise sind da Anführungszeichen drumherum, wenn 
man also &__FUNCTION__ schreibt kriegt man die Adresse des Strings wo 
der Name der Funktion drin steht. Wobei das auch ganz praktisch sein 
könnte.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Sebastian V. schrieb:
> Es gibt ein FUNCTION (wie krieg
> ich das Unterstrichene hier weg?)

In Code-Tags einschließen:
1
__FUNCTION__
oder kursiv machen, da wohl mehrere Formatierungen gleichzeitig nicht 
unterstützt werden:
__FUNCTION__

Sebastian V. schrieb:
> Blöderweise sind da Anführungszeichen drumherum, wenn
> man also &__FUNCTION__ schreibt kriegt man die Adresse des Strings wo
> der Name der Funktion drin steht.

Naja, was heißt Anführungszeichen? __FUNCTION__ ist eine 
Stringvariable. In C++ mit gcc würde sich übrigens __PRETTY_FUNCTION__ 
anbieten. Das enthält dann nicht nur den reinen Namen, sondern die ganze 
Signatur der Funktion.

: Bearbeitet durch User
von Konrad S. (maybee)


Lesenswert?

Beim gcc wäre evtl. auch -finstrument-functions und Verwandtschaft eine 
Überlegung wert.
https://gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc/Code-Gen-Options.html
Aber sowas verwende ich eigentlich nur auf ausgewachsenen Rechnern und 
zu sehr speziellen Zwecken während der Entwicklung, nie für Software, 
die in den produktiven Einsatz geht.

von Alex2.2 (Gast)


Lesenswert?

Zunächst einmal danke. Ich hatte das bereits befürchtet. Ich würde 
wirklich gerne die Addresse wissen und nicht marker of den _FUNCTION_ 
string. Es handelt sich um einen Renesas RX, der durchaus informationen 
über Resetursachen und vieles mehr anbietet. Geschrieben wird mit IAR 
C/C++ für RX Prozessoren. Eure genannten Möglichkeiten hatte ich auch 
bereits in Erwägung gezogen, bin aber nicht wirklich glücklich damit. 
Diese Firmware wird doch bereits sehr komplex aber soll mit Sicherheit 
nicht auf einem OS laufen.

nochmals danke

von Sebastian V. (sebi_s)


Lesenswert?

Rolf M. schrieb:
> Naja, was heißt Anführungszeichen? _FUNCTION_ ist eine
> Stringvariable.

Zu einer Variable wird es aber erst wenn der Compiler drüber läuft. Der 
Präprozessor macht ja erstmal nur eine Textersetzung.

Alex2.2 schrieb:
> Ich würde wirklich gerne die Addresse wissen

Dazu fällt mir noch mindestens ein Problem ein. Du sagtest, dass du C++ 
benutzt und dann willst du sicher auch die Adressen von 
Memberfunktionen. Allerdings sind Pointer auf Memberfunktionen (PMF) 
echt komische Tiere. Ich habe gerade mal in VS ausprobiert einen PMF zu 
einem void* zu konvertieren. Lässt mich der Compiler nicht machen, weder 
mit C-style cast noch reinterpret_cast.

von Klaus W. (mfgkw)


Lesenswert?

Liefert backtrace() nicht letztlich alles, was du brauchst?
http://www.linuxjournal.com/article/6391

von Alex2.2 (Gast)


Lesenswert?

Klaus W. schrieb:
> Liefert backtrace() nicht letztlich alles, was du brauchst?

gibt's bei IAR nicht. Zu dumm aber das wär's natürlich.

Nun gut ich habe mir da etwas zusammengebaut in Richtung "hole dir den 
Inhalt des PC und sichere den erstmal. Da geht bei IAR dann so:
1
#define __getCurrentAddress(aPtr)\
2
  register u32 aPtr;\
3
  asm("mvfc pc, %[ptr] \n"\
4
  : [ptr]"=r"(aPtr));
und innhalb jeder Methode dann
1
void aClass::aMethode(void)
2
{
3
   __getCurrentAddress(aPtr);
4
   Euroscan::system::watchdog.enterMethod(this, aPtr);
5
   //do somthing
6
   Euroscan::system::watchdog.leaveMethod(this, aPtr);
7
}
diese Verfahren ist nicht exakt, da normalerweise noch ein bischen 
Compileroverhead in Form von push operationen, ...etc entsteht aber es 
ist sicher erstmal hilfreich um Firmwarefehler zu analysieren.

von Klaus W. (mfgkw)


Lesenswert?

Wolltest du nicht C++ machen? Und dann so ein Makro? :-)
Eine inline-Funktion mit demselben Inhalt würde doch netter aussehn...

von Alex2.2 (Gast)


Lesenswert?

Klaus W. schrieb:
> Wolltest du nicht C++ machen? Und dann so ein Makro? :-)
> Eine inline-Funktion mit demselben Inhalt würde doch netter aussehn...

stimmt du hast recht. Hab zuviel C gemacht. Daher wird das noch ein 
inline. ;-)

von Rolf M. (rmagnus)


Lesenswert?

Sebastian V. schrieb:
> Rolf M. schrieb:
>> Naja, was heißt Anführungszeichen? FUNCTION ist eine
>> Stringvariable.
>
> Zu einer Variable wird es aber erst wenn der Compiler drüber läuft. Der
> Präprozessor macht ja erstmal nur eine Textersetzung.

Der Präprozessor hat da gar nichts damit zu tun. Nochmal: __FUNCTION__ 
ist eine Variable, kein Präprozessor-Makro.
Siehe https://gcc.gnu.org/onlinedocs/gcc/Function-Names.html


> Allerdings sind Pointer auf Memberfunktionen (PMF) echt komische Tiere. Ich
> habe gerade mal in VS ausprobiert einen PMF zu einem void* zu konvertieren.
> Lässt mich der Compiler nicht machen, weder mit C-style cast noch
> reinterpret_cast.

Streng genommmen dürfte er das schon mit normalen Funktionszeigern 
nicht.

von Alex2.2 (Gast)


Lesenswert?

Rolf M. schrieb:
>> Allerdings sind Pointer auf Memberfunktionen (PMF) echt komische Tiere. Ich
>> habe gerade mal in VS ausprobiert einen PMF zu einem void* zu konvertieren.
>> Lässt mich der Compiler nicht machen, weder mit C-style cast noch
>> reinterpret_cast.
>
> Streng genommmen dürfte er das schon mit normalen Funktionszeigern
> nicht.

Darf man auch nicht und mit vielen Compilern, z. B. meinem geht das auch 
nicht, jedenfalls nicht ohne riskante tricks. Da ich eine stabile 
Firmware möchte (die lässt sich nähmlich nicht mal so eben upgraden) 
mache ich das auch nicht.

von StinkyWinky (Gast)


Lesenswert?

Alex2.2 schrieb:
>> Ich habe mir aber überlegt im Falle eines Abstruzes (Watchdog reset,
>> etc) zumindest eine kurze Nachricht zurückzulassen.

Sämtliche nützliche Informationen müssen VOR dem Absturz/WD-Reset 
gesichert worden sein.

von Alex2.2 (Gast)


Lesenswert?

StinkyWinky schrieb:
> Sämtliche nützliche Informationen müssen VOR dem Absturz/WD-Reset
> gesichert worden sein.

Ach - wirklich? Nun deswegen möchte ich ja auch VOR dem Absturz 
sicherstellen, dass ich an einer nicht initialisierten Stelle im 
Speicher zumindest ungefähr die Position speichere, an der sich die 
Programverarbeitung befand als es knallte.
Da ich nicht in der kompfotablen Lage bin, einen kompletten Stack auf 
einem nicht befindlichem Monitor anzuzeigen oder in einen auf einer 
nicht vorhanden Festplatte in einen Logfile abzulegen, muß ich es mit 
möglichst kleinen Notizen machen, die dann als Record zwischen den 
normalen Daten auf einen kleinen Datenflash geschrieben werden.
Diese kann man im Fehlerfalle oder bei Reclamationen auswerten. 
Deutlich?

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Hallo Alex2.2,

Sebastian V. schrieb:
> Dafür gibt es keine allgemeine Lösung in C++. Wenn du uns mitteilst um
> welche spezielle Hardware es geht, kann man vielleicht dafür eine Lösung
> finden. Einige Mikrocontroller sichern den PC im Fehlerfall und springen
> einen entsprechenden Interrupt an. Dort könnte man den PC und andere
> Infos in den RAM schreiben um diese später auszuwerten.

Das ist die einzige "vernünftige" Antwort die Du bekommen wirst.

Zuerst solltest Du mal gucken, was ein Absturz den wirklich ist. In der 
Regel läuft es immer auf eine hardware trap hinaus. Dort wird ein 
Interrupt Handler aufgerufen und in dem Moment liegt die Adresse, die Du 
suchst auf dem Stack. Dort musst Du sie nur noch abholen! Wo sie genau 
steht, solltest Du im Reference Manual Deiner CPU finden.

mfg Torsten

von Hans-Georg L. (h-g-l)


Lesenswert?


von Rolf M. (rmagnus)


Lesenswert?

Alex2.2 schrieb:
>> Streng genommmen dürfte er das schon mit normalen Funktionszeigern
>> nicht.
>
> Darf man auch nicht

Schön, daß du das nochmal wiederholst, falls jemand es bei mir nicht 
gelesen haben sollte...

> und mit vielen Compilern, z. B. meinem geht das auch nicht, jedenfalls
> nicht ohne riskante tricks.

Auf vielen anderen Compilern, vor allem auf dem PC, ist es aber Gang und 
Gäbe, weil recht grundlegende Funktionen das voraussetzen.

von db8fs (Gast)


Lesenswert?

Rolf M. schrieb:
>> und mit vielen Compilern, z. B. meinem geht das auch nicht, jedenfalls
>> nicht ohne riskante tricks.
>
> Auf vielen anderen Compilern, vor allem auf dem PC, ist es aber Gang und
> Gäbe, weil recht grundlegende Funktionen das voraussetzen.

Wird z.B. für Signal-Handler-Bindung (Delegates) in boost::bind() oder 
auch QT genutzt. Und da ist das schon 'ne feine Sache für solche 
Objekt-Request-Broker-Geschichten.

Aber es ist schon wahr, das Handling von Member-Funktionspointern ist 
aufgrund der Typbindung an die Klasse sicher kein triviales 
Einsteigerthema und erfordert schon etwas Verständnis, wie das 
C++-Objektmodell und das Typensystem funktioniert.
Ich erwisch mich da auch öfters mal damit, das wenn ich das für 'ne 
Objektbindung für eine bestimmte Lösung mal brauche, wieder 
nachzuschlagen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.