Implementera statisk analys i processen istÀllet för att anvÀnda den för att hitta buggar

Jag blev uppmanad att skriva den hÀr artikeln av den stora mÀngd material om statisk analys som alltmer kommer till min uppmÀrksamhet. För det första, detta PVS-studioblogg, som aktivt marknadsför sig pÄ Habré med hjÀlp av recensioner av fel som hittats av deras verktyg i projekt med öppen kÀllkod. Nyligen implementerad PVS-studio Java-stöd, och, naturligtvis, utvecklarna av IntelliJ IDEA, vars inbyggda analysator förmodligen Àr den mest avancerade för Java idag, kunde inte hÄlla sig borta.

NÀr du lÀser sÄdana recensioner fÄr du kÀnslan av att vi pratar om ett magiskt elixir: tryck pÄ knappen, och hÀr Àr den - en lista över defekter framför dina ögon. Det verkar som att allt eftersom analysatorerna förbÀttras kommer fler och fler buggar att hittas automatiskt, och produkterna som skannas av dessa robotar kommer att bli bÀttre och bÀttre, utan nÄgon anstrÀngning frÄn vÄr sida.

Men det finns inga magiska elixirer. Jag skulle vilja prata om det som vanligtvis inte pratas om i inlÀgg som "hÀr Àr de saker som vÄr robot kan hitta": vad analysatorer inte kan göra, vad Àr deras verkliga roll och plats i mjukvaruleveransprocessen och hur man implementerar dem korrekt .

Implementera statisk analys i processen istÀllet för att anvÀnda den för att hitta buggar
Ratchet (kĂ€lla: ĐČĐžĐșĐžĐżĐ”ĐŽĐžŃ).

Vad statiska analysatorer aldrig kan göra

Vad Àr kÀllkodsanalys ur en praktisk synvinkel? Vi tillhandahÄller en del kÀllkod som indata, och som utdata fÄr vi pÄ kort tid (mycket kortare Àn pÄgÄende tester) lite information om vÄrt system. Den grundlÀggande och matematiskt oöverstigliga begrÀnsningen Àr att vi bara kan fÄ en ganska snÀv klass av information pÄ detta sÀtt.

Det mest kÀnda exemplet pÄ ett problem som inte kan lösas med statisk analys Àr avstÀngningsproblem: Detta Àr ett teorem som bevisar att det Àr omöjligt att utveckla en generell algoritm som kan avgöra frÄn kÀllkoden för ett program om det kommer att loopa eller avslutas inom en begrÀnsad tid. En förlÀngning av detta teorem Àr Rice's teorem, som anger att för alla icke-triviala egenskaper hos berÀkningsbara funktioner, Àr det ett algoritmiskt svÄrlöst problem att avgöra om ett godtyckligt program utvÀrderar en funktion med en sÄdan egenskap. Det Àr till exempel omöjligt att skriva en analysator som kan avgöra frÄn vilken kÀllkod som helst om programmet som analyseras Àr en implementering av en algoritm som berÀknar, till exempel, kvadreringen av ett heltal.

Funktionaliteten hos statiska analysatorer har sÄledes oöverstigliga begrÀnsningar. En statisk analysator kommer aldrig i alla fall att kunna upptÀcka sÄdant som till exempel förekomsten av ett "nullpekareundantag" pÄ sprÄk som tillÄter vÀrdet av null, eller i alla fall att faststÀlla förekomsten av ett " attribut inte hittat" pÄ dynamiskt skrivna sprÄk. Allt som den mest avancerade statiska analysatorn kan göra Àr att markera speciella fall, vars antal, bland alla möjliga problem med din kÀllkod, utan att överdriva Àr en droppe i hinken.

Statisk analys handlar inte om att hitta buggar

Av ovanstÄende följer slutsatsen: statisk analys Àr inte ett sÀtt att minska antalet defekter i ett program. Jag skulle vÄga sÀga: nÀr det appliceras pÄ ditt projekt för första gÄngen, kommer det att hitta "intressanta" platser i koden, men troligtvis kommer det inte att hitta nÄgra defekter som pÄverkar kvaliteten pÄ ditt program.

Exemplen pÄ defekter som automatiskt hittas av analysatorer Àr imponerande, men vi bör inte glömma att dessa exempel hittades genom att skanna en stor uppsÀttning stora kodbaser. Enligt samma princip hittar hackare som har möjlighet att prova flera enkla lösenord pÄ ett stort antal konton sÄ smÄningom de konton som har ett enkelt lösenord.

Betyder detta att statisk analys inte bör anvÀndas? SjÀlvklart inte! Och av exakt samma anledning som det Àr vÀrt att kontrollera varje nytt lösenord för att se till att det finns med i stopplistan över "enkla" lösenord.

Statisk analys Àr mer Àn att hitta buggar

Faktum Àr att de problem som praktiskt löses genom analys Àr mycket bredare. NÀr allt kommer omkring, i allmÀnhet Àr statisk analys varje verifiering av kÀllkoder som utförs innan de lanseras. HÀr Àr nÄgra saker du kan göra:

  • Kontrollera kodningsstil i ordets vidaste bemĂ€rkelse. Detta inkluderar bĂ„de att kontrollera formatering, leta efter anvĂ€ndningen av tomma/extra parenteser, stĂ€lla in tröskelvĂ€rden för mĂ„tt som antal rader/cyklomatisk komplexitet för en metod, etc. - allt som potentiellt hindrar lĂ€sbarheten och underhĂ„llbarheten av koden. I Java Ă€r ett sĂ„dant verktyg Checkstyle, i Python - flake8. Program i den hĂ€r klassen kallas vanligtvis "linters".
  • Inte bara körbar kod kan analyseras. Resursfiler som JSON, YAML, XML, .properties kan (och bör!) automatiskt kontrolleras för giltighet. NĂ€r allt kommer omkring Ă€r det bĂ€ttre att ta reda pĂ„ att JSON-strukturen Ă€r trasig pĂ„ grund av nĂ„gra oparade citat i ett tidigt skede av automatisk Pull Request-verifiering Ă€n under testkörning eller körtid? LĂ€mpliga verktyg finns: t.ex. YAMLlint, JSONLint.
  • Kompilering (eller analys för dynamiska programmeringssprĂ„k) Ă€r ocksĂ„ en typ av statisk analys. I allmĂ€nhet kan kompilatorer producera varningar som indikerar problem med kĂ€llkodens kvalitet och bör inte ignoreras.
  • Ibland Ă€r kompilering mer Ă€n att bara kompilera körbar kod. Till exempel om du har dokumentation i formatet AsciiDoctor, i det ögonblick dĂ„ den omvandlas till HTML/PDF, hanterar AsciiDoctor (Maven-plugin) kan utfĂ€rda varningar, till exempel om trasiga interna lĂ€nkar. Och detta Ă€r en bra anledning att inte acceptera Pull-förfrĂ„gan med dokumentationsĂ€ndringar.
  • Stavningskontroll Ă€r ocksĂ„ en typ av statisk analys. Verktyg en förtrollning kan kontrollera stavning inte bara i dokumentationen, utan Ă€ven i programkĂ€llkoder (kommentarer och bokstavliga ord) i olika programmeringssprĂ„k, inklusive C/C++, Java och Python. Ett stavfel i anvĂ€ndargrĂ€nssnittet eller dokumentationen Ă€r ocksĂ„ ett fel!
  • Konfigurationstester (om vad de Ă€r - se. detta Đž detta rapporter), Ă€ven om de körs i en enhetstestkörning sĂ„som pytest, Ă€r de i sjĂ€lva verket ocksĂ„ en typ av statisk analys, eftersom de inte exekverar kĂ€llkoder under exekveringen.

Som du kan se spelar sökning efter buggar i den hÀr listan den minst viktiga rollen, och allt annat Àr tillgÀngligt genom att anvÀnda gratis verktyg med öppen kÀllkod.

Vilken av dessa typer av statisk analys ska du anvÀnda i ditt projekt? SjÀlvklart, ju fler desto bÀttre! Det viktigaste Àr att implementera det korrekt, vilket kommer att diskuteras vidare.

Leveranspipeline som ett flerstegsfilter och statisk analys som första steg

Den klassiska metaforen för kontinuerlig integration Àr en pipeline genom vilken förÀndringar flödet, frÄn kÀllkodsÀndringar till leverans till produktion. Standardsekvensen av steg i denna pipeline ser ut sÄ hÀr:

  1. statisk analys
  2. kompilering
  3. enhetstester
  4. integrationstester
  5. UI-tester
  6. manuell kontroll

Ändringar som avvisats i det N:e steget av pipelinen överförs inte till steg N+1.

Varför just pÄ detta sÀtt och inte pÄ annat sÀtt? I testdelen av pipelinen kommer testare att kÀnna igen den vÀlkÀnda testpyramiden.

Implementera statisk analys i processen istÀllet för att anvÀnda den för att hitta buggar
Testa pyramid. KĂ€lla: artikel Martin Fowler.

LÀngst ner i denna pyramid finns test som Àr lÀttare att skriva, snabbare att utföra och som inte har en tendens att misslyckas. DÀrför borde det finnas fler av dem, de borde tÀcka mer kod och exekveras först. I toppen av pyramiden Àr det tvÀrtom, sÄ antalet integrations- och UI-tester bör reduceras till det nödvÀndiga minimum. Personen i denna kedja Àr den dyraste, lÄngsamma och otillförlitliga resursen, sÄ han Àr i slutet och utför bara arbetet om de tidigare stegen inte hittade nÄgra defekter. Samma principer anvÀnds dock för att bygga en pipeline i delar som inte Àr direkt relaterade till testning!

Jag skulle vilja erbjuda en analogi i form av ett flerstegs vattenfiltreringssystem. Smutsigt vatten (förÀndringar med defekter) tillförs ingÄngen, vid utgÄngen mÄste vi fÄ rent vatten, i vilket alla oönskade föroreningar har eliminerats.

Implementera statisk analys i processen istÀllet för att anvÀnda den för att hitta buggar
Flerstegsfilter. KĂ€lla: Wikimedia Commons

Som ni vet Àr rengöringsfilter utformade sÄ att varje efterföljande kaskad kan filtrera bort en allt finare del av föroreningar. Samtidigt har grövre reningskaskader högre genomströmning och lÀgre kostnad. I vÄr analogi betyder detta att grindar av ingÄngskvalitet Àr snabbare, krÀver mindre anstrÀngning för att starta och Àr i sig mer opretentiösa i drift - och det Àr den sekvens i vilken de Àr byggda. Rollen för statisk analys, som, som vi nu förstÄr, kan rensa bort endast de grövre defekterna, Àr rollen som "lera"-nÀtet i början av filterkaskaden.

Statisk analys i sig förbĂ€ttrar inte kvaliteten pĂ„ slutprodukten, precis som ett "lerfilter" inte gör vatten drickbart. Och Ă€ndĂ„, i samband med andra delar av pipelinen, Ă€r dess betydelse uppenbar. Även om utgĂ„ngsstegen i ett flerstegsfilter potentiellt Ă€r kapabla att fĂ„nga allt som ingĂ„ngsstegen gör, Ă€r det tydligt vilka konsekvenser som kommer att bli av ett försök att nöja sig med enbart finreningssteg, utan ingĂ„ngssteg.

Syftet med "lerfÀllan" Àr att befria efterföljande kaskader frÄn att fÄnga upp mycket grova defekter. Till exempel bör Ätminstone personen som gör kodgranskningen inte distraheras av felaktigt formaterad kod och brott mot etablerade kodningsstandarder (som extra parenteser eller för djupt kapslade grenar). Buggar som NPE bör fÄngas upp av enhetstester, men om analysatorn redan före testet indikerar för oss att en bugg kommer att hÀnda, kommer detta att avsevÀrt pÄskynda ÄtgÀrden.

Jag tror att det nu Àr klart varför statisk analys inte förbÀttrar kvaliteten pÄ produkten om den anvÀnds ibland, och bör anvÀndas stÀndigt för att filtrera bort förÀndringar med grova defekter. FrÄgan om huruvida anvÀndning av en statisk analysator kommer att förbÀttra kvaliteten pÄ din produkt motsvarar ungefÀr att frÄga: "Kommer vatten som tas frÄn en smutsig damm att förbÀttras i drickskvalitet om det passerar genom ett durkslag?"

Implementering i ett Àldre projekt

En viktig praktisk frÄga: hur implementerar man statisk analys i den kontinuerliga integrationsprocessen som en "kvalitetsport"? NÀr det gÀller automatiska tester Àr allt uppenbart: det finns en uppsÀttning tester, ett misslyckande av nÄgon av dem Àr tillrÀcklig anledning att tro att monteringen inte klarade kvalitetsporten. Ett försök att installera en grind pÄ samma sÀtt baserat pÄ resultaten av en statisk analys misslyckas: det finns för mÄnga analysvarningar i den Àldre koden, du vill inte ignorera dem helt, men det Àr ocksÄ omöjligt att sluta skicka en produkt bara för att den innehÄller analysatorvarningar.

NÀr den anvÀnds för första gÄngen producerar analysatorn ett stort antal varningar pÄ alla projekt, varav de allra flesta inte Àr relaterade till produktens korrekta funktion. Det Àr omöjligt att korrigera alla dessa kommentarer pÄ en gÄng, och mÄnga Àr inte nödvÀndiga. NÀr allt kommer omkring vet vi att vÄr produkt som helhet fungerar, Àven innan vi införde statisk analys!

Som ett resultat Àr mÄnga begrÀnsade till tillfÀllig anvÀndning av statisk analys, eller anvÀnder den bara i informationslÀge, nÀr en analysatorrapport helt enkelt utfÀrdas under montering. Detta motsvarar frÄnvaron av nÄgon analys, för om vi redan har mÄnga varningar, sÄ gÄr förekomsten av en annan (oavsett hur allvarlig) nÀr du Àndrar koden obemÀrkt.

Följande metoder för att introducera kvalitetsgrindar Àr kÀnda:

  • StĂ€lla in en grĂ€ns för det totala antalet varningar eller antalet varningar dividerat med antalet kodrader. Detta fungerar dĂ„ligt, eftersom en sĂ„dan grind fritt tillĂ„ter förĂ€ndringar med nya defekter att passera, sĂ„ lĂ€nge deras grĂ€ns inte överskrids.
  • Fixar, vid ett visst tillfĂ€lle, alla gamla varningar i koden som ignorerade, och vĂ€grar bygga nĂ€r nya varningar uppstĂ„r. Denna funktion tillhandahĂ„lls av PVS-studio och vissa onlineresurser, till exempel Codacy. Jag hade inte möjligheten att arbeta i PVS-studio, för min erfarenhet av Codacy Ă€r deras huvudproblem att bestĂ€mma vad som Ă€r ett "gammalt" och vad som Ă€r ett "nytt" fel Ă€r en ganska komplex algoritm som inte alltid fungerar korrekt, sĂ€rskilt om filer Ă€r kraftigt modifierade eller bytt namn. Enligt min erfarenhet kunde Codacy ignorera nya varningar i en pull-förfrĂ„gan, samtidigt som den inte godkĂ€nde en pull-förfrĂ„gan pĂ„ grund av varningar som inte var relaterade till Ă€ndringar i koden för en given PR.
  • Enligt min mening Ă€r den mest effektiva lösningen den som beskrivs i boken Kontinuerlig leverans "spĂ€rrmetoden". Grundtanken Ă€r att antalet statiska analysvarningar Ă€r en egenskap för varje version, och endast Ă€ndringar Ă€r tillĂ„tna som inte ökar det totala antalet varningar.

Ratchet

Det fungerar sÄ hÀr:

  1. I det inledande skedet görs en registrering i metadata om frislÀppandet av antalet varningar i koden som analyserarna hittat. SÄ nÀr du bygger uppströms skriver din förvarshanterare inte bara "release 7.0.2", utan "release 7.0.2 som innehÄller 100500 XNUMX checkstyle-varningar." Om du anvÀnder en avancerad repository manager (som Artifactory) Àr det enkelt att lagra sÄdan metadata om din release.
  2. Nu jÀmför varje pull-begÀran, nÀr den Àr byggd, antalet resulterande varningar med antalet tillgÀngliga varningar i den aktuella versionen. Om PR leder till en ökning av detta antal, passerar koden inte kvalitetsgrinden för statisk analys. Om antalet varningar minskar eller inte Àndras gÄr det över.
  3. Vid nÀsta release kommer det omrÀknade antalet varningar att registreras igen i releasemetadata.

SÄ lite i taget men stadigt (som nÀr en spÀrrhake fungerar) kommer antalet varningar att tendera till noll. SjÀlvklart kan systemet luras genom att införa en ny varning, men korrigera nÄgon annans. Detta Àr normalt, eftersom det över en lÄng strÀcka ger resultat: varningar korrigeras, som regel, inte individuellt, utan i en grupp av en viss typ pÄ en gÄng, och alla lÀtt borttagbara varningar elimineras ganska snabbt.

Denna graf visar det totala antalet Checkstyle-varningar för sex mÄnaders drift av en sÄdan "spÀrr" pÄ ett av vÄra OpenSource-projekt. Antalet varningar har minskat med en storleksordning, och detta skedde naturligt, parallellt med produktutvecklingen!

Implementera statisk analys i processen istÀllet för att anvÀnda den för att hitta buggar

Jag anvÀnder en modifierad version av den hÀr metoden, som separat rÀknar varningar av projektmodul och analysverktyg, vilket resulterar i en YAML-fil med byggmetadata som ser ut ungefÀr sÄ hÀr:

celesta-sql:
  checkstyle: 434
  spotbugs: 45
celesta-core:
  checkstyle: 206
  spotbugs: 13
celesta-maven-plugin:
  checkstyle: 19
  spotbugs: 0
celesta-unit:
  checkstyle: 0
  spotbugs: 0

I alla avancerade CI-system kan ratchet implementeras för alla statiska analysverktyg utan att förlita sig pÄ plugins och tredjepartsverktyg. Varje analysator producerar sin egen rapport i ett enkelt text- eller XML-format som Àr lÀtt att analysera. Allt som ÄterstÄr Àr att skriva den nödvÀndiga logiken i CI-skriptet. Du kan se hur detta implementeras i vÄra open source-projekt baserade pÄ Jenkins och Artifactory hÀr eller hÀr. BÄda exemplen beror pÄ biblioteket ratchetlib: metod countWarnings() rÀknar xml-taggar i filer som genereras av Checkstyle och Spotbugs pÄ vanligt sÀtt, och compareWarningMaps() implementerar samma spÀrrhake, vilket ger ett fel nÀr antalet varningar i nÄgon av kategorierna ökar.

En intressant implementering av "spÀrren" Àr möjlig för att analysera stavningen av kommentarer, bokstavliga texter och dokumentation med aspell. Som du vet Àr inte alla ord som Àr okÀnda i standardlexikonet felaktiga nÀr du kontrollerar stavningen, de kan lÀggas till i anvÀndarlexikonet. Om du gör en anpassad ordbok till en del av projektets kÀllkod, sÄ kan stavningskvalitetsporten formuleras sÄ hÀr: köra aspell med en standard och anpassad ordbok borde inte hittar inga stavfel.

Om vikten av att fixa analysatorversionen

Sammanfattningsvis Àr poÀngen att notera att oavsett hur du implementerar analys i din leveranspipeline, mÄste versionen av analysatorn fixas. Om du tillÄter att analysatorn uppdateras spontant, kan nya defekter "dyka upp" vid montering av nÀsta pull-begÀran som inte Àr relaterade till kodÀndringar, utan Àr relaterade till det faktum att den nya analysatorn helt enkelt kan hitta fler defekter - och detta kommer att bryta din process för att acceptera pull-förfrÄgningar. Att uppgradera en analysator bör vara en medveten ÄtgÀrd. Fast fixering av versionen av varje monteringskomponent Àr dock i allmÀnhet ett nödvÀndigt krav och ett Àmne för en separat diskussion.

Resultat

  • Statisk analys kommer inte att hitta buggar för dig och kommer inte att förbĂ€ttra kvaliteten pĂ„ din produkt som ett resultat av en enda applikation. En positiv effekt pĂ„ kvaliteten kan endast uppnĂ„s genom att den stĂ€ndigt anvĂ€nds under leveransprocessen.
  • Att hitta buggar Ă€r inte alls huvuduppgiften för analys, de allra flesta anvĂ€ndbara funktioner Ă€r tillgĂ€ngliga i opensource-verktyg.
  • Implementera kvalitetsgrindar baserat pĂ„ resultaten av statisk analys i det allra första steget av leveranspipelinen, med hjĂ€lp av en "spĂ€rr" för Ă€ldre kod.

referenser

  1. Kontinuerlig leverans
  2. A. Kudryavtsev: Programanalys: hur man förstÄr att du Àr en bra programmerare rapportera om olika metoder för kodanalys (inte bara statisk!)

KĂ€lla: will.com

Köp pĂ„litlig hosting för webbplatser med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster