Tehnični članak

Diagnosticiranje tihih napak štubov v PDF knjižnici

Ko Delphi knjižnica pridobi gradbeno konfiguracijo brez vizualnega ogrodja, so nadomestni razredi tisto, kjer živijo hrošči. Ne platforma, ne prevajalnik: namestitve (stand-ins). PDFlibPas ima grafično plast, ki zagotavlja ustrežnike bitne slike, platna, pisave, metafila in tiskalnika za gradnje brez VCL, in prenos na Free Pascal je površil vsak način odpovedi, ki ga lahko ima namestitev. Razvrstijo se lepo po strošku diagnosticiranja in vrstni red je nasprotje tistega, kar nakazuje intuicija

Namestitev, ki sproži izjemo, je poceni najti; izjema poimenuje metodo. Namestitev, ki vrne prazne podatke, je draga, ker se odpoved pojavi nekaj plasti stran od svojega vzroka. Namestitev, ki vrne uspeh, je najslabša od vseh, ker je povratna koda veljavna, koda napake je nič, nobena izjema ni sprožena in edini dokaz, da je kaj šlo narobe, je v bajtih, ki so prišli ven

Trije liki tihe napake štuba v Pascalovi PDF knjižnici, razvrščeni po strošku diagnosticiranja, od štuba, ki sproža, do uspeha nad praznim izhodom
Sprožajoča namestitev je poceni za diagnosticiranje, prazni podatki so dragi, uspešni povratek nad praznim artefaktom pa je najslabše najti

Tretji lik: veljaven identifikator slike nad praznim XObjectom

Vektorski pretvornik metafila je bilo prazno telo procedure v ne-VCL konfiguraciji. Vse nad njim je ostalo delovati. Vstopne točke uvoza EMF in vstopna točka zajema platna so tekle do konca in vrnele legalen identifikator slike, ki ga je klicatelj nato postavil na stran. V datoteko je pristal formni XObject z dolžino vsebine nič. Stran se je upodobila bela

Nič ni poročalo o težavi in to vključuje lastni demonstracijski program knjižnice za to funkcijo, ki je narisal prazno stran in tega ni opazil. Ni bilo odpovedane povratne vrednosti za preverjanje, ker je zaporedje klicev resnično vsem uspelo; edino napačno je bila velikost proizvedenega toka. Diagnosticiranje tega razreda napak pomeni zastaviti drugačno vprašanje: ne »ali je klic odpovedal« ampak »ali je artefakt verjeten«. Formni XObject dolžine nič, slika nič pikslov, stran nič bajtov vsebine, to so trditve, ki ga ujamejo

Popravek ima dve polovici in druga polovica je enostavna za pozabo. Najprej naredite prazno implementacijo sprožajočo, tako da odpoved sploh ima kanal. Drugo, pretvorite to izjemo v rezultat null v tovarni slik in dodajte preglede null na dveh mestih, ki porabijo identifikator slike, ker sicer se »čista odpoved« obrne naravnost v kršitev dostopa, ko drevo strani dereferencira nič. Štub, ki meče, je samo izboljšava, če so bili klicatelji pripravljeni na odpoved, ki je prej nikoli niso mogli prejeti

Drugi lik: prazni podatki, tri plasti od sesutja

Namestitev platna metafila ni izpolnila svojih fizičnih mer. Ta vrednost se deli v izračun geometrije strani, zato je izračun proizvedel nič, zato se je izračun omejevalnega okvira delil z nič. Goli ročaj izjem je to pogoltnil, tovarna slik je vrnila rezultat null, kršitev dostopa pa se je končno zgodila v drevesu strani, ko je bila ničla uporabljena. Tri plasti med vzrokom in simptomom, z ročajem izjem na sredini, ki briše dokaze

Veriga odpovedi prazne namestitve platna metafila v PDFlibPas, ki doseže kršitev dostopa tri plasti po deljenju z nič
Prazna mera deli izračun geometrije na nič, goli ročaj izbriše izjemo, identifikator null pa sesuje drevo strani

Ista enota je imela še dva primerka vzorca. Razred pisave je imel prazni telesa Assign in konstruktorja, kar je pomembnejše, kot izgleda, ker je lastnost pisave platna samo za branje: dodeljevanje vanjo je edini način dostave pisave, zato prazna implementacija naredi izbiro pisave tiho neučinkovito in besedilo pride ven v katerem koli privzetem. In vrednost pik na palec nič je naredila vsakega klicatelja, ki dimensionira platno iz metrik pisave, proizvajalca platna nič-krat-nič, kar da prazno stran in uspešen povratek

// Liki, ki ga iskati v namestitveni enoti: metoda, ki ne sproži
// izjeme in ne počne ničesar. Oba se prevodita in oba proizvedeta
// »uspeh« brez izhoda
procedure TMetafileCanvasStandIn.Create(...);
begin
  // brez klica inherited, brez inicializacije polj
end;

function TBitmapStandIn.LoadFromStream(Stream: TStream): Boolean;
begin
  Result := True;    // in bitna slika je še vedno prazna
end;

Široka struktura, ki obdrži samo prvi znak

Ta sploh ni težava namestitve, ampak pripada v isti katalog, ker je simptom enako daleč od vzroka. Struktura številčenja tiskalnikov je bila deklarirana z vsemi dvanajstimi člani nizov tipiziranimi kot kazalce na enobajtne znake, medtem ko je funkcija, ki jo izpolni, različica širokih znakov API-ja številčenja

Velikosti kazalcev so identične, zato je razporeditev strukture pravilna in nič se ne sesuje. Tisto, kar se zgodi namesto tega, je, da branje niza UTF-16 kot enobajtnega niza ustane na prvem ničelnem bajtu, ki je za vsako ime tiskalnika ASCII zgornja polovica drugega znaka. Vsako ime tiskalnika se je vrnilo kot natanko en znak. Nizvodno je validacija imen odpovedala, ustvarjanje tiskalnika je odpovedalo in tiskanje je odpovedalo za vsak pravi tiskalnik na stroju, in noben od teh simptomov ne kaže na deklaracijo strukture

Ime tiskalnika UTF-16 skrajšano na en znak, potem ko je bila široka struktura Win32 deklarirana s člani PAnsiChar namesto PWideChar
Enobajtni člani berejo ime UTF-16 samo do prvega ničelnega bajta, zato se vsako ime tiskalnika vrne z natanko enim znakom
// Napačno: prava velikost, napačen tip elementa. Brez napake
// prevajanja, brez sesutja, vsak niz skrajšan na en znak
type
  TPrinterInfo2Wrong = record
    pServerName: PAnsiChar;
    pPrinterName: PAnsiChar;
    // ... še deset
  end;

// Pravilno: struktura *W ima široke člane povsod
type
  TPrinterInfo2W = record
    pServerName: PWideChar;
    pPrinterName: PWideChar;
    // ... še deset
  end;

Pravilo, ki iz tega izhaja, je mehansko in vredno uporabe brez razmišljanja: za vsako strukturo Win32, katere ime se konča na W, preverite, da je vsak član niza široka različica, polje po polju. Mešanje svetov ANSI in širokih ne proizvede niti diagnostike prevajalnika niti sesutja, samo tiho krajšanje, in enako velja v obratni smeri za različice ANSI

Goli ročaj izjem je pravi nasprotnik

Vsaka od teh preiskav je bila upočasnjena s strani istega konstrukta: ročaj, ki ujame vse in to pretvori v povratno vrednost lažno. Je razumna stvar za pisanje okoli dekodirnika slik, ker pokvarjena slika ne bi smela podreti dokumentnega opravila. Je tudi naprava za brisanje enega samega kosa informacije, ki ga potrebujete

Praktični odziv je narediti ročaj začasno glasnega. Odlaganje razreda izjeme, sporočila in sledi (backtrace) iz znotraj golega ročaja, pod pogojnim razhroščevalnim stavkom, pretvori nerazložen povratek null v poimenovano izjemo z lokacijo. V dveh od treh primerov zgoraj je ta en korak končal preiskavo, ker je bila izjema deljenje z nič ali kršitev dostopa v metodi namestitve, katere ime je reklo vse

Kontrolni seznam za sprejetje poti namestitve

Štirje predmeti, v vrstnem redu, v katerem se izplačajo. Pred klicem v nadomestni razred preberite metode, ki jih nameravate uporabiti, in potrdite, da ima vsak pravo telo; prazno telo ni podrobnost implementacije, je manjkajoča funkcija. Dajte prednost namestitvam, ki sprožijo izjemo, pred namestitvami, ki vračajo nevtralne vrednosti, in to sparite s pregledi null na mestih, kjer tovarna lahko zdaj upravičeno vrne nič. Preverite funkcijo z pregledom artefakta, ne povratne kode, saj je celoten način odpovedi tukaj čista povratna koda nad praznim artefaktom; razčlenitev na ravni bajtov tega, kaj dokument dejansko vsebuje, je najhitrejši način, da to vidite, in članek o reviziji velikosti datoteke pokriva to orodje. In ko funkcija nima preživetja sposobne nadomestne implementacije, usmerite prizadete vzorce na pot, ki deluje, in recite zakaj v komentarju, namesto da pustite demonstracijo, ki tiho proizvaja prazen izhod

Širša točka velja daleč onkraj ene knjižnice. Vsaka kodna baza s pogojno drugo implementacijo, plastjo mock, brezglavnim načinom, platformskim šimom, je izpostavljena tretjemu liku. Razlog, zakaj se tako dobro skriva, je, da je vsaka vrata kakovosti, na katera se ekipa običajno zanese — povratne kode, kode napak, izjeme, izstopna stanja — statusni kanal, tretji lik pa vse te obdrži čiste. Samo izhod ga izda. To je tudi utemeljitev za pregledovanje artefaktov namesto stanj pri rokovanju z nezaupanim vhodom, opisano v članku o razčlenjevanju nezaupanega PDF, in za primerjavo upodobljenega izhoda čez motorje namesto zaupanja enemu, opisano v večmotornem upodabljanju

PDFlibPas je lastna PDF knjižnica v Object Pascalu za Delphi, C++Builder in Free Pascal, njegova ne-VCL konfiguracija pa je tisto, kar naredi brezglavne gradnje in gradnje čez orodjarne mogoče; trenutna pokritost konfiguracij je navedena na strani produkta losLab PDF Developer Library