Műszaki cikk

Csendes csonkhibák diagnosztizálása Pascal PDF-könyvtárban

Amikor egy Delphi függvénytár vizuális keretrendszer nélküli build-konfigurációt kap, a helyettesítő osztályok azok, amelyekben a hibák laknak. Nem a platform, nem a fordító: a helyettesek. A PDFlibPas-nak van grafikai rétege, amely bittérkép, vászon, betű, metafájl és nyomtató megfelelőket szolgáltat a VCL nélküli buildekhez, és Free Pascalra való portolása felszínre hozta minden hibamódot, amelyet egy helyettes kaphat. Ezek tisztán diagnosztikai költség szerint rendeződnek, és a sorrend az ellentéte annak, amit az intuíció sugall

Egy dobó helyettes olcsón megtalálható; a kivétel megnevezi a metódust. Egy üres adatot visszaadó helyettes drága, mert a hiba okától több rétegre jelenik meg. Egy sikert jelentő helyettes a legrosszabb mind közül, mert a visszatérési kód érvényes, a hibakód nulla, nincs kivétel, és az egyetlen bizonyíték arra, hogy baj történt, a keletkezett bájtokban van

Csendes csonkhiba három alakja egy Pascal PDF-könyvtárban, diagnosztikai költség szerint rendezve, a dobó csonktól a sikeres visszatérésig üres kimenet felett
Egy dobó helyettes olcsón diagnosztizálható, az üres adat drága, és egy sikeres visszatérés üres artefaktum felett a legnehezebben található meg

Harmadik alak: érvényes képazonosító egy üres XObject felett

A vektoros metafájl-átalakító üres eljárástörzs volt a nem VCL-es konfigurációban. Minden fölötte tovább működött. Az EMF import belépési pontok és a vászonrögzítési belépési pont lefutottak a végéig, és törvényes képazonosítót adtak vissza, amelyet a hívó aztán egy oldalra helyezett. A fájlba egy nulla tartalomhosszúságú forma XObject került. Az oldal fehérrenderelt

Semmi nem jelentett problémát, és ez alá tartozik a függvénytár e funkcióhoz írt saját bemutatóprogramja is, amely üres oldalt rajzolt, és nem vett észre semmit. Nem volt ellenőrizendő hibás visszatérési érték, mert a hívássorozat valóban mind sikeres volt; az egyetlen hibás dolog az előállított folyam mérete volt. E hibaosztály diagnosztizálása azt jelenti, hogy másik kérdést teszünk fel: nem azt, hogy „elbukott-e a hívás”, hanem azt, hogy „hihető-e az artefaktum”. Egy nulla hosszúságú forma XObject, egy nulla pixeles kép, egy nulla tartalombájtos oldal — ezek az állítások fogják el

A javításnak két fele van, és a második fele könnyen felejtő. Először tegye az üres implementációt dobóvá, hogy a hibának egyáltalán legyen csatornája. Másodszor alakítsa át azt a kivételt null eredménnyé a képgyárban, és adjon null-ellenőrzéseket a két helyre, amelyek képazonosítót fogyasztanak, mert különben a „tiszta hiba” egyenesen hozzáférési sérüléssé válik, amikor az oldalfa semmire nem hivatkozik. Egy dobó csonk csak akkor javulás, ha a hívók fel voltak készülve egy olyan hibára, amelyet korábban soha nem tudtak kapni

Második alak: üres adat, három rétegre az összeomlástól

A metafájl-vászon helyettes nem töltötte ki fizikai méreteit. Az érték egy oldalgeometria-számításba osztódik bele, tehát a számítás nullát produkált, tehát a befoglaló doboz számítása nullával osztott. Egy csupasz kivételkezelő nyelte el azt, a képgyár null eredményt adott vissza, és a hozzáférési sérülés végül az oldalfában történt, amikor a nullát használták. Három réteg ok és tünet között, egy kivételkezelővel a közepén, amely törli a bizonyítékot

Egy üres metafájl-vászon helyettes hibalánca a PDFlibPas-ban, amely három réteggel a nullával osztás után ér hozzáférési sérüléshez
Az üres dimenzió nullára oszt egy geometriaszámítást, egy csupasz kezelő törli a kivételt, és a nullazonosító összedönti az oldalfát

Ugyanaz az egység még két példányt hordozott a mintából. A betűosztálynak üres Assign és konstruktor törzse volt, ami fontosabb, mint amilyennek látszik, mert a vászon betűtulajdonsága írásvédett: beleírni az egyetlen mód annak, hogy betűt szállítsanak, tehát egy üres implementáció a betűválasztást csendben hatástalanná teszi, és a szöveg abban jelenik meg, ami az alapértelmezés volt. És egy nullás hüvelykenkénti pixel érték minden olyan hívót, amely betűmetrikákból méretez vásznat, nulla-szor-nulla vászon előállítására késztetett, ami üres oldalt és sikeres visszatérést ad

// A keresendő alak egy helyettesítő egységben: olyan metódus,
// amely sem nem dob, sem nem tesz semmit. Mindkettő lefordul, és
// mindkettő „sikert” produkál kimenet nélkül
procedure TMetafileCanvasStandIn.Create(...);
begin
  // nincs örökölt hívás, nincs mezőinicializálás
end;

function TBitmapStandIn.LoadFromStream(Stream: TStream): Boolean;
begin
  Result := True;    // és a bittérkép továbbra is üres
end;

A széles struktúra, amely csak az első karaktert tartja meg

Ez egyáltalán nem helyettesprobléma, de ugyanabba a katalógusba tartozik, mert a tünet ugyanúgy távol van az okától. A nyomtatófelsorolási struktúrát a tizenkét karakterlánc-tagjával egybájtos karakterekre mutató pointertípusként deklarálták, miközben azt kitöltő függvény a felsorolási API széleskarakteres változata

A pointerek mérete azonos, tehát a struktúraelrendezés helyes, és semmi nem omlik össze. Az történik helyette, hogy egy UTF-16 karakterláncot egybájtos karakterláncként olvasva az első nulla bájtnál megáll, amely bármely ASCII nyomtatónév esetén a második karakter felső fele. Minden nyomtatónév pontosan egy karakterrel jött vissza. Lejjebb a névvalidáció elbukott, a nyomtatólétrehozás elbukott, és a nyomtatás elbukott a gép minden valós nyomtatóján, és egyik tünet sem mutat struktúradeklarációra

UTF-16 nyomtatónév egy karakterre csonkolva, miután egy széles Win32 struktúrát PAnsiChar tagokkal deklaráltak PWideChar helyett
Az egybájtos tagok egy UTF-16 nevet csak az első nulla bájtjáig olvasnak, így minden nyomtatónév pontosan egy karakterrel jön vissza
// Rossz: helyes méret, rossz elemtípus. Nincs fordítási hiba, nincs
// összeomlás, minden karakterlánc egy karakterre csonkolva
type
  TPrinterInfo2Wrong = record
    pServerName: PAnsiChar;
    pPrinterName: PAnsiChar;
    // ... további tíz
  end;

// Helyes: egy *W struktúrának végig széles tagjai vannak
type
  TPrinterInfo2W = record
    pServerName: PWideChar;
    pPrinterName: PWideChar;
    // ... további tíz
  end;

A belőle származó szabály mechanikus, és gondolkodás nélkül érdemes alkalmazni: minden Win32 struktúra esetében, amelynek neve W-re végződik, ellenőrizze mezőnként, hogy minden karakterlánc-tagja a széles változat-e. Az ANSI és a széles világ keverése sem fordítói diagnosztikát, sem összeomlást nem produkál, csak csendes csonkolást, és ugyanez fordítva igaz az ANSI változatokra

Egy csupasz kivételkezelő a tényleges ellenfél

Mindezeket a vizsgálatokat ugyanaz a szerkezet lassította: egy kezelő, amely mindent elfog, és hamis visszatérési értékké alakít. Érthető dolog egy képfeldolgozó köré írni, mert egy sérült kép nem dönthet meg egy dokumentumfeladatot. Ugyanakkor eszköz arra, hogy törölje azt az egyetlen információt, amelyre szüksége van

A gyakorlati válasz az, hogy a kezelőt átmenetileg hangosabbá tegye. A kivételosztály, az üzenet és a veremkiíratás kiírása a csupasz kezelőn belülről, egy hibakeresési feltétel alatt, egy megmagyarázatlan null-visszatérést névvel és hellyel rendelkező kivétellé alakít. A fenti három eset közül kettőben ez az egy lépés befejezte a vizsgálatot, mert a kivétel nullával osztás vagy hozzáférési sérülés volt egy helyettes metódusban, amelynek a neve mindent elmondott

Ellenőrzőlista helyettesítő út bevezetéséhez

Négy tétel, abban a sorrendben, ahogyan megtérülnek. Mielőtt helyettesítő osztályba hívna, olvassa el azokat a metódusokat, amelyeket használni készül, és erősítse meg, hogy mindegyiknek valós törzse van; egy üres törzs nem implementációs részlet, hanem hiányzó funkció. Részesítse előnyben a dobó helyetteseket a semleges értékeket visszaadókkal szemben, és párosítsa azt null-ellenőrzésekkel azokon a helyeken, ahol egy gyár mostantól törvényesen adhat vissza semmit. Ellenőrizze a funkciót az artefaktum megvizsgálásával, nem a visszatérési kóddal, mert az egész hibamód itt tiszta visszatérési kód üres artefaktum felett; egy dokumentum tényleges tartalmának bájtszintű bontása a leggyorsabb mód annak megtekintésére, és a fájlméret-audit cikk tárgyalja azt az eszközkészletet. És amikor egy funkciónak nincs működőképes helyettesítő implementációja, irányítsa az érintett mintákat arra az útra, amely működik, és mondja meg egy megjegyzésben, miért, ahelyett hogy olyan bemutatót hagyna, amely csendben üres kimenetet állít elő

A tágabb pont egyetlen függvénytáron túl is érvényes. Bármely kódbázis, amely feltételes második implementációval, mock-réteggel, fej nélküli móddal, platform shimvel rendelkezik, ki van téve a harmadik alaknak. Az ok, hogy ilyen jól rejtőzik, az, hogy minden minőségi kapu, amelyre egy csapat normálisan támaszkodik — visszatérési kódok, hibakódok, kivételek, kilépési státuszok — státuszcsatorna, és a harmadik alak mindet tisztán tartja. Csak a kimenet árulja el. Ez az ok is amögött, hogy ellenőrizetlen bemenet kezelésekor artefaktumokat vizsgálunk státuszok helyett, amelyről a nem megbízható PDF elemzési cikk ír, és amögött, hogy a renderelt kimenetet motorok között hasonlítjuk össze, nem pedig abba bízunk, hogy egyetlen motorra támaszkodunk, amelyről a többmotoros renderelés ír

A PDFlibPas natív Object Pascal PDF-könyvtár Delphi, C++Builder és Free Pascal rendszerekhez, és a nem VCL-es konfigurációja az, amely lehetővé teszi a fej nélküli és eszközláncok közötti buildeket; az aktuális konfigurációlefedettség a losLab PDF Developer Library terméklapon található