Wie PVS-Studio Analyzer begann, mehr Fehler in Unity-Projekten zu finden

image1.png


Während der Entwicklung des statischen Analysators PVS-Studio versuchen wir, ihn in verschiedene Richtungen zu entwickeln. Daher arbeitet unser Team an Plugins für IDE (Visual Studio, Rider), verbessert die Integration in CI usw. Die Steigerung der Effizienz der Projektanalyse für Unity ist auch eines unserer vorrangigen Ziele. Wir glauben, dass statische Analysen es Programmierern ermöglichen werden, mit dieser Spiel-Engine die Qualität ihres Quellcodes zu verbessern und die Arbeit an jedem Projekt zu vereinfachen. Daher möchte ich die Popularität von PVS-Studio bei Unternehmen erhöhen, die für Unity entwickeln. Einer der ersten Schritte bei der Implementierung dieser Idee bestand darin, Anmerkungen für die in der Engine definierten Methoden zu schreiben. Auf diese Weise können Sie die Richtigkeit des Codes überprüfen, der mit Aufrufen kommentierter Methoden verknüpft ist.



Einführung



Anmerkungen sind einer der wichtigsten Analysemechanismen. Sie enthalten verschiedene Informationen zu Argumenten, Rückgabewerten und internen Merkmalen von Methoden, die nicht automatisch ermittelt werden können. Gleichzeitig kann der Annotationsprogrammierer die ungefähre interne Struktur der Methode und die Besonderheiten ihrer Funktionsweise basierend auf der Dokumentation und dem gesunden Menschenverstand annehmen.



Das Aufrufen der GetComponent- Methode sieht beispielsweise etwas seltsam aus, wenn der zurückgegebene Wert nicht verwendet wird. Bullshit Fehler? Überhaupt nicht. Natürlich kann dies einfach eine zusätzliche Herausforderung sein, die von allen vergessen und aufgegeben wird. Oder es kann sein, dass eine wichtige Zuordnung fehlt. Anmerkungen können dem Analysator helfen, ähnliche und viele andere Fehler zu finden.



Natürlich haben wir bereits viele Anmerkungen für den Analysator geschrieben. Zum Beispiel kommentierte Klassenmethoden aus dem System- Namespace . Darüber hinaus gibt es einen Mechanismus zur automatischen Annotation einiger Methoden. Mehr dazu hier . Ich stelle fest, dass dieser Artikel mehr über den Teil von PVS-Studio erzählt, der für die Analyse von C ++ - Projekten verantwortlich ist. Es gibt jedoch keinen spürbaren Unterschied in der Funktionsweise von Anmerkungen für C # und C ++.



Schreiben von Anmerkungen für Unity-Methoden



Wir sind bestrebt, die Qualität der Codeüberprüfung von Projekten mit Unity zu verbessern. Daher wurde beschlossen, die Methoden dieser Engine zu kommentieren.



Die ursprüngliche Idee war, alle Unity-Methoden mit Anmerkungen im Allgemeinen abzudecken, es gab jedoch viele davon. Aus diesem Grund haben wir uns entschlossen, zunächst Methoden aus den am häufigsten verwendeten Klassen zu kommentieren.



Sammlung von Informationen



Zunächst musste herausgefunden werden, welche Klassen häufiger als andere verwendet werden. Ein wichtiger Aspekt ist außerdem die Möglichkeit, Anmerkungsergebnisse zu erfassen - neue Fehler, die der Analysator aufgrund schriftlicher Anmerkungen in realen Projekten findet. Daher bestand der erste Schritt darin, nach geeigneten Open-Source-Projekten zu suchen. Dies war jedoch nicht so einfach.



Das Problem ist, dass viele der gefundenen Projekte in Bezug auf den Quellcode recht klein waren. Wenn es Fehler gibt, gibt es nicht sehr viele davon, und es ist noch weniger wahrscheinlich, dass dort irgendwelche positiven Ergebnisse gefunden werden, die sich speziell auf Methoden von Unity beziehen. Manchmal gab es Projekte, die praktisch keine (oder überhaupt keine) Unity-spezifischen Klassen verwendeten, obwohl sie laut Beschreibung irgendwie mit der Engine verbunden waren. Solche Funde waren für die jeweilige Aufgabe völlig ungeeignet.



Natürlich hatte ich manchmal Glück. Das Juwel in dieser Sammlung ist beispielsweise das MixedRealityToolkit . Der darin enthaltene Code ist bereits anständig, was bedeutet, dass die gesammelten Statistiken über die Verwendung von Unity-Methoden in einem solchen Projekt vollständiger sind.



So wurden 20 Projekte mit den Fähigkeiten der Engine rekrutiert. Um die am häufigsten verwendeten Klassen zu finden, wurde ein Roslyn-basiertes Dienstprogramm geschrieben, das Methodenaufrufe von Unity berechnet. Ein solches Programm kann übrigens auch als statischer Analysator bezeichnet werden. Wenn Sie darüber nachdenken, analysiert sie die Quelle wirklich, ohne das Projekt selbst zu starten.



Der geschriebene "Analysator" ermöglichte es, die Klassen zu finden, deren durchschnittliche Verwendungshäufigkeit in den gefundenen Projekten am höchsten ist:



  • UnityEngine.Vector3
  • UnityEngine.Mathf
  • UnityEngine.Debug
  • UnityEngine.GameObject
  • UnityEngine.Material
  • UnityEditor.EditorGUILayout
  • UnityEngine.Component
  • UnityEngine.Object
  • UnityEngine.GUILayout
  • UnityEngine.Quaternion
  • Usw.


Dies bedeutet natürlich überhaupt nicht, dass diese Klassen wirklich so oft von Entwicklern verwendet werden - schließlich sind Statistiken, die auf einer so kleinen Stichprobe von Projekten basieren, nicht sehr vertrauenswürdig. Diese Informationen reichten jedoch zunächst aus, um sicherzustellen, dass die Methoden von Klassen, die irgendwo verwendet werden, mit Anmerkungen versehen sind.



Anmerkung



Nachdem Sie die benötigten Informationen erhalten haben, können Sie mit der eigentlichen Anmerkung beginnen. Die wahren Helfer in dieser Angelegenheit waren die Dokumentation und der Unity-Editor, in dem ein Testprojekt erstellt wurde. Dies war erforderlich, um einige Punkte zu überprüfen, die nicht in der Dokumentation angegeben sind. Zum Beispiel war es keineswegs immer klar, ob die Übergabe von null in einem Argument zu einem Fehler führen würde oder ob das Programm ohne Probleme fortgesetzt werden würde. Natürlich ist das Übergeben von null normalerweise nicht sehr gut, aber in diesem Fall haben wir nur Fehler betrachtet, die den Ausführungsfluss unterbrochen haben oder vom Unity-Editor als Fehler protokolliert wurden.



Bei solchen Überprüfungen wurden interessante Merkmale der Arbeit einiger Methoden gefunden. Zum Beispiel Code ausführen



MeshRenderer renderer = cube.GetComponent<MeshRenderer>();
Material m = renderer.material;
List<int> outNames = null;
m.GetTexturePropertyNameIDs(outNames);


führt dazu, dass der Unity-Editor selbst abstürzt, obwohl in solchen Fällen normalerweise die Ausführung des aktuellen Skripts unterbrochen und der entsprechende Fehler protokolliert wird. Natürlich ist es unwahrscheinlich, dass Entwickler dies häufig schreiben, aber die Tatsache, dass der Unity-Editor durch Ausführen gewöhnlicher Skripte zum Absturz gebracht werden kann, ist nicht sehr gut. Das gleiche passiert in mindestens einem anderen Fall:



MeshRenderer renderer = cube.GetComponent<MeshRenderer>();
Material m = renderer.material;
string keyWord = null;
bool isEnabled = m.IsKeywordEnabled(keyWord);


Die angegebenen Probleme sind für den Unity 2019.3.10f1-Editor relevant.



Sammlung von Ergebnissen



Nachdem die Anmerkung abgeschlossen ist, müssen Sie überprüfen, wie sich dies auf die generierten Warnungen auswirkt. Vor dem Hinzufügen von Anmerkungen für jedes der ausgewählten Projekte wird ein Protokoll mit Fehlern erstellt, das wir als Referenz bezeichnen. Anschließend werden neue Anmerkungen in den Analysator integriert und die Projekte erneut überprüft. Die generierten Warnlisten unterscheiden sich aufgrund von Anmerkungen von den Referenzlisten.



Das Annotationstestverfahren wird automatisch mit dem speziell für diese Anforderungen geschriebenen CSharpAnalyserTester-Programm durchgeführt. Es beginnt mit der Analyse von Projekten, vergleicht anschließend die resultierenden Protokolle mit den Referenzprotokollen und generiert Dateien mit Informationen zu den Unterschieden.



Der beschriebene Ansatz wird auch verwendet, um herauszufinden, welche Änderungen in den Protokollen beim Hinzufügen einer neuen oder beim Ändern einer vorhandenen Diagnose angezeigt werden.



Wie bereits erwähnt, war es schwierig, große Open Source-Projekte für Unity zu finden. Dies ist unangenehm, da der Analysator für sie interessantere Antworten liefern könnte. Gleichzeitig würde es viel mehr Unterschiede zwischen den Referenzprotokollen und den nach der Annotation generierten Protokollen geben.



Die schriftlichen Anmerkungen haben jedoch dazu beigetragen, mehrere verdächtige Punkte in den betrachteten Projekten zu identifizieren, was auch ein angenehmes Ergebnis der Arbeit ist.



Zum Beispiel wurde ein etwas seltsamer GetComponent- Aufruf gefunden :



void OnEnable()
{
  GameObject uiManager = GameObject.Find("UIRoot");

  if (uiManager)
  {
    uiManager.GetComponent<UIManager>();
  }
}


Analyzer-Warnung : V3010 Der Rückgabewert der Funktion 'GetComponent' muss verwendet werden. - ZUSÄTZLICH IN AKTUELLEM UIEditorWindow.cs 22



Aus der Dokumentation ist es logisch zu schließen, dass der von dieser Methode zurückgegebene Wert irgendwie verwendet werden muss. Daher wurde es beim Kommentieren entsprechend markiert. Das Anrufergebnis wird sofort nichts zugewiesen, was etwas seltsam aussieht.



Und hier ist ein weiteres Beispiel für zusätzliche Analysatoroperationen:



public void ChangeLocalID(int newID)
{
  if (this.LocalPlayer == null)                          // <=
  {
    this.DebugReturn(
      DebugLevel.WARNING, 
      string.Format(
        ...., 
        this.LocalPlayer, 
        this.CurrentRoom.Players == null,                // <=
        newID  
      )
    );
  }

  if (this.CurrentRoom == null)                          // <=
  {
    this.LocalPlayer.ChangeLocalID(newID);               // <=
    this.LocalPlayer.RoomReference = null;
  }
  else
  {
    // remove old actorId from actor list
    this.CurrentRoom.RemovePlayer(this.LocalPlayer);

    // change to new actor/player ID
    this.LocalPlayer.ChangeLocalID(newID);

    // update the room's list with the new reference
    this.CurrentRoom.StorePlayer(this.LocalPlayer);
  }
}


Warnungen des Analysators :



  • V3095 Das Objekt 'this.CurrentRoom' wurde verwendet, bevor es gegen null verifiziert wurde. Überprüfen Sie die Zeilen: 1709, 1712. - ZUSÄTZLICH IN AKTUELL LoadBalancingClient.cs 1709
  • V3125 Das Objekt 'this.LocalPlayer' wurde verwendet, nachdem es gegen null verifiziert wurde. Überprüfen Sie die Zeilen: 1715, 1707. - ZUSÄTZLICH IM AKTUELLEN LoadBalancingClient.cs 1715


Beachten Sie, dass PVS-Studio nicht darauf achtet, LocalPlayer an string.Format zu übergeben , da dies nicht zu einem Fehler führt. Und der Code sieht aus, als wäre er absichtlich geschrieben worden.



In diesem Fall ist der Effekt von Anmerkungen nicht so offensichtlich. Sie waren es jedoch, die diese positiven Ergebnisse hervorbrachten. Es stellt sich die Frage, warum diese Warnungen vorher nicht da waren.



Tatsache ist, dass in der DebugReturn- Methode mehrere Aufrufe ausgeführt werden, die theoretisch den Wert der CurrentRoom- Eigenschaft beeinflussen könnten :



public virtual void DebugReturn(DebugLevel level, string message)
{
  #if !SUPPORTED_UNITY
  Debug.WriteLine(message);
  #else
  if (level == DebugLevel.ERROR)
  {
    Debug.LogError(message);
  }
  else if (level == DebugLevel.WARNING)
  {
    Debug.LogWarning(message);
  }
  else if (level == DebugLevel.INFO)
  {
    Debug.Log(message);
  }
  else if (level == DebugLevel.ALL)
  {
    Debug.Log(message);
  }
  #endif
}


Der Analysator kennt die Besonderheiten der aufgerufenen Methoden nicht, was bedeutet, dass er nicht weiß, wie sie sich auf die Situation auswirken werden. PVS-Studio geht daher davon aus, dass sich der Wert von this.CurrentRoom während des Betriebs der DebugReturn- Methode ändern kann . Daher wird eine weitere Überprüfung durchgeführt.



Die Anmerkungen lieferten Informationen, dass die in DebugReturn aufgerufenen Methoden die Werte anderer Variablen nicht beeinflussen. Daher ist die Verwendung einer Variablen vor der Überprüfung auf Null verdächtig.



Fazit



Zusammenfassend lässt sich sagen, dass Sie durch das Kommentieren von Unity-spezifischen Methoden zweifellos mehr verschiedene Fehler in Projekten finden können, die diese Engine verwenden. Die Annotationsabdeckung aller verfügbaren Methoden nimmt jedoch viel Zeit in Anspruch. Es ist effizienter, die am häufigsten verwendeten zuerst mit Anmerkungen zu versehen. Um zu verstehen, welche Klassen häufiger verwendet werden, sind jedoch geeignete Projekte mit einer großen Codebasis erforderlich. Darüber hinaus können Sie mit großen Projekten die Leistung von Anmerkungen viel besser steuern. All dies werden wir auch in naher Zukunft tun.



Der Analysator wird ständig weiterentwickelt und weiterentwickelt. Das Hinzufügen von Anmerkungen für Unity-Methoden ist nur ein Beispiel für die Erweiterung der Funktionen. Mit der Zeit wächst somit die Effizienz von PVS-Studio. Wenn Sie PVS-Studio noch nicht ausprobiert haben, können Sie es beheben, indem Sie es von der entsprechenden Seite herunterladen . Dort können Sie einen Testschlüssel für den Analysator erhalten, um sich mit seinen Funktionen vertraut zu machen, indem Sie verschiedene Projekte überprüfen.





Wenn Sie diesen Artikel einem englischsprachigen Publikum zugänglich machen möchten, verwenden Sie bitte den Übersetzungslink: Nikita Lipilin. Wie der PVS-Studio-Analysator begann, noch mehr Fehler in Unity-Projekten zu finden .



All Articles