Hallo,
zu Optimierungszwecken versuche ich herauszufinden, ob eine bestimmte
Variable abc in meinem Code irgendwann mal gelesen wird. Also, es kann
ja sein, dass die Variable nur geschrieben wird - das wäre in Ordnung
und interessiert mich nicht. Mich interessieren die Lesezugriffe.
Der Code wird generiert und ist sehr umfangreich, händische Prüfung
scheidet da leider aus. (Gibt ja auch Zeiger auf die Variable,
Funktionsübergaben, structs und arrays die indirekt auf die Variable
zugreifen etc etc). Debugging ist auch nicht möglich.
Daher dachte ich: Der Compiler kann ja zumindest Warnungen anzeigen,
wenn eine Variable nicht benutzt wird. Dazu muss er ja auch die Zugriffe
prüfen. Kann ich mir beim Compilieren irgend ein Output-file erzeugen,
dessen Informationen mir bei meinem Problem helfen?
Oder hat jemand vll eien ganz andere Idee, mein Problem zu lösen?
Schnapphase schrieb:> Daher dachte ich: Der Compiler kann ja zumindest Warnungen anzeigen,> wenn eine Variable nicht benutzt wird. Dazu muss er ja auch die Zugriffe> prüfen. Kann ich mir beim Compilieren irgend ein Output-file erzeugen,> dessen Informationen mir bei meinem Problem helfen?
Ich unterstelle, daß es sich um
1. AVR-8-Bit Prozessoren,
2. die Programmiersprache "C",
3. den Compiler gcc unter Windows
handelt.
Die .lss Datei (?) - erzeugbar per Checkbox in den Compileroptionen -
enthält genau diese Informationen: Suchen auf den Variablennamen in der
Datei, Art des Zugriffs prüfen, fertig.
Für globale Daten ist die .map Datei ebenfalls brauchbar.
Bernhard
Hi,
fast richtig - geht aber um TriCore ;) Sonst passt's, gcc unter Windows
und ja, C. Sorry, hätte ich noch erwähnen können.
Die .map Datei spuckt er mir auch aus, da sehe ich aber nur, in welchen
Objects die Variable verwendet wird. Es wird aber nicht zwischen Lese-
und Schreibzugriff unterschieden.
Eine .lss Datei hab ich noch nie gesehen. und Checkboxen hab ich bei
meinem gcc auch nicht, ist dir evtl das zugehörige Flag für gcc bekannt?
Ein grep mit Variablennamen über alle .c-Files liefert dir schonmal alle
Zeilen, in denen die Variable vorkommen. Die musst du dann absuchen. Bei
Zugriffen über Pointern, memcopy, unions, und ähnlichen Spielchen kommen
die dann noch hinzu.
Oder mal doxygen über die Quellen laufen lassen, dessen output ist auch
ganz hilfreich, um solche Fragen zu beantworten.
Auch schön ist ein hardware-debugger mit break on read ;)
Oliver
Vielen Dank erstmal für deine Hilfe.
Mein Problem ist, dass die Prüfung wirklich 100%ig korrekt sein muss.
Der Debugger fällt daher schonmal raus, denn man kann mit vernünftigem
Aufwand nicht absichern, dass wirklich alle möglichen Programmpfade beim
Debugging durchlaufen werden. Auch wenn das halt on read natürlich
optimal wäre ;)
Gleiches gilt für deinen grep-Vorschlag - schließlich gibt es Fälle, bei
denen z.B. Zeigerzugriffe erst zur Laufzeit feststehen.
1
pExample=&iExample+INPUT_PINS;
Wenn die Belegung der Input-Pins halt zufällig so ist, dass pExample auf
den zu untersuchenden Speicherbereich zeigt, hat man verloren. Gut, ob
das nun praktisch wirklich relevant ist, sei mal dahingestellt...
Am liebsten wäre mir wirklich erstmal ein beim Compilieren generierter
Outupt - klar wäre der auch nicht 100%ig sicher, aber der Aufwand wäre
gleich Null und man könnte sich Gedanken machen, wie man damit
weitermacht.
Doxygen schaue ich mir an.
Für alle anderen Vorschläge bin ich offen ;)
Schnapphase schrieb:> Am liebsten wäre mir wirklich erstmal ein beim Compilieren generierter> Outupt - klar wäre der auch nicht 100%ig sicher, aber der Aufwand wäre> gleich Null und man könnte sich Gedanken machen, wie man damit> weitermacht.
gcc bietet eine Option, beim Compilieren ( Compile - ASM - LINK ) nach
dem Compile Schluß zu machen und die entstandenen Assembler-Dateien zu
analysieren. Der Name der Opßtion steht in der gcc Dokumentation.
Bernhard
Schnapphase schrieb:> Gleiches gilt für deinen grep-Vorschlag - schließlich gibt es Fälle, bei> denen z.B. Zeigerzugriffe erst zur Laufzeit feststehen.> pExample = &iExample + INPUT_PINS;
Und genau diesen Fall kannst du nie ausschließen. Dinge die erst zur
Laufzeit feststehen kann man nunmal nicht zur Compilezeit prüfen.
Du kannst natürlich den erzeugten ASM Quelltext automatisiert
untersuchen was etwas einfacher sein sollte als den C-Quelltext, aber
auch dort sind Zugriffe mittels Pointer auf die Variable nicht wirklich
einfach aufzudecken.
Kurz und gut:
Vermeide es die Adresse der Variable zu ermitteln (ist ja laut deiner
Aussage generierter Code) und prüfe dann auf Schreibzugriffe mittels
grep.
Matthias
> gcc bietet eine Option, beim Compilieren ( Compile - ASM - LINK ) nach> dem Compile Schluß zu machen und die entstandenen Assembler-Dateien zu> analysieren. Der Name der Opßtion steht in der gcc Dokumentation.> Du kannst natürlich den erzeugten ASM Quelltext automatisiert> untersuchen was etwas einfacher sein sollte als den C-Quelltext
Ja, das hab ich mir auch schon gedacht, die ASM files habe ich auch
schon generieren können. Also wenn Codeparser, dann wohl mit dem
Assemblercode.
Ich dachte halt nur, bevor ich mit nem Parser anfange, dass ich erstmal
wegen den Compilergeschichten Infos einhole. Weil bei einem einfachen
grep wirds nicht bleiben - Reicht ja schon, wenn die Variable als Zeiger
einer Funktion übergeben wird, dann muss ja auch diese Funktion
analytisiert werden, wobei die Variable dort anders benannt ist. Oder
wenn ein Pointer auf die Variable gesetzt wird und dann halt damit
zugewiesen bzw. gelesen wird. Und dann kann ja auch wieder ein Pointer
auf den Pointer... und Arrays.... und ganz hässlich: Structs, die dann
den Speicherbereich der Variable umschließen... usw usw usw. Ein
einfaches grep wird da nicht reichen :/ Werde da wohl nicht drumrum
kommen...
> Vermeide es die Adresse der Variable zu ermitteln
Wie meinst du das? Die Adresse steht bei mir nach dem linken ja schon
fest.
Schnapphase schrieb:> Ja, das hab ich mir auch schon gedacht, die ASM files habe ich auch> schon generieren können. Also wenn Codeparser, dann wohl mit dem> Assemblercode.
Das ist aber nicht viel weniger spassig, als im C-Code. Die Adresse kann
in Registern stehen, je nach Compiler wird da noch gepusht und
"gepoppt". Ich kenne zwar jetzt den Tricore nicht, aber es würde mich
wundern, wenn der keine indirekten oder indizierten Adressierungsarten
kennt.
Das mit der 100%igen Sicherheit wird da auch schwierig.
Oliver
einfach schrieb:> Benenn einfach die Variable um, und schau dir die Fehler an, die der> Compiler wirft !
geht leider nicht wenn irgendwo direkt auf den speicher der variable
zugegriffen wird.
*((*int)0x1234) = 12;
Ich frage mich ja, in welcher Situation man sich die Frage stellen muss,
ob eine bestimmte Variable gelesen wird. Sollte man als Autor nicht
wissen, was sein Programm tut? - Und was ist daran so schlimm, wenn
besagte Variable gelesen wird?
Schnapphase schrieb:> Mein Problem ist, dass die Prüfung wirklich 100%ig korrekt sein muss.
Du hast verloren. Denn das Aliasing-Problem ist unentscheidbar.
Es geht hierbei um ein Universitätsprojekt zur Entwicklung eines
Codegenerators. Der vom Codegenerator erzeugte Quelltext soll bestimmten
Prüfungen unterzogen werden, daher diese etwas 'weltfremde'
Problemstellung.
Ist wohl alles ziemlich theoretisch, daher ist auch keine HW vorhanden,
auf der man debuggen könnte.
Warum jetzt genau nur auf Lesezugriffe geachtet wird, weiß ich leider
selbst nicht ;)
Im universitären Umfeld sollte man IMHO schon wissen, daß das
prinzipiell nicht geht.
Aliasing ist nämlich eines der absoluten Standardprobleme in Compilerbau
und Codeanalyse. Also setzt man Heuristiken ein und ignoriert die
weltfremden Abweichungen (Codeanalyse) oder übersetzt konservativ
(Compilerbau) oder schränkt die Programmiersprache ein (siehe C mit
"strict aliasing").
Hi
wenn doch aber C-Code generiert wird könnte man den ja mit einem anderen
Compiler übersetzen so das der Code auf debugfähiger Hardware (also im
einfachsten Fall x86) lauffähig wird. Dann einen entsprechenden
Datenbreakpoint erzeugen, das Programm mit Testdaten füttern und mit
einem code coverage Werkzeug prüfen ob zumindest alle Zweige durchlaufen
werden. Es kann natürlich auch nützlich sein den generierten Code in
kleine Einheiten zu teilen und diese dann separat zu testen -> Unit
Test.
Matthias
Μαtthias W. schrieb:> Dann einen entsprechenden> Datenbreakpoint erzeugen, das Programm mit Testdaten füttern und mit> einem code coverage Werkzeug prüfen ob zumindest alle Zweige durchlaufen> werden.
Völlig unausreichend, denn dass aller Code durchlaufen wurde heißt noch
lange nicht, dass jeder Pointer auch jeden möglichen Wert angenommen
hat. Die gewünschte 100%-Lösung ist wie erwähnt schon theoretisch nicht
machbar.
Schnapphase schrieb:> Es geht hierbei um ein Universitätsprojekt zur Entwicklung eines> Codegenerators. Der vom Codegenerator erzeugte Quelltext soll bestimmten> Prüfungen unterzogen werden, daher diese etwas 'weltfremde'> Problemstellung.>> Ist wohl alles ziemlich theoretisch, daher ist auch keine HW vorhanden,> auf der man debuggen könnte.
Um einen Codegenerator zu prüfen ist ein Compiler nicht das beste
Werkzeug. Da schreibst besser einen Code-Generator-Prüfer ;-)
Um zu sehen, ob und wieviele Zugriffe (egal ob lesend und/oder
schreibend) geschehen, kommst du nicht umhin, das Programm ablaufen zu
lassen und per Profiling Informationen zu sammeln. Da man nicht weiß,
wie lange das Programm läuft -- Halteproblem lässt grüssen -- muss die
erzeugte Software schon bestimmte Eigenschaften sicherstellen, daß sie
zB nach einer bestimmten Zeit oder bestimmter Anzahl Instruktionen
terminiert. Andere Bedingung an den Generator wäre, daß der Code
statisch analysierbar ist, aber selbst mit dieser starken Restriktion
ist ein Compiler nicht das Werkzeug der Wahl. Dierzu gibt es Software
zur statischen Analyse, die aber den Uni-Etat sprengen dürfte ;-)
Ebenso zu klären ist auch, ob die Anzahl der Zugriffe statisch zu
bestimmen sind oder dynamisch. Beides erfordert komplett verschiedene
Ansätze.
Auch andere Hardware ist keine Lösung, da auf dieser ein anderer
Compiler den Code erzeugt. Daß der vielleicht "GCC" heisst tut da nix
zur Sache. Ein anderes System ist also kein Modell für deinen TriCore +
gcc.
Problem beim Profiling ist jedoch, daß es den Code verändert bzw. das
Programm andere Ablaufeigenschaften hat; es ist also zunächst zu klären,
was in dem Zusammenhang unter "Programm" zu verstehen ist: Es ist ja
durchaus möglich, daß ein "foo=1" im C-Programm steht, sich aber nicht
mehr im erzeugten Binary findet. Die Übersetzungsprozess sind die
Nebenwirkung, die der Code hat, invariant. Mehr nicht. Jedenfalls wird
der Code nicht Eins-zu-Eins übersetzt.
Am ehesten praktikabel ist die Verwendung eines Simulators und
Compilererweiterungen wie Virtual Profiling -- unter der Voraussetzung,
daß das Halteproblem-Dingens geklärt ist. Solche Erweiterungen gibt es
zB für neuere Verionen des tricore-gcc von HighTec; und viele andere
tricore-gccs dürften nicht unterwegs sein. Ergo: den Support fragen, was
man dazu braucht. Die Analyse gilt dann allerdings nur für bestimmte
Ablaufpfade.