Eines der aktuellsten Einsatzszenarien des PVS-Studio-Analysators ist seine Integration in CI-Systeme. Und obwohl die Analyse eines PVS-Studio-Projekts aus nahezu jedem Continuous-Integration-System in nur wenigen Befehlen integriert werden kann, machen wir diesen Prozess weiterhin noch komfortabler. PVS-Studio unterstützt jetzt die Konvertierung der Analyseausgabe in ein Format für TeamCity – TeamCity Inspections Type. Mal sehen, wie es funktioniert.
Informationen zur verwendeten Software
Informationen zum untersuchten Projekt
Probieren wir diese Funktionalität an einem praktischen Beispiel aus – analysieren wir das OpenRCT2-Projekt.
Einstellung
Um Zeit zu sparen, werde ich den Installationsprozess wahrscheinlich überspringen und von dem Moment an beginnen, in dem der TeamCity-Server auf meinem Computer läuft. Wir müssen zu: localhost:{Port, der während des Installationsprozesses angegeben wurde} (in meinem Fall localhost:9090) gehen und Autorisierungsdaten eingeben. Nach dem Betreten werden wir begrüßt von:
Klicken Sie auf die Schaltfläche Projekt erstellen. Wählen Sie als Nächstes „Manuell“ aus und füllen Sie die Felder aus.
Nach dem Drücken der Taste Erstellen, wir werden von einem Fenster mit Einstellungen begrüßt.
Lasst uns klicken Erstellen Sie eine Build-Konfiguration.
Füllen Sie die Felder aus und klicken Sie Erstellen. Wir sehen ein Fenster, in dem Sie aufgefordert werden, ein Versionskontrollsystem auszuwählen. Da sich die Quellen bereits lokal befinden, klicken Sie auf überspringen.
Abschließend gehen wir zu den Projekteinstellungen über.
Fügen wir Montageschritte hinzu. Klicken Sie dazu auf: Build-Schritte -> Build-Schritt hinzufügen.
Hier wählen wir:
- Läufertyp -> Befehlszeile
- Ausführen -> Benutzerdefiniertes Skript
Da wir die Analyse während der Projektkompilierung durchführen, sollten Montage und Analyse ein Schritt sein. Füllen Sie daher das Feld aus Benutzerdefiniertes Skript:
Auf einzelne Schritte gehen wir später noch ein. Wichtig ist, dass das Laden des Analysators, das Zusammenstellen des Projekts, die Analyse, die Ausgabe des Berichts und die Formatierung nur elf Zeilen Code erfordern.
Als letztes müssen wir die Umgebungsvariablen festlegen. Ich habe einige Möglichkeiten beschrieben, um ihre Lesbarkeit zu verbessern. Gehen wir dazu weiter: Parameter -> Neuen Parameter hinzufügen und füge drei Variablen hinzu:
Alles was Sie tun müssen, ist den Knopf zu drücken Führen Sie in der oberen rechten Ecke. Während das Projekt zusammengestellt und analysiert wird, erzähle ich Ihnen etwas über das Drehbuch.
Direkt Skript
Zuerst müssen wir die neueste PVS-Studio-Distribution herunterladen. Hierzu nutzen wir den Paketmanager Chocolatey. Für diejenigen, die mehr darüber erfahren möchten, gibt es eine entsprechende
choco install pvs-studio -y
Als Nächstes starten wir das CLMonitor-Dienstprogramm zur Projekterstellungsverfolgung.
%CLmon% monitor –-attach
Dann erstellen wir das Projekt als Umgebungsvariable MSB ist der Pfad zu der Version von MSBuild, die ich erstellen muss
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
Geben wir den Login und den Lizenzschlüssel für PVS-Studio ein:
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
Nachdem der Build abgeschlossen ist, führen Sie CLMonitor erneut aus, um vorverarbeitete Dateien und eine statische Analyse zu generieren:
%CLmon% analyze -l "c:ptest.plog"
Dann werden wir ein anderes Dienstprogramm aus unserer Distribution verwenden. PlogConverter konvertiert einen Bericht von einem Standardformat in ein TeamCity-spezifisches Format. Dadurch können wir es direkt im Build-Fenster anzeigen.
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"
Der letzte Schritt besteht darin, den formatierten Bericht anzuzeigen stdout, wo es vom TeamCity-Parser erfasst wird.
type "C:tempptest.plog_TeamCity.txt"
Vollständiger Skriptcode:
choco install pvs-studio -y
%CLmon% monitor --attach
set platform=x64
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
%CLmon% analyze -l "c:ptest.plog"
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"
type "C:tempptest.plog_TeamCity.txt"
In der Zwischenzeit ist die Montage und Analyse des Projekts erfolgreich abgeschlossen, wir können auf die Registerkarte gehen Projekte und stellen Sie sicher, dass es so ist.
Jetzt lasst uns auf klicken Inspektionen insgesamtSo gelangen Sie zur Anzeige des Analyseberichts:
Warnungen werden nach Diagnoseregelnummern gruppiert. Um durch den Code zu navigieren, müssen Sie auf die Zeilennummer mit der Warnung klicken. Durch Klicken auf das Fragezeichen in der oberen rechten Ecke öffnet sich ein neuer Tab mit Dokumentation. Sie können auch durch den Code navigieren, indem Sie auf die Zeilennummer mit der Analysewarnung klicken. Bei Verwendung ist die Navigation von einem entfernten Computer aus möglich SourceTreeRoot Marker. Wer sich für diese Funktionsweise des Analysators interessiert, kann sich mit dem entsprechenden Abschnitt vertraut machen
Anzeigen der Ergebnisse des Analysegeräts
Nachdem wir nun mit der Bereitstellung und Konfiguration des Builds fertig sind, werfen wir einen Blick auf einige interessante Warnungen in dem Projekt, das wir betrachten.
Warnung N1
Object* CreateObjectFromJson(....)
{
Object* result = nullptr;
....
result = CreateObject(entry);
....
if (readContext.WasError())
{
throw std::runtime_error("Object has errors");
}
....
}
Object* CreateObject(const rct_object_entry& entry)
{
Object* result;
switch (entry.GetType())
{
case OBJECT_TYPE_RIDE:
result = new RideObject(entry);
break;
case OBJECT_TYPE_SMALL_SCENERY:
result = new SmallSceneryObject(entry);
break;
case OBJECT_TYPE_LARGE_SCENERY:
result = new LargeSceneryObject(entry);
break;
....
default:
throw std::runtime_error("Invalid object type");
}
return result;
}
Der Analysator hat einen Fehler festgestellt, der nach der dynamischen Speicherzuweisung in CreateObjectWenn eine Ausnahme auftritt, wird der Speicher nicht gelöscht und es tritt ein Speicherverlust auf.
Warnung N2
static uint64_t window_cheats_page_enabled_widgets[] =
{
MAIN_CHEAT_ENABLED_WIDGETS |
(1ULL << WIDX_NO_MONEY) |
(1ULL << WIDX_ADD_SET_MONEY_GROUP) |
(1ULL << WIDX_MONEY_SPINNER) |
(1ULL << WIDX_MONEY_SPINNER_INCREMENT) |
(1ULL << WIDX_MONEY_SPINNER_DECREMENT) |
(1ULL << WIDX_ADD_MONEY) |
(1ULL << WIDX_SET_MONEY) |
(1ULL << WIDX_CLEAR_LOAN) |
(1ULL << WIDX_DATE_SET) |
(1ULL << WIDX_MONTH_BOX) | // <=
(1ULL << WIDX_MONTH_UP) |
(1ULL << WIDX_MONTH_DOWN) |
(1ULL << WIDX_YEAR_BOX) |
(1ULL << WIDX_YEAR_UP) |
(1ULL << WIDX_YEAR_DOWN) |
(1ULL << WIDX_DAY_BOX) |
(1ULL << WIDX_DAY_UP) |
(1ULL << WIDX_DAY_DOWN) |
(1ULL << WIDX_MONTH_BOX) | // <=
(1ULL << WIDX_DATE_GROUP) |
(1ULL << WIDX_DATE_RESET),
....
};
Nur wenige Menschen außer einem statischen Analysator könnten diesen Aufmerksamkeitstest bestehen. Dieses Copy-Paste-Beispiel ist genau aus diesem Grund gut.
Warnungen N3
struct RCT12SpriteBase
{
....
uint8_t flags;
....
};
struct rct1_peep : RCT12SpriteBase
{
....
uint8_t flags;
....
};
Natürlich ist die Verwendung einer Variablen mit demselben Namen in der Basisklasse und im Nachkommen nicht immer ein Fehler. Die Vererbungstechnologie selbst geht jedoch davon aus, dass alle Felder der übergeordneten Klasse in der untergeordneten Klasse vorhanden sind. Indem wir Felder mit demselben Namen im Erben deklarieren, führen wir zu Verwirrung.
Warnung N4
void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
if ((imageDirection / 8) && (imageDirection / 8) != 3)
{
....
}
....
}
Lass uns genauer hinschauen. Ausdruck imageDirection/8 wird falsch sein, wenn Bildrichtung liegt im Bereich von -7 bis 7. Zweiter Teil: (Bildrichtung / 8) != 3 Schecks Bildrichtung für außerhalb des Bereichs: von -31 bis -24 bzw. von 24 bis 31. Es erscheint mir ziemlich seltsam, auf diese Weise zu prüfen, ob Zahlen in einem bestimmten Bereich enthalten sind, und selbst wenn dieser Code keinen Fehler enthält, würde ich empfehlen, diese Bedingungen expliziter umzuschreiben. Dies würde den Menschen, die diesen Code lesen und pflegen würden, das Leben erheblich erleichtern.
Warnung N5
void process_mouse_over(....)
{
....
switch (window->widgets[widgetId].type)
{
case WWT_VIEWPORT:
ebx = 0;
edi = cursorId; // <=
// Window event WE_UNKNOWN_0E was called here,
// but no windows actually implemented a handler and
// it's not known what it was for
cursorId = edi; // <=
if ((ebx & 0xFF) != 0)
{
set_cursor(cursorId);
return;
}
break;
....
}
....
}
Dieses Codefragment wurde höchstwahrscheinlich durch Dekompilierung erhalten. Dem hinterlassenen Kommentar nach zu urteilen, wurde dann ein Teil des nicht funktionierenden Codes entfernt. Es sind jedoch noch einige Operationen übrig Cursor-ID, was auch wenig Sinn ergibt.
Warnung N6
void Network::ProcessPlayerList()
{
....
auto* player = GetPlayerByID(pendingPlayer.Id);
if (player == nullptr)
{
// Add new player.
player = AddPlayer("", "");
if (player) // <=
{
*player = pendingPlayer;
if (player->Flags & NETWORK_PLAYER_FLAG_ISSERVER)
{
_serverConnection->Player = player;
}
}
newPlayers.push_back(player->Id); // <=
}
....
}
Dieser Code ist ganz einfach zu korrigieren; Sie müssen ihn nur ein drittes Mal überprüfen Spieler auf einen Nullzeiger umstellen oder ihn dem Hauptteil der bedingten Anweisung hinzufügen. Ich würde die zweite Option vorschlagen:
void Network::ProcessPlayerList()
{
....
auto* player = GetPlayerByID(pendingPlayer.Id);
if (player == nullptr)
{
// Add new player.
player = AddPlayer("", "");
if (player)
{
*player = pendingPlayer;
if (player->Flags & NETWORK_PLAYER_FLAG_ISSERVER)
{
_serverConnection->Player = player;
}
newPlayers.push_back(player->Id);
}
}
....
}
Warnung N7
std::optional<ServerListEntry> ServerListEntry::FromJson(...)
{
auto name = json_object_get(server, "name");
.....
if (name == nullptr || version == nullptr)
{
....
}
else
{
....
entry.name = (name == nullptr ? "" : json_string_value(name));
....
}
....
}
Sie können eine schwer lesbare Codezeile auf einen Schlag loswerden und das Problem mit der Suche lösen nullptr. Ich schlage vor, den Code wie folgt zu ändern:
std::optional<ServerListEntry> ServerListEntry::FromJson(...)
{
auto name = json_object_get(server, "name");
.....
if (name == nullptr || version == nullptr)
{
name = ""
....
}
else
{
....
entry.name = json_string_value(name);
....
}
....
}
Warnung N8
void CustomListView::MouseUp(....)
{
....
if (!ColumnHeaderPressedCurrentState)
{
ColumnHeaderPressed = std::nullopt;
ColumnHeaderPressedCurrentState = false;
Invalidate();
}
}
Der Code sieht ziemlich seltsam aus. Es scheint mir, dass entweder in der Bedingung oder bei der Neuzuweisung der Variablen ein Tippfehler vorliegt ColumnHeaderPressedCurrentState Bedeutung falsch.
Abschluss
Wie wir sehen, ist die Integration des statischen Analysators PVS-Studio in Ihr TeamCity-Projekt recht einfach. Dazu reicht es aus, nur eine kleine Konfigurationsdatei zu schreiben. Durch die Überprüfung des Codes können Sie Probleme sofort nach der Montage erkennen und so beheben, wenn die Komplexität und die Kosten der Änderungen noch gering sind.
Wenn Sie diesen Artikel einem englischsprachigen Publikum zugänglich machen möchten, verwenden Sie bitte den Übersetzungslink: Vladislav Stolyarov.
Source: habr.com