Code-Analyse f. Initialisierung

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
Jonathan
Establishment
Beiträge: 2353
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Code-Analyse f. Initialisierung

Beitrag von Jonathan »

Moin,

ich habe sporadisch Abstürze beim starten meiner Anwendung. Ich könnte mir vorstellen, dass irgendetwas nicht richtig initialisiert wird und die Werte im Speicher meistens passen, manchmal aber auch nicht. Ich hatte vor einiger Zeit schonmal das Problem, dass ich vergessen hatte eine Variable im copy-Ctor zu kopieren, und könnte mir vorstellen, dass ich einen ähnlichen Fehler habe. Ich brauche wohl nicht dazu zu sagen, dass der Absturz im Debug-Mode nicht auftritt^^.

Wie finde ich diesen und ähnliche Fehler jetzt am besten? Ich habe mal Dr. Memory ausprobiert, aber das findet erstens sehr sehr viele false-positives in irgendwelchen Bibliothekscode und zweitens läuft das Programm dabei um Größenordnungen langsamer.

Aber was ist mit statischer Code-Analyse? Es muss doch recht leicht möglich sein (und daher entsprechende Tools geben) um zu prüfen ob Code sauber ist. Beispielsweise ob in jedem Konstruktor alle Variablen zugewiesen werden. Und ob dies in der Richtigen Reihenfolge geschieht. Klar gibt es immer Grenzfälle in denen solche Analysen scheitern müssen, aber ich denke ich sollte in jedem Falle eine Menge Unsauberkeiten finden und meinen Code verbessern können. Habt ihr irgendwelche Erfahrungen mit derlei Tools?

Achja: Ich benutze derzeit VS Community 2015 unter Windows. Ich habe lange keine neue Version installiert weil ich keine Lust hatte, Abhängigkeiten neu zu kompillieren, aber wie es aussieht muss ich das vielleicht auch gar nicht.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Jonathan
Establishment
Beiträge: 2353
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Code-Analyse f. Initialisierung

Beitrag von Jonathan »

Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Code-Analyse f. Initialisierung

Beitrag von Krishty »

Was hält dich denn davon ab, die Release-Version zu debuggen?

Falls ein Debugger das Problem verschwinden lässt, kannst du in Windows einstellen, einen Crash Dump zu speichern, den du danach mit Visual Studio öffnen und analysieren kannst: https://docs.microsoft.com/en-us/window ... mode-dumps

Aber mein erster Schuss wäre schlicht, die Release-Version zu debuggen …

P.S.: Du kannst im Application Verifier auch Dirty Stacks einschalten um den Stack mit beliebigen Werten vorinitialisieren. Für den Heap müsste es eine ähnliche Einstellung geben
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Alexander Kornrumpf
Moderator
Beiträge: 2106
Registriert: 25.02.2009, 13:37

Re: Code-Analyse f. Initialisierung

Beitrag von Alexander Kornrumpf »

Jonathan hat geschrieben: 03.11.2020, 10:05 Es muss doch recht leicht möglich sein (und daher entsprechende Tools geben) um zu prüfen ob Code sauber ist. Beispielsweise ob in jedem Konstruktor alle Variablen zugewiesen werden. Und ob dies in der Richtigen Reihenfolge geschieht. Klar gibt es immer Grenzfälle in denen solche Analysen scheitern müssen, aber ich denke ich sollte in jedem Falle eine Menge Unsauberkeiten finden und meinen Code verbessern können. Habt ihr irgendwelche Erfahrungen mit derlei Tools?
Mit ist klar dass du implizit C++ meintest, aber in die Kerbe von neulich: IntelliJ kann das natürlich.
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Code-Analyse f. Initialisierung

Beitrag von Schrompf »

Die Statische CodeAnalyse vom VisualStudio ist ein kostenloses grundlegendes Ding, dass Du mal ausprobieren kannst... falls es die in VS2015 schon gegeben habt. Ergebnisse nur begrenzt nützlich, die Erfahrungen damit hast Du hier im Forum vielleicht mitgelesen. Aber so Sachen wir "Member von Struct nicht initialisiert" findet das Ding.

Dann gibt's natürlich die großen Keulen: SonarCube, Klocwork, vielleicht noch weitere, die ich nicht kenne. Die aufzusetzen ist massiv, die bringen ne Datenbank und ne Weboberfläche und nen Workflow mit. Aber dafür finden Sie neben dem üblichen Haufen an "Eigentlich nur Styleguide"-Problemen auch tatsächlich Bugs der Art "wenn hier Nullptr reinkommt und das if() diesen Pfad nimmt, dann wird hier ein Nullptr dereferenziert.". Sehr sehr selten, und leider überfrachtet von False Positives, aber wenn dann absolut nützlich.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Spiele Programmierer
Establishment
Beiträge: 426
Registriert: 23.01.2013, 15:55

Re: Code-Analyse f. Initialisierung

Beitrag von Spiele Programmierer »

Bezüglich statischer Analyse kann ich mich nur den anderen anschließen. Man findet zwar schon Bugs, aber die Rate der Falschmeldungen ist extrem hoch. Dass sie dir in deinem konkreten Fall weiterhelfen kann, halte ich für recht unwahrscheinlich.

Ich würde auch erstmal versuchen, mit Release-Mode-Debugging und so den Fehler einzugrenzen. Wenn du in der glücklichen Lage bist, das der Code mit Clang auf Linux kompiliert, würde mal den Memory Sanitizer verwenden. Wenn der Fehler so ist, wie du vermutest, dann sollte das einige weitere nützliche Diagnoseinformationen liefern. (Wobei auch noch die Gefahr besteht, dass der Fehler dann nicht mehr auftritt...)
Zuletzt geändert von Spiele Programmierer am 03.11.2020, 15:34, insgesamt 1-mal geändert.
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Code-Analyse f. Initialisierung

Beitrag von Schrompf »

Visual Studio hat seit einer neueren 2019er Version auch den AddressSanatizer. Ein deutlich zu groß bemessenes memset() hat der bei mir sicher gefunden. Nur die Einrichtung war nicht-trivial - ich hab zigtausend False Positives noch vor Betreten der Main gehabt, aber mit irgendnem simplen Switch konnte ich die vermeiden.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Spiele Programmierer
Establishment
Beiträge: 426
Registriert: 23.01.2013, 15:55

Re: Code-Analyse f. Initialisierung

Beitrag von Spiele Programmierer »

Uninitialisierte Speicherzugriffe findet, soweit ich weiß, aber nur der Memory Sanitizer und nicht der Address Sanitizer.

Mit dem Address Sanitizer von VS 2019 hatte ich auch eher gemischte Ergebnisse. Man muss irgendwelche komischen Bibliotheken manuell linken. Und dann hält der Address Sanitizer bei mir manchmal an irgendwelchen zufälligen Stellen in meinem Code an. Wenn man das Programm neustartet, ist es dann immer weg bzw. ganz wo anders. Ich glaube fast, dass ist ein Bug im Sanitizer von VS, da der Fehler an sehr unterschiedlichen Stellen angezeigt wird und der fragliche Code eigentlich deterministisch sein sollte.
Benutzeravatar
Jonathan
Establishment
Beiträge: 2353
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Code-Analyse f. Initialisierung

Beitrag von Jonathan »

Krishty hat geschrieben: 03.11.2020, 10:30 Was hält dich denn davon ab, die Release-Version zu debuggen?
Also wenn ich es im Release-Modus ausführe, stürzt es kurz nach dem Starten ab, aber VS kann mir keinen Code anzeigen, nur das Disassembly. Das hilft mir erstmal überhaupt nicht weiter. Wenn ich im Release-Modus mit Debug-Informationen kompiliere läuft das Programm ganz normal durch und es gibt keinen Absturz. Allerdings sind die Abstürze halt auch nicht immer so reproduzierbar, manchmal passiert es, manchmal nicht, aber bisher noch nie in einem Falle, wo ich dann tatsächlich gewusst hätte, wo im Code das Problem liegt. Von daher weiß ich grad nicht, wie ich das am geschicktesten debuggen soll...
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Code-Analyse f. Initialisierung

Beitrag von Schrompf »

Alles drauf, was Du findest :-)

Schuss ins Blaue: ich hatte sowas mal, und es lag an nem nicht-initialisiertem Bool. Im Debug-Modus zaubert Visual Studio überall ein kluges Hex-Muster rein: 0xcccccccc oder ähnliche. Das hilft bei nicht-initialisierten Zeigern, Zählern, Zinsen, aber bei bool kriegst Du mit diesem gut gemeinten Move leider nur ein verlässliches true als Init-Wert. Erst im Release wird der Bool dann zufällig auch mal false und der Crash tritt auf.

Sowas findet aber die statische Code-Analyse.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4254
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Code-Analyse f. Initialisierung

Beitrag von Chromanoid »

Sowas ähnliches hat übrigens meine C++-Programmier-Leidenschaft gekillt. Sowas ähnliches hatte ich in PaperRPG (ein Uralt-Projekt von mir, falls sich noch wer erinnert - hach das habe ich auf der zfxCON05 vorgestellt, das waren noch Zeiten). Ich war da natürlich noch nicht wirklich gut im Programmieren (wer weiß ob ich das heute bin ;)), aber das ist soo frustig. Das war VC 6.0 noch damals (hatte ich mir tatsächlich zugelegt)...
Benutzeravatar
Jonathan
Establishment
Beiträge: 2353
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Code-Analyse f. Initialisierung

Beitrag von Jonathan »

Kurzer Zwischenstand:

- Hab erstmal Crash Dumps aktiviert. Vielleicht kompilliere ich ne Zeitlang nur mit Debugsymbolen, sollte der Absturz dann auftreten kann ich mit den Dumps hoffentlich was anfangen.
- Hab VS 2019 installiert und die Codeanalyse durchlaufen lassen. Kann natürlich viel mehr als 2015. Ein paar Kleinigkeiten wurden gefunden ("Variable is written only once, consider making it const", und auch eine nicht-Initialisierte Variable in einer Klasse die nicht in Benutzung war.
- Ich habe Dr. Memroy aufgesetzt und durchlaufen lassen. Das hat tatsächlich noch einen nicht-Initialisierten boolen gefunden, allerdings in einer Template-Klasse. Vielleicht hat IntelliSense das deshalb übersehen? Hm.
- Der Bool steuerte ob ein Sprite angezeigt werden soll, ich sehe nicht wirklich wie das zu einem Crash führen sollte. Was ist denn das schlimmste das ein "if(show)" auslösen kann, wenn vollkommen egal wäre, ob der Zweig darunter ausgeführt wird oder nicht?
- Ansonsten wurde etwas Kleinkram geändert, aber ich kann mich an nichts erinnern, das nach diesem Absturz aussah. Allerdings ist der Absturz gestern auch gar nicht mehr aufgetreten, vielleicht war es also doch irgendetwas davon? Sehr unbefriedigend, aber ich mache jetzt erstmal so weiter. Falls der Absturz nochmal kommt habe ich jetzt zumindest eine Reihe an Tools zum Durchprobieren.

Btw.: Sollte es nur auf fremden Rechnern abstürzen, wären dann diese Debug-Dumps das Mittel der Wahl für mich das zu debuggen? Ich habe das schon bei diversen Spielen und Anwendungen gesehen dass man die Möglichkeit hatte dem Entwickler Crash-Reports zu schicken. Wie setzt man sowas am besten auf? Würde man bei der Installation den entsprechenden "Windows Error Reporting\LocalDumps" Registry-Key erstellen und die Daten dann von dort aus verschicken?
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Spiele Programmierer
Establishment
Beiträge: 426
Registriert: 23.01.2013, 15:55

Re: Code-Analyse f. Initialisierung

Beitrag von Spiele Programmierer »

Jonathan hat geschrieben: 05.11.2020, 22:36Was ist denn das schlimmste das ein "if(show)" auslösen kann, wenn vollkommen egal wäre, ob der Zweig darunter ausgeführt wird oder nicht?
Theoretisch laut C++-Standard ist das der Supergau "Undefined Behaviour". D.h. es ist extrem, sehr, sehr schlimm und du hast keine Garantie, dass dein Programm auch nur irgendwie ansatzweise mehr das macht, was es machen soll.
Praktisch passiert wahrscheinlich das was man erwartet. Es kann aber auch in der Praxis zu unerwünschten Seiteneffekten kommen. Beispielsweise wird in der ABI festgeschrieben, dass ein bool entweder 0 oder 1 sein muss. Der Compiler kann auf Basis dieser Annahme optimieren. Hier ein Beispiel:

Code: Alles auswählen

int only_one_bit_set_am_I_right(bool arg)
{
    if (arg)
        return 256;
    else
        return 0;
}
Die Funktion sollte doch nur 0 oder 256 zurückgeben, hab ich nicht recht? Der Clang-Compiler optimiert das aber in einen Bit-Shift, der einfach das gesetzte Bit verschiebt (Hier ein Godbolt-Link). Dadurch kann dann natürlich noch ganz was anderes dabei herauskommen.

Realistisch gesehen schätze ich es aber nicht so arg wahrscheinlich ein, dass bei dir hier ein Absturz entsteht.
Jonathan hat geschrieben: 05.11.2020, 22:36Btw.: Sollte es nur auf fremden Rechnern abstürzen, wären dann diese Debug-Dumps das Mittel der Wahl für mich das zu debuggen? Ich habe das schon bei diversen Spielen und Anwendungen gesehen dass man die Möglichkeit hatte dem Entwickler Crash-Reports zu schicken. Wie setzt man sowas am besten auf? Würde man bei der Installation den entsprechenden "Windows Error Reporting\LocalDumps" Registry-Key erstellen und die Daten dann von dort aus verschicken?
Wenn ich direkten Kontakt zu den Benutzer habe, bei dem es abstürzt, dann würde ich ihn bitten vom Task Manager aus einen Debug Dump zu erzeugen und mir zuzuschicken.

Programmatisch kann man auch auf Windows mit der MiniDumpWriteDump-Funktion einen Crash-Dump erzeugen. Oder man kann eine Bibliothek wie Breakpad verwenden. Das erzeugt den Dump dann automatisch (und für alle Betriebssysteme, die die Bibliothek unterstützt) und lädt es optional irgendwo hoch. Das habe ich allerdings noch nicht selbst verwendet.

Es gibt unterschiedliche Möglichkeiten welche Informationen in den Crash-Dump gepackt werden. Zum Beispiel vom Task-Manager aus kann man nur einen vollen Crash-Dump erzeugen, der wirklich alle Daten aus dem Arbeitsspeicher deines Programmes enthält. Die erzeugten Dateien sind deshalb recht groß, allerdings kann es die Fehlersuche erheblich erschweren wenn man sonst z.B. nur Register und Stack-Variablen sehen kann. Wenn man die Datei zippt, dann ist es nicht so dramatisch, wie es klingt.

Ah und noch was: Falls du Crash-Dumps analysieren willst, solltest du unbedingt die exakten *.pdb-Dateien und alle Binärdateien mit vollen Debuginformationen aufbewahren. Außerdem schadet es nicht, die Version im Repository zu kennzeichnen. Sonst sitzt du irgendwann mit einem Crash-Dump da, aber du kannst höchstens wieder Assemblercode debuggen...
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Code-Analyse f. Initialisierung

Beitrag von Schrompf »

Für meine Steam-Projekte hat sich bewährt: eine Lib, die StackTraces erzeugen kann - heutzutage vielleicht boost::stacktrace, damals WheatyExceptionRecord. Das hängst Du dann in Deine Assertions, Deine Exceptions und in einen Crash Handler rein. Windows wäre das SetUnhandledExceptionFilter(), unter Linux set_sighandler() oder so.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Code-Analyse f. Initialisierung

Beitrag von Krishty »

Jonathan hat geschrieben: 05.11.2020, 22:36Btw.: Sollte es nur auf fremden Rechnern abstürzen, wären dann diese Debug-Dumps das Mittel der Wahl für mich das zu debuggen? Ich habe das schon bei diversen Spielen und Anwendungen gesehen dass man die Möglichkeit hatte dem Entwickler Crash-Reports zu schicken. Wie setzt man sowas am besten auf? Würde man bei der Installation den entsprechenden "Windows Error Reporting\LocalDumps" Registry-Key erstellen und die Daten dann von dort aus verschicken?
Wenn ich direkten Kontakt zu den Benutzer habe, bei dem es abstürzt, dann würde ich ihn bitten vom Task Manager aus einen Debug Dump zu erzeugen und mir zuzuschicken.
+1
Ah und noch was: Falls du Crash-Dumps analysieren willst, solltest du unbedingt die exakten *.pdb-Dateien und alle Binärdateien mit vollen Debuginformationen aufbewahren. Außerdem schadet es nicht, die Version im Repository zu kennzeichnen. Sonst sitzt du irgendwann mit einem Crash-Dump da, aber du kannst höchstens wieder Assemblercode debuggen...
+1

Und hier der Königsweg, den ich aber mangels digitaler Signatur noch nicht ausprobieren konnte:
  • Du änderst nichts an deinem Programm. Insbesondere fügst du keinen Code hinzu, der Crash Dumps erzeugt.
  • Wenn dein Programm auf irgendeinem Windows abstürzt, wird der Standard-Exception-Handler aufgerufen. Der ist das Windows Error Reporting (WER).
  • WerFault.exe startet und sammelt einen Dump deines Programms ein. Es ist dabei optimiert, auch unter schwierigen Bedingungen zu funktionieren (Stack zerstört, Out of Memory, kein Paged Pool, …). Du siehst es vielleicht manchmal kurz im Task Manager nach einem Crash. Bei Windows 7 war das dieses Fenster “foo.exe stopped working, please wait”.
  • Je nach Benutzereinstellungen wird der Report an Microsoft gesendet. (Ist Standardeinstellung.)
  • Microsoft erkennt an der digitalen Signatur, welchem Entwickler die Anwendung gehört, und sammelt die pro Entwickler. Sollte der Crash in einer Windows-Komponente wie Kernel32 oder Direct3D stattgefunden haben, wird er ans Windows-Team weitergeleitet.
  • Mit deinem digitalen Zertifikat kannst du dich bei Microsoft anmelden, um Einsicht zu erhalten.
  • Du bekommst die Crash Dumps präsentiert nach Absturzursache (ähnlicher Instruction Pointer, ähnliche Call Stacks) und nach Häufigkeit. Dadurch kannst du deine Arbeit priorisieren.
So wird das jedenfalls von Microsoft propagiert und insbesondere Raymond Chen drängt immer darauf, es so zu tun. Für die konkreten Schritte müsstest du googeln. Und, wie gesagt, für ’nen Hunni ein Zertifikat erwerben.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten