<?xml version="1.0" ?>
<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
  <!ENTITY kcachegrind '<application
>KCachegrind</application
>'>
  <!ENTITY cachegrind "<application
>Cachegrind</application
>">
  <!ENTITY calltree "<application
>Calltree</application
>">
  <!ENTITY callgrind "<application
>Callgrind</application
>">
  <!ENTITY valgrind "<application
>Valgrind</application
>">
  <!ENTITY oprofile "<application
>OProfile</application
>">
  <!ENTITY kappname "&kcachegrind;">
  <!ENTITY package "kdesdk">
  <!ENTITY % addindex "IGNORE">
  <!ENTITY % Estonian  "INCLUDE">
]>

<!-- ................................................................ -->

<book lang="&language;">

<bookinfo>
<title
>&kcachegrind;i käsiraamat</title>

<authorgroup>
<author
><firstname
>Josef</firstname
> <surname
>Weidendorfer</surname
> <affiliation
> <address
><email
>Josef.Weidendorfer@gmx.de</email
></address>
</affiliation>
</author>

<othercredit role="translator"
><firstname
>Marek</firstname
><surname
>Laane</surname
><affiliation
><address
><email
>bald@starman.ee</email
></address
></affiliation
><contrib
>Tõlge eesti keelde</contrib
></othercredit
> 

</authorgroup>

<copyright>
<year
>2002-2004</year>
<holder
>Josef Weidendorfer</holder>
</copyright>
<legalnotice
>&FDLNotice;</legalnotice>

<date
>2004-07-27</date>
<releaseinfo
>0.4.6</releaseinfo>

<abstract>
<para
>&kcachegrind; on &kde; töökeskkonna tarbeks loodud profileerimisandmete kuvamise tööriist. </para>
</abstract>

<keywordset>
<keyword
>KDE</keyword>
<keyword
>kdesdk</keyword>
<keyword
>Cachegrind</keyword>
<keyword
>Callgrind</keyword>
<keyword
>Valgrind</keyword>
<keyword
>Profileerimine</keyword>
</keywordset>

</bookinfo>


<chapter id="introduction">
<title
>Sissejuhatus</title>

<para
>&kappname; on profileerimistööriistade tekitatud andmete brauser. Käesolevas peatükis selgitame, mida tähendab üldse profileerimine, kuidas seda tehakse ning räägime veidi olemasolevatest profileerimistööriistadest. </para>

<sect1 id="introduction-profiling">
<title
>Profileerimine</title>

<para
>Programmi luues soovid kindlasti lõpuks muuta selle ka võimalikult kiireks (aga samas ikka töökindlaks!). Aeg on mõistagi kallis väärtus ja optimeerimisfunktsioone kasutatakse harva. Nii on sul vaja teada saada, milline osa programmist kulutab kõige rohkem aega. </para>

<para
>Jadakoodi puhul piisab tavaliselt programmide käituskarakteristika, näiteks funktsioonides ja koodiridades veedetud ajahulga statistiliste andmete kogumisest. Seda nimetatakse profileerimiseks. Vastav programm töötab profileerimistööriista kontrolli all, mis programmi täitmise lõppedes väljastab kokkuvõtte. Paralleelkoodi puhul on aga asjad teisiti - peamiselt tekitab jõudlusprobleeme see, kui üks protsessor ootab teiselt andmeid. Et sellist ooteaega ei ole tavaliselt võimalik väga lihtsalt muuta, on siin mõttekas genereerida ajatempliga sündmuste jäljed. Selliseid andmeid KCachegrind ei kuva. </para>

<para
>Profileerimisandmete analüüsimisel peaks olema üsna lihtne näha koodi nii-öelda pudelikaelu ning seejärel parandada näiteks väljakutsete esitamise viisi ja optimeerida koodi erinevaid piirkondi. Pärast seda saab uue profileerimisega kontrollida, kui edukas optimeerimine oli. </para>
</sect1>

<sect1 id="introduction-methods">
<title
>Profileerimismeetodid</title>

<para
>Koodi piirkonna (nt. funktsiooni) täitmisele kuluva aja täpseks mõõtmiseks või selle käigus toimuvate sündmuste jäädvustamiseks tuleb antud piirkonna ette ja järele lisada mõõtmiskood. See arvestab aega või sündmusi ning arvutab erinevusi. See tähendab, et enne täitmist on vaja muuta originaalkoodi. Seda nimetatakse instrumentatsiooniks. Instrumentatsiooniga võib tegelda nii programmeerija ise, kompilaator või käitussüsteem. Et huvipakkuvad piirkonnad on enamasti pesastatud, mõjutab mõõtmise teostamine otseselt ka mõõtmistulemusi. Seepärast tuleb instrumentatsiooniga olla väga hoolas ning mõõtmise tulemusi enne järelduste tegemist põhjalikult uurida. Pole midagi parata - jõudlusanalüüs on täppismõõtmise korral keeruline ja aeganõudev ettevõtmine.</para>

<para
>Täppismõõtmise muudavad võimalikuks tänapäevaste protsessorite pakutavad riistvaralised loendurid (sealhulgas ajalist kasvu arvestavad loendurid), mis arvestavad iga sündmuse aega. Ilma loendurita tuleks meil sündmuste omistamisel koodi piirkondadele tegelda eraldi iga sündmusega, suurendades ise loenduri näitu antud koodi piirkonnas. Tarkvaraliselt ei ole see mõistagi võimalik, kuid eeldades, et sündmuste jaotus koodis on ühetaoline, kui me uurime iga sündmuse asemel ainult iga n-ndat sündmust, siis selle tarbeks on loodud häälestatav mõõtmismeetod, mida nimetatakse diskreetimiseks. Ajapõhine diskreetimine (TBS, Time Based Sampling) kasutab taimerit ja uurib regulaarselt programmi loendurit programmikoodi histogrammi loomiseks. Sündmusepõhine diskreetimine (EBS, Event Based Sampling) kasutab ära tänapäevaste protsessorite riistvaralisi loendureid ning töötab režiimis, milles loenduri alatäite korral kutsutakse välja katkestusetöötleja vastava sündmusjaotuse histogrammi tekitamiseks; töötlejas taaslähtestatakse sündmuseloendur alati diskreetimismeetodi 'n'-väärtusele. Diskreetimise eeliseks on see, et koodi ei tule muuta, kuid see on siiski omamoodi kompromiss: mainitud eeldus võib olla korrektne, kui n on väike, aga mida väiksem on n, seda suurem on katkestusetöötleja üldkulu.</para>

<para
>Teine mõõtmisviis on selle matkimine, mis juhtub arvutis antud koodi täitmisel, s.t. täitmise simulatsioon. Simulatsioon arvestab alati enam-vähem täpse masinamudeliga, kuid väga konkreetsete nõuete korral, kus on vaja äärmiselt realistlikku täpsust, võib simulatsiooniaeg olla tegelikkuses talumatult suur. Simulatsiooni eeliseks on see, et koodi võib ilma häirivaid tulemusi kartmata lisada suvalist, ka keerukat mõõte/simulatsioonikoodi. Lisamine otse enne täitmist (niinimetatud käitusinstrumentatsioon) originaal-binaarfaili kasutades on kasutajale äärmiselt mugav, sest puudub vajadus taaskompileerimise järele. Simulatsioon on mõttekas siis, kui simuleeritakse ainult teatud masina osi lihtsa mudeliga. Teine positiivne joon on see, et lihtsa mudeli tekitatud tulemusi on enamasti suhteliselt lihtsam mõista - sageli ongi tegeliku riistvara probleemiks see, et tulemustesse satuvad masina erinevate osade omavahel kattuvad efektid.</para>
</sect1>

<sect1 id="introduction-tools">
<title
>Profileerimistööriistad</title>

<para
>Tuntuim sellistest tööriistadest on GCC profileerimistööriist <application
>gprof</application
>. Selle kasutamiseks tuleb programm kompileerida võtmega <option
>-pg</option
>, misjärel programmi käivitamine tekitab faili <filename
>gmon.out</filename
>, mille käsuga <command
>gprof</command
> saab muuta inimsilmale arusaadavale kujule. Puuduseks on aga vajadus kasutada kompileerimist, et ette valmistada käivitatav fail, mis peab olema staatiliselt lingitud. Meie kasutame kompilaatori genereeritud instrumentatsiooni, mis mõõdab funktsioonides esinevaid väljakutseteid ja arvestab vastavalt väljakutseid, koos TBS-iga, mis tagab koodi ajajaotuse histogrammi. Nende kahe infokogumi põhjal saab heuristiliselt arvutada funktsioonide kumulatiivset aega, s.t. aega, mis veedetakse funktsioonis koos kõigi sellest välja kutsutud funktsioonidega. </para>

<para
>Sündmuste toimumise täppismõõtmiseks on olemas selliste funktsioonidega teegid, mis suudavad lugeda riistvaraliste loendurite andmeid. Tuntumad neist on PerfCtr Linuxi jaoks ja arhitektuurist sõltumatud PAPI ning PCL teegid. Täppismõõtmine vajab siiski koodi instrumentatsiooni, nagu juba eespool märgitud. Teekide puhta kasutamise asemel võib kasutada ka automaatse instrumentatsiooni süsteeme, nagu ADAPTOR (FORTRAN-i lähtekoodi instrumentatsioon) või DynaProf (koodi sisestamine DynInst'i vahendusel).</para>

<para
>&oprofile; on diskreetimist kasutav Linuxi süsteemse profileerimise tööriist.</para>

<para
>Mitmes mõttes on profileerimiseks väga mugav kasutada Cachegrindi või Callgrindi, mis on käitusinstrumentatsiooni raamistikku &valgrind; kasutavad simulaatorid. Et nende puhul pole vaja ligipääsu riistvaralistele loenduritele (see on tänapäeva Linuxi distributsioonide puhul sageli keeruline) ning profileeritavad binaarfailid saab jätta muutmata, on need heaks alternatiiviks muudele profileerimistööriistadele. Simulatsiooni miinuspoole - aegluse - saab osaliselt kompenseerida simulatsiooni ainult huvipakkuvates programmide osades ning isegi võib-olla ainult silmuste mõningates kordustes ette võttes. Mõõtmis/simulatsiooniinstrumentatsioonita on Valgrindi aegluskoefitsient kõigest vahemikus 3 kuni 5. Pealegi võib juhul, kui sulle pakub huvi ainult väljakutsete graafik ja väljakutsete loendamine, lülitada välja vahemälu simulaatori. </para>

<para
>Vahemälu simulatsioon on esimene samm reaalaja hindamisel, kuivõrd tänapäevaste süsteemide korral on käitus äärmiselt tundlik niinimetatud vahemälu (väikesed ja kiired puhvrid, mis kiirendavad korduvaid pöördumisi ühtede ja samade mälupesade poole) kasutamisele. &cachegrind; simuleerib vahemälu mällupöördumisi jälitades. Saadud andmed hõlmavad instruktsioonide ja andmete mällupöördumiste ning 1. ja 2. taseme vahemälu vajakute arvu ja seondavad need käivitatava programmi lähteridade ja funktsioonidega. Tüüpiliste protsessorite vajakute latentsusaega kasutades saab vajakuid kombineerides hinnata kulunud aega. </para>

<para
>Callgrind on &cachegrind;i laiend, mis loob käigult programmi väljakutsete graafiku, &ie; näitab, millised funktsioonid milliseid välja kutsuvad ja kui palju sündmusi funktsiooni töö ajal ette tuleb. Lisaks saab profileerimisandmeid koondada eraldi lõimedesse ja väljakutsete ahelatesse. See pakub profileerimisandmeid instruktsiooni tasemel, mis lubab dissambleeritud koodi annoteerimist. </para>
</sect1>

<sect1 id="introduction-visualization">
<title
>Kuvamine</title>

<para
>Profileerimistööriistad loovad tüüpiliselt päris palju andmeid. Soov võimalikult vähese vaevaga väljakutsete graafikus liikuda ning kiiresti lülituda funktsioonide sorteerimise režiimide ja erinevate sündmusetüüpide näitamise vahel ongi motiveerinud selleks kõige paremini sobivate graafiliste kasutajaliideste loomist. </para>

<para
>&kappname; on nende soovide täitja, võimaldades kuvada profileerimisandmeid. Kuigi algselt loodi see &cachegrind;i ja &calltree; andmete võimalikult hõlpsat sirvimist silmas pidades, on olemas konverterid, mis suudavad näidata ka muude tööriistade profileerimisandmeid. Lisas kirjeldame ka Cachegrindi/Callgrindi failivormingut. </para>

<para
>Lisaks funktsioonide nimekirjale, mida saab sorteerida kas tavalise või kumulatiivse kulu järgi ning rühmitada ka lähtefaili, jagatud teegi või C++-klassi järgi, pakub &kappname; valitud funktsiooni korral veel mitmeid kuvamisvaateid, nimelt <itemizedlist>
<listitem
><para
>väljakutsete graafiku vaade, mis näitab väljakutsete graafiku osa valitud funktsiooni piirkonnas,</para>
</listitem>
<listitem
><para
>puukaardivaade, mis näitab pesastatud väljakutsete seoseid, samuti kumulatiivset kulu, mis võimaldab kiiresti visuaalselt tuvastada problemaatilised funktsioonid,</para>
</listitem>
<listitem
><para
>lähtekoodi ja disassembleri annotatsioonivaade, mis võimaldab näha kuludetaile lähtekoodi ridade ja assembleri instruktsioonide suhtes.</para>
</listitem>
</itemizedlist>

</para>
</sect1>
</chapter>

<chapter id="using-kcachegrind">
<title
>&kcachegrind;i kasutamine</title>

<sect1 id="using-profile">
<title
>Kuvatavate andmete tekitamine</title>

<para
>Kõik, kes jõudlust hinnata soovivad, tahavad mõistagi näha vastavaid andmeid, mõõtes profileerimistööriistaga rakenduse käituskarakteristika aspekte. &kcachegrind; ei paku omalt poolt ühtki profileerimistööriista, kuid tuleb sellega ometi hästi toime koostöös &callgrind;iga ning suudab konverteri vahendusel kuvada ka &oprofile;'i loodud andmeid. Kuigi käesolev käsiraamat ei ole pühendatud nende tööriistade põhjalikule tutvustamisele, räägime järgnevalt siiski neist lühidalt. </para>

<sect2>
<title
>&callgrind;</title>

<para
>&callgrind;i leiab aadressilt <ulink url="http://kcachegrind.sf.net"
> http://kcachegrind.sf.net</ulink
>. Pane tähele, et varem nimetati seda &calltree;'ks, aga see nimi oli eksitav. </para>

<para
>Kõige levinum on selle kasutamiseks käivitada rakendus käsurealt <application
>callgrind</application
>i sees kujul <blockquote
><para
><command
>callgrind minurakendus minuargumendid</command
></para
></blockquote
> Programmi töö lõppemisel luuakse fail <filename
>callgrind.out.pid</filename
>, mille saab laadida &kcachegrind;i. </para>

<para
>Veidi täiustatum viis on salvestada välja profileerimisandmed rakenduste määratud funktsiooni väljakutsumisel. Näiteks <command
>konqueror</command
>i korral ainult veebilehekülje renderdamise profileerimisandmete nägemiseks võib lasta andmed salvestada ainult siis, kui valitakse menüükäsk Vaade->Laadi uuesti. See vastab väljakutsele <symbol
>KonqMainWindow::slotReload</symbol
>, milleks kasuta käsku <blockquote
><para
><command
> callgrind --dump-before=KonqMainWindow::slotReload konqueror </command
></para
></blockquote
> See loob rea profileerimisandmete faile, mille nimele lisatakse lõppu järjestikku kasvavad numbrid. Luuakse ka ilma sellise numbrita fail, mille nime lõpus on ainult protsessi PID - selle faili avamisel &kcachegrind;is avatakse ka teised ning neid võib näha osade ülevaates ja osade nimekirjas. </para>

</sect2>

<sect2>
<title
>&oprofile;</title>

<para
>&oprofile;'i leiab aadressilt <ulink url="http://oprofile.sf.net"
> http://oprofile.sf.net</ulink
>. Järgi paigaldamiseks veebileheküljel toodud juhiseid, aga enne seda võiksid kontrollida, ega sinu distributsioon juba seda valmispakituna ei paku (nagu teeb näiteks SuSE). </para>

<para
>Süsteemne profileerimine on lubatud ainult administraatorile (root), kes ainsana saab jälgida kogu süsteemis toimuvat. Seepärast tuleb kõike järgnevat sooritada administraatori õigustes. Esmalt seadista profileerimisprotsess, kasutades graafilist kasutajaliidest <command
>oprof_start</command
> või käsureatööriista opcontrol. Standardseadistus peaks olema taimerirežiim (TBS, vaata selle kohta sissejuhatust). Mõõtmise käivitamiseks anna käsk <command
>opcontrol -s</command
>. Seejärel käivita vajalik rakendus ning anna hiljem käsk <command
>opcontrol -d</command
>. See kirjutab mõõtmistulemused failidena kataloogi <filename
>/var/lib/oprofile/samples/</filename
>. Andmete kuvamiseks &kcachegrind;is anna tühjas kataloogis käsk: <blockquote
><para
><command
> opreport -gdf | op2callgrind </command
></para
></blockquote
> See tekitab hulga faile - üks iga süsteemis töötava programmi kohta. Kõik need saab ükshaaval &kcachegrind;is avada. </para>

</sect2>
</sect1>

<sect1 id="using-basics">
<title
>Kasutajaliidese põhitõed</title>

<para
>Kui käivitada &kcachegrind; käsurealt profiiliandmetega või avada need menüükäsuga Fail->Ava, näed vasakul külgriba funktsioonide nimekirjaga ning paremal, põhiosas kuvatakse valitud funktsiooni. Põhiala on jagatud mitmeks, et näidata korraga mitut erinevat kuva. </para>

<para
>Esmakäivitusel on põhiala jagatud ülemiseks ja alumiseks osaks, milles mõlemas paikneb terve rida erinevatel kaartidel asuvaid kuvasid. Kuvade liigutamiseks kasuta kaartide kontekstimenüüd ja liiguta nende eraldajaid. Erinevate kuvapaigutuste kiireks vahetamiseks vali Vaade->Paigutus->Dubleeri, muuda paigutust ja liigu ühelt paigutuselt teisele menüükäsuga Vaade->Paigutus->Järgmine (või kasuta vastavaid kiirklahve, mis on veelgi kiirem võimalus). </para>

<para
>Kuvamisel on oluline aktiivne sündmuse tüüp: &callgrind;i korral on need näiteks vahemälu vajakud või tsükli hinnang, &oprofile;'i korral lihtsaimal juhul "taimer". Sündmuse tüüpi saab muuta liitkastis tööriistaribal või sündmuste tüüpide vaates. Esimese ülevaate käituskarakteristikast saab vasakul nimekirjas funktsiooni <symbol
>main</symbol
> valides ning väljakutsete graafiku kuva uurides: nii näed, mis sinu programmis tegelikult toimub. Pane tähele, et väljakutsete graafiku vaates näidatakse ainult suurte sündmuste arvuga funktsioone. Graafikus mõnel funktsioonil topeltklõpsu tehes see muutub ning näitab väljakutsutud funktsioone valitud funktsiooni lähikonnas. </para>

<para
>Graafilise kasutajaliidese põhjalikumaks tundmaõppimiseks tasuks lisaks käesolevale käsiraamatule tutvuda dokumentatsiooniga veebileheküljel <ulink url="http://kcachegrind.sf.net"
> http://kcachegrind.sf.net</ulink
>. Lisaks sellele pakub iga &kcachegrind;i element abivõimalust <quote
>Mis see on?</quote
> </para>
</sect1>

</chapter>


<chapter id="kcachegrind-concepts">
<title
>Peamised kontseptsioonid</title>

<para
>Selles peatükis seletame mõningaid &kcachegrind;i kontseptsioone ja tutvustame liideses kasutatavaid mõisteid. </para>

<sect1 id="concepts-model">
<title
>Profileerimisandmete andmemudel</title>

<sect2>
<title
>Kuluolemid</title>

<para
>Sündmuste tüüpide (näiteks L2 vajakute) kuluarvestus omistatakse kuluolemitele, mis on seotud antud programmi lähtekoodi või andmestruktuuridega. Kuluolemid ei pruugi olla lihtsalt koodi- või andmepositsioonid, need võivad olla ka positsioonijärjendid. Nii on näiteks väljakutsel allikas ja sihtmärk, andmeaadressil aga andmetüüp ja koodipositsioon, kus eraldus aset leiab. </para>

<para
>KCachegrind tunneb ja kasutab järgmisi kuluolemeid. Lihtsad positsioonid: <itemizedlist
> <listitem
><para
> Instruktsioon. Assembleri instruktsioon määratud addressiga. </para
></listitem
> <listitem
><para
> Funktsiooni lähtekoodi rida. Kõik instruktsioonid, mida kompilaator (silumisinfo vahendusel) seob antud lähtekoodi reaga, mis on määratud lähtekoodi faili nime ja reanumbriga ja mis käivitatakse mingi funktsiooni kontekstis. Viimast on vaja seepärast, et inline-funktsiooni sees esinev lähtekoodi rida võib esineda mitme funktsiooni kontekstis. Instruktsioonid, mis ei ole seotud reaalse lähtekoodi reaga, seotakse reaga 0 failis "???". </para
></listitem
> <listitem
><para
> Funktsioon. Funktsioon koosneb antud fuktsiooni kõigist lähtekoodi ridadest. Funktsiooni määrab selle nimi ja asukoht mingis binaarobjektis, kui see on saadaval. Viimast on vaja seepärast, et ühe programmi binaarobjektid võivad sisaldada samanimelisi funktsioone (neile pääseb ligi näiteks dlopen/dlsym abil; käituslinkur lahendab funktsioonid kasutatava binaarobjekti etteantud otsingujärjekorras). Kui profileerimistööriist ei suuda tuvastada funktsiooni sümbolnime (kui näiteks pole saadaval silumisinfot), kasutatakse tavaliselt esimesena käivitatud instruktsiooni aadressi või "???". </para
></listitem
> <listitem
><para
> Binaarobjekt. Kõik funktsioonid, mille kood asub antud binaarobjekti vahemikus kas peamises käivitatavas failis või jagatud teegis. </para
></listitem
> <listitem
><para
> Lähtekoodi fail. Kõik funktsioonid, mille esimene instruktsioon on seotud antud lähtekoodi faili reaga. </para
></listitem
> <listitem
><para
> Klass. Funktsioonide sümbolnimed on tavaliselt hierarhiliselt korraldatud nimeruumidesse, nt. C++ nimeruumidesse, või objektorienteeritud keele klassidesse. Nii võib klass sisaldada klassi funktsioone või ka põimitud klasse. </para
></listitem
> <listitem
><para
> Profiili osa. Profileerimise mingi ajalõik antud lõime ID-ga, protsessi ID-ga ja käivitatud käsureaga. </para
></listitem
> </itemizedlist
> Nagu nimekirjast näha, võib üks kuluolemite kogum sageli defineerida mõne muu kuluolemi, mistõttu on olemas kuluolemite kumulatiivne hierarhia, nagu ülalt peaks niigi selguma. </para>

<para
>Positsioonijärjendid: <itemizedlist
> <listitem
><para
> Väljakutse instruktsiooniaadressilt sihtfunktsioonile. </para
></listitem
> <listitem
><para
> Väljakutse lähtekoodi realt sihtfunktsioonile. </para
></listitem
> <listitem
><para
> Väljakutse lähtekoodi funktsioonilt sihtfunktsioonile. </para
></listitem
> <listitem
><para
> Tingimuslik/tingimusteta hüpe lähtekoodist sihtinstruktsioonile. </para
></listitem
> <listitem
><para
> Tingimuslik/tingimusteta hüpe lähtekoodist sihtreale. </para
></listitem
> </itemizedlist
> Hüpped funktsioonide vahel ei ole lubatud, sest neil ei ole ka väljakutsete graafikul mingit mõtet. Sestap tuleb sellised konstruktsioonid, nagu eranditöötlus ja C-keelele omased pikad hüpped vajaduse korral transleerida väljakutsete pinus. </para>

</sect2>



<sect2>
<title
>Sündmuste tüübid</title>

<para
>Profileerimisandmetes saab määrata igasuguseid sündmuste tüüpe neile nime andes. Nende kulu kuluolemi suhtes on 64-bitine täisarv. </para>
<para
>Sündmuste tüüpe, mille kulu on määratletud profileerimisandmete failis, nimetatakse reaalseteks sündmusteks. Lisaks võib määrata viisi, kuidas tuletatakse reaalsetest sündmustest muid sündmuste tüüpe. Viimaseid nimetatakse järglassündmusteks. </para>
</sect2>

</sect1>

<sect1 id="concepts-state">
<title
>Kuvamisolek</title>

<para
>KCachegrindi akna kuvamisolek näitab: <itemizedlist
> <listitem
><para
> esmast ja sekundaarset sündmuse tüüpi, </para
></listitem
> <listitem
><para
> funktsiooni rühmitust (kasutusel funktsiooni profiili nimekirjas ja olemi värvimisel), </para
></listitem
> <listitem
><para
> profiili osasid, mille kulu kuva arvestab, </para
></listitem
> <listitem
><para
> aktiivset kuluolemit (nt. funktsiooni profiili dokilt valitud funktsiooni), </para
></listitem
> <listitem
><para
> valitud kuluolemit. </para
></listitem
> </itemizedlist
> See olek mõjutab kuva. </para>
<para
>Kuvatakse alati üht, aktiivset kuluolemit. Kui kuluolemi korral ei ole kuvamine võimalik, siis seda ka ei näidata (nt. kui valid rühmituse nimekirjas topeltklõpsuga ELF-objekti, ei ole ELF-objekti korral kuidagi võimalik lähtekoodi annotatsioon). </para>
<para
>Nii näitab aktiivse funktsiooni korral väljakutsutute nimekiri kõiki funktsioone, mida aktiivne funktsioon välja kutsub. Nende seast võib valida mõne seda aktiivseks muutmata. Kui väljakutsete graafikut näidatakse selle kõrval, valib see automaatselt sama funktsiooni. </para>

</sect1>

<sect1 id="concepts-guiparts">
<title
>Graafilise kasutajaliidese komponendid</title>

<sect2>
<title
>Külgdokid</title>
<para
>Külgdokid ehk külgribad on aknad, mida võib seada mis tahes KCachegrindi peaakna serva. Need näitavad alati mingi reegli kohaselt sorteeritud kuluolemite nimekirja. <itemizedlist>
<listitem
><para
>Funktsiooni profiil. Funktsiooni profiil on funktsioonide nimekiri, mis näitab kumulatiivset ja omakulu, väljakutsete arvu, funktsioonide nime ja asukohta. </para
></listitem>
<listitem
><para
>Osade ülevaade </para
></listitem>
<listitem
><para
>Väljakutsete pinu </para
></listitem>
</itemizedlist>
</para>
</sect2>

<sect2>
<title
>Kuvamisala</title>
<para
>Kuvamisala, mis tavaliselt moodustab KCachegrindi peaakna parempoolse osa, koosneb ühest (vaikimisi) või enamast kaardivaatest, mis on korraldatud kas rõhtsalt või püstiselt. Iga kaardivaade näitab korraga ainult ühe kuluolemi kuva. Olemi nime näeb kaardivaate ülaosas. Kui kaardivaateid on palju, saab korraga ometi olla neist aktiivne ainult üks. Aktiivse kaardi olemi nime näidatakse rasvases kirjas ning see määrab ka KCachegrindi akna aktiivse kuluolemi. </para>
</sect2>

<sect2>
<title
>Kaardivaate alad</title>
<para
>Igas kaardivaates võib olla kuni neli vaateala, nimelt üleval, paremal, vasakul ja all. Igas alas võib olla mitu kaartidele eraldatud kuva. Nähtava kuva saab valida kaardirealt. Ülemise ja parempoolse ala kaardiribad asuvad üleval, vasakpoolse ja alumise ala omad all. Selle, milline kuva on millisel alal, saab määrata kaartide kontekstimenüüst. </para>
</sect2>

<sect2>
<title
>Kuva sünkroniseerimine kaardivaates olemi valimisega</title>
<para
>Aktiivse olemi kõrval on igas kaardivaates ka valitud olem. Kuna enamik kuvatüüpe näitab mitmeid olemeid, seades mingil moel keskmesse aktiivse, saab valitud elementi vahetada kuva sees liikudes (hiireklõpsuga või klaviatuuri abil). Tavaliselt on valitud elemendid ka esile tõstetud. Valitud olemi muutmisel mõnes kaardivaate kuvas tõstavad sama kaardivaate muud kuvad samuti automaatselt esile uue valitud olemi. </para>
</sect2>

<sect2>
<title
>Kaardivaadete sünkroniseerimine</title>
<para
>Mitme kaardivaate korral toob valiku muutmine ühel kaardil kaasa muutuse järgmises kaardivaates (suunaga paremale/alla). Selline sidumine võimaldab näiteks kiiresti lehitseda väljakutsete graafikuid. </para>
</sect2>

<sect2>
<title
>Paigutused</title>
<para
>Akna kõigi kaardivaadete paigutuse saab salvestada (vaata menüükäsku Vaade->Paigutus). Aktiivse paigutuse dubleerimisel (Ctrl+pluss või menüüst) ming mõne suuruse muutmiseks või kuva liigutamisel kaardivaates kuhugi mujale saab endise ja uue paigutuse vahel lülituda kiirklahviga Ctrl+nool vasakule/paremale. Paigutusi saab KCachegrindis salvestada ning need jäetakse seansside vahel meelde. Parajasti aktiivse paigutuse saab muuta kõigile uutele KCachegrindi seanssidele vaikepaigutuseks või siis taastada vaikepaigutuse. </para>
</sect2>
</sect1>

<sect1 id="concepts-sidedocks">
<title
>Külgdokid</title>

<sect2>
<title
>Lameprofiil</title>
<para
>Lameprofiil koosneb gruppide ja funktsioonide nimekirjast. Gruppide nimekirja kuuluvad sõltuvalt valitud grupi tüübist kõik grupid, kus esineb kulu. Gruppide nimekiri on peidetud, kui rühmitamine ei ole sisse lülitatud. </para>
<para
>Funktsioonide nimekiri sisaldab valitud grupi funktsioone või kõiki funktsioone, kui rühmitamine ei ole sisse lülitatud. Funktsioonid on seatud järjekorda mõne veeru, näiteks kumulatiivse või omakulu järgi. Seda, kui palju funktsioone nimekirjas maksimaalselt näidatakse, saab määrata seadistustedialoogis (Seadistused->KCachegrindi seadistamine). </para>
</sect2>

<sect2>
<title
>Osade ülevaade</title>
<para
>Profileerimisel on võimalik luua mitu profiiliandmete faili, mida saab üheskoos KCachegrindis laadida. Osade ülevaate dokis näidatakse neid vastavalt loomisajale rõhtsalt korraldatuna, kusjuures ristkülikute suurus vastab antud osa kulule. Üht või mõnda osa valides saab piirata kulu, mida KCahcegrind näitab muudes vaadetes, ainult valitud osaga või osadega. </para>
<para
>Osad on aga täiendavalt jagatud kas partitsioneerimis- või kumulatiivses režiimis: <itemizedlist>
<listitem
><para
>Partitsioneerimine: profiiliandmete osa on jagatud rühmadeks vastavalt valitud rühma tüübile. Kui näiteks valida ELF-objektide rühmad, näed iga kasutatud ELF-objekti (jagatud teegi või käivitatava faili) kohta värvilist ristkülikut, mille suurus sõltub objekti kulust. </para
></listitem>
<listitem
><para
>Kumulatiivne jaotus: selle puhul näidatakse parajasti aktiivse funktsiooni kumulatiivset kulu osas ristkülikuna. See omakorda on täiendavalt jagatud, näidades oma väljakutsutute kumulatiivset kulu. </para
></listitem>
</itemizedlist>
</para>
</sect2>

<sect2>
<title
>Väljakutsete pinu</title>
<para
>See on täiesti fiktiivne "kõige tõenäolisem" väljakutsete pinu. Seda alustatakse parajasti aktiivsest funktsioonist ning üles ja alla lisatake suurima kuluga väljakutsujad/väljakutsutud. </para>
<para
>Veerud "Kulu" ja "Väljakutsed" näitavad kõigi mainitud rea väljakutsete kulu. </para>
</sect2>
</sect1>

<sect1 id="concepts-visualizations">
<title
>Kuvamine</title>

<sect2>
<title
>Sündmuste tüübid</title>
<para
>See nimekiri näitab kõiki antud sündmuse tüübi parajasti aktiivse funktsiooni saadaolevaid kulutüüpe ning vastavat kumulatiivset ja omakulu. </para>
<para
>Nimekirjast sündmuse tüüpi valides saab muuta kõikjal KCachegrindis näidatavat kulutüüpi. </para>
</sect2>

<sect2>
<title
>Väljakutsete nimekirjad</title>
<para
>Need nimekirjad näitavaid väljakutseid nii parajasti aktiivsest funktsioonist kui parajasti aktiivsele funktsioonile. "Kõik" väljakutsujad/väljakutsutud tähendavad siin funktsioone, mida on võimalik väljakutsuja/väljakutsutu puhul jälgida isegi juhul, kui vahele jääb muid funktsioone. </para>
<para
>Väljakutsete nimekirjad on järgmised: <itemizedlist>
<listitem
><para
>Otseväljakutsujad </para
></listitem>
<listitem
><para
>Otseväljakutsutud </para
></listitem>
<listitem
><para
>Kõik väljakutsujad </para
></listitem>
<listitem
><para
>Kõik väljakutsutud </para
></listitem>
</itemizedlist>
</para>
</sect2>

<sect2>
<title
>Kaardid</title>
<para
>Esmase sündmuse tüübi puukujuline kuva piki väljakutsete hierarhiat üles või alla. Iga värviline ristkülik tähistab funktsiooni, selle suurus on aga võimalikult hästi proportsioonis kuluga ajal, mis aktiivne funktsioon töötab (proportsionaalsuse täpsusel on küll omad piirangud). </para>
<para
>Väljakutsujate kaardi korral näitab graafik kõigi parajasti aktiivse funktsiooni väljakutsujate pesastatud hierarhiat. Väljakutsutute kaardi korral näitab graafik kõigi parajasti aktiivse funktsiooni väljakutsutute pesastatud hierarhiat. </para>
<para
>Välimuse valikud leiab kontekstimenüüst. Täpse suuruse näitamiseks vali "Jäta vigased piirded vahele". Kuna see võib võtta tublisti aega, tasuks eelnevalt piirata kuvamise maksimaalne sügavus. "Parim" määratleb järglaste poolitamise suuna eellaste proportsiooni põhjal. "Alati parim " määratleb järelejäänud ruumi igale järglasele. "Ignoreeri proportsioone" jätab enne järglaste joonistamist ruumi funktsiooni nimele. Arvesta, et suuruse proportsioonid võivad eriti halval juhul olla tugevasti moonutatud. </para>
<para
>Järglaste vahel saab liikuda ka klaviatuuri abil, kasutades vasak- ja paremnoole klahve. Üles- ja allanoole klahvidega saab hierarhias taseme võrra üles- või allapoole liikuda. Klahv Enter aktiveerib elemendi, millel parajasti viibid. </para>
</sect2>

<sect2>
<title
>Väljakutsegraafik</title>
<para
>See näitab väljakutsete graafikut aktiivse funktsiooni piirkonnas. Näidatakse ainult kulu aktiivse funktsiooni tegeliku töötamise ajal, s.t. main() puhul, kui see muidugi on näha, näidatakse kulu, mis on tegelikult sama aktiivse funktsiooni kuluga, kuna see on osa main() kumulatiivsest kulust aktiivse funktsiooni töötamise ajal. </para>
<para
>Tsüklite puhul näitavad sinised väljakutsenooled, et tegemist on kunstliku väljakutsega, mis on lisatud ainult graafiku korrigeerimiseks ja mida tegelikult pole kunagi esinenud. </para>
<para
>Kui graafik ei mahu vidinasse ära, näidatakse ühel serval tillukest eelvaatlust. See sarnaneb kõiges täpselt väljakutsepuule ning valitud funktsioon on seal esile tõstetud. </para>
</sect2>

<sect2>
<title
>Annotatsioonid</title>
<para
>Annoteeritud lähtekoodi ja assembleri nimekiri näitab parajasti aktiivse funktsiooni lähtekoodiridu/disassembleeritud instruktsioone koos (oma)kuluga lähterea/instruktsiooni koodi täitmisel. Väljakutse korral lisatakse lähtekoodi read väljakutse detailidega: väljakutse (kumulatiivne) kulu, väljakutsete arv ja väljakutse sihtmärk. </para>
<para
>Väljakutse sihtmärgi aktiveerimiseks vali selline väljakutse infoga rida. </para>
</sect2>
</sect1>

</chapter>


<chapter id="commands">
<title
>Käskude seletused</title>

<sect1 id="kcachegrind-mainwindow">
<title
>&kcachegrind;i peaaken</title>
<para
></para>

<sect2>
<title
>Menüü <guimenu
>Fail</guimenu
></title>
<para>
<variablelist>

<varlistentry>
<term
><menuchoice
><shortcut
> <keycombo
>&Ctrl;<keycap
>N</keycap
></keycombo
> </shortcut
> <guimenu
>Fail</guimenu
> <guimenuitem
>Uus</guimenuitem
> </menuchoice
></term>
<listitem
><para
><action
> Avab tühja tipptaseme akna, millesse saab laadida profileerimisandmed. </action
> Õigupoolest pole seda väga vajagi, sest Fail->Ava tekitab samuti uue tipptaseme akna, mis näitab juba valitud andmeid. </para
></listitem>
</varlistentry>

<varlistentry>
<term
><menuchoice
><shortcut
> <keycombo
>&Ctrl;<keycap
>O</keycap
></keycombo
> </shortcut
> <guimenu
>Fail</guimenu
> <guimenuitem
>Ava</guimenuitem
> </menuchoice
></term>
<listitem
><para
><action
> Avab failidialoogi, milles saab valida laaditava profileerimisandmete faili. </action
> Kui parajasti avatud tipptaseme aknas on juba mingid andmed, avatakse uus aken. Kui soovid avada täiendavad profileerimisandmed aktiivses aknas, kasuta käsku Fail->Lisa. </para>
<para
>Profileerimisandmete failinime lõpus seisab tavaliselt ..-, kus viimane punkt ja kriips tähistavad ühe ja sama rakenduse profileerimise erinevaid andmefaile. Kui laadida fail, mille lõpus seisab ainult . (üks punkt), laetakse ühtlasi ka kõik antud profileerimise muude failinime lõppudega andmefailid. </para>
<para
>Näide: kui sul on profileerimisandmete failid cachegrind.out.123 ja cachegrind.out.123.1, siis esimest laadides laetakse automaatselt ka teine. </para
></listitem>
</varlistentry>

<varlistentry>
<term
><menuchoice
><guimenu
>Fail</guimenu
> <guimenuitem
>Lisa</guimenuitem
> </menuchoice
></term>
<listitem
><para
><action
> Lisab profileerimisandmete faili aktiivsesse aknasse. </action
> Selle käsuga saab mitu andmefaili laadida ühes ja samas tipptaseme aknas isegi siis, kui need ei ole pärit ühest ja samast profileerimisest (sellele osutavad failinimed). Seda saab kasutada näiteks võrdlemiseks. </para
></listitem>
</varlistentry>

<varlistentry>
<term
><menuchoice
><guimenu
>Fail</guimenu
>  <guimenuitem
>Lae uuesti</guimenuitem
> </menuchoice
></term>
<listitem
><para
><action
> Laeb profileerimisandmed uuesti. </action
> See on tõenäoliselt kõige huvipakkuvam siis, kui juba avatud rakenduse profiili jaoks on loodud uus profileerimisandmete fail. </para
></listitem>
</varlistentry>

<varlistentry>
<term
><menuchoice
><shortcut
> <keycombo
>&Ctrl;<keycap
>Q</keycap
></keycombo
> </shortcut
> <guimenu
>Fail</guimenu
> <guimenuitem
>Välju</guimenuitem
> </menuchoice
></term>
<listitem
><para
><action
>Lõpetab</action
> &kappname;i töö</para
></listitem>
</varlistentry>
</variablelist>
</para>

</sect2>

<sect2>
<title
>Menüü <guimenu
>Vaade</guimenu
></title>
<para>
<variablelist>

<varlistentry>
<term
><menuchoice
><guimenu
>Vaade</guimenu
> <guimenuitem
>Esmane sündmuse tüüp</guimenuitem
> </menuchoice
></term>
<listitem
><para
><action
>(Kirjutamata)</action
></para
></listitem>
</varlistentry>

<varlistentry>
<term
><menuchoice
><guimenu
>Vaade</guimenu
> <guimenuitem
>Sekundaarne sündmuse tüüp</guimenuitem
> </menuchoice
></term>
<listitem
><para
><action
>(Kirjutamata)</action
></para
></listitem>
</varlistentry>

<varlistentry>
<term
><menuchoice
><guimenu
>Vaade</guimenu
> <guimenuitem
>Rühmitamine</guimenuitem
> </menuchoice
></term>
<listitem
><para
><action
>(Kirjutamata)</action
></para
></listitem>
</varlistentry>

<varlistentry>
<term
><menuchoice
><guimenu
>Vaade</guimenu
> <guimenuitem
>Paigutus</guimenuitem
> </menuchoice
></term>
<listitem
><para
><action
>(Kirjutamata)</action
></para
></listitem>
</varlistentry>

<varlistentry>
<term
><menuchoice
><guimenu
>Vaade</guimenu
> <guimenuitem
>Poolita</guimenuitem
> </menuchoice
></term>
<listitem
><para
><action
>(Kirjutamata)</action
></para
></listitem>
</varlistentry>

</variablelist>
</para>

</sect2>


</sect1>
</chapter>

<chapter id="faq">
<title
>Küsimused ja vastused</title>
&reporting.bugs; &updating.documentation; <qandaset id="faqlist">


<qandaentry>
<question>
<para
>Milleks see &kcachegrind; üldse hea on? </para>
</question>
<answer>
<para
>&kcachegrind;ist on kasu tarkvara arendamise viimases järgus, mida nimetatakse profileerimiseks. Kui sa ei tööta ise tarkvara välja, ei ole sul ka &kcachegrind;iga midagi tarka peale hakata. </para>
</answer>
</qandaentry>

<qandaentry>
<question>
<para
>Mis on "Kumul." ja "Oma" erinevus? </para>
</question>
<answer>
<para
>Need on teatud sündmuse tüübi korral funktsiooni kulu atribuudid. Kui üks funktsioon teist välja kutsub, on mõttekas eristada funktsiooni enda kulu ("omakulu") ja nii selle kui kõigi väljakutsutud funktsioonide kulu ("kumulatiivne kulu"). </para>
<para
>Nii näiteks on main() korral kumulatiivne kulu alati peaaegu 100%, samas kui selle omakulu on tavaliselt tühine, sest tegeliku töö teevad ära muud funktsioonid. </para>
</answer>
</qandaentry>

<qandaentry>
<question>
<para
>Minu KChachegrindi tööriistariba/menüüriba näeb välja üsna tühi. Kas see peabki nii olema?</para>
</question>
<answer>
<para
>Ilmselt on KCachegrindi paigaldamisel midagi valesti läinud. Soovitatav on rakendus kompileerida paigaldusprefiksiga, milleks on sinu süsteemi KDE baaskataloog, näiteks <command
>configure --prefix=/opt/kde3; make install</command
>. Kui valid mõne muu kataloogi, näiteks $HOME/kde, peab panema keskkonnamuutuja KDEDIR enne KCachegrindi käivitamist sellele kataloogile osutama. </para>
</answer>
</qandaentry>

<qandaentry>
<question>
<para
>Kui ma teen väljakutsegraafikus funktsioonil topeltklõpsu, näitab see funktsiooni main jaoks valitud funktsiooniga sama kulu. Kas see ei peaks olema üsna konstantselt 100%? </para>
</question>
<answer>
<para
>Sa aktiveerisid funktsiooni main() all, mille kulu on väiksem kui funktsioonil main(). Iga funktsiooni korral näidatakse ainult funktsiooni täieliku kulu osa ehk siis kulu aktiveeritud funktsiooni töötamise ajal, mis tähendab, et suvalise funktsiooni näidatav kulu ei saa kunagi olla suurem kui aktiveeritud funktsiooni kulu. </para>
</answer>
</qandaentry>


</qandaset>
</chapter>

<chapter id="glossary">
<title
>Sõnastik</title>

<para
>Alljärgnev on üsna suvalises järjekorras mõistete lühisõnastik. <itemizedlist>
<listitem
><para
>Profileerimine: statistilise info kogumine töötava programmi käituskarakteristika kohta. </para
></listitem>
<listitem
><para
>Jälitamine: programmi töötamise jälgimine ja toimuvate sündmuste salvestamine koos ajatemplitega väljundfaili ehk jälitusfaili. </para
></listitem>
<listitem
><para
>Jälitus: ajatempliga sündmuste jada, mis toimusid jälitatava programmi töötamise ajal. Selle suurus on tavaliselt lineaarses sõltuvuses programmi töötamise ajast. </para
></listitem>
<listitem
><para
>Profileerimisandmete fail: fail, mis sisaldab profileerimiseksperimendi või selle osa käigus mõõdetud või jälituse järeltöötlusel loodud andmeid. Selle suurus on tavaliselt lineaarses sõltuvuses programmi koodi suurusest. </para
></listitem>
<listitem
><para
>Profileerimisandmete osa (sageli vääralt jälitusosa): profileerimisandmete faili andmed. </para
></listitem>
<listitem
><para
>Profileerimiseksperiment: programmi töö profileerimistööriista kontrolli all, mis võib anda tulemuseks palju programmi osade ja/või lõimede põhjal loodud profileerimisandmete faile. </para
></listitem>
<listitem
><para
>Profileerimisprojekt: profileerimiseksperimentide seadistus mingi programmi jaoks, mida soovitakse profileerida (võib-olla ka selle mitmeid versioone). Profileerimisandmete võrdlemisel on tavaliselt mõtet ainult ühe profileerimisprojekti eksperimentidega loodud erinevate profileerimisandmete korral. </para
></listitem>
<listitem
><para
>Kuluolem: abstraktne element, mis on seotud lähtekoodiga, mille sündmusi arvestatakse. Kuluolemi mõõtmeteks on koodi asukoht (nt. lähterida, funktsioon), andmete asukoht (nt. kasutatud andmete tüüp, andmeobjekt), täitmise asukoht (nt. lõim, protsess) ning eelmainitud asukohtade järjendid (nt. väljakutsed, objekti kasutamine lausest, vahemälust väljatõrjutud andmed). </para
></listitem>
<listitem
><para
>Sündmuse tüüp: sündmuse liik, mille kulu saab sisuda kuluolemiga. Need jagunevad reaalseteks sündmuse tüüpideks ja päritud sündmuse tüüpideks. </para
></listitem>
<listitem
><para
>Reaalne sündmuse tüüp: sündmuse tüüp, mida saab mõõta tööriistaga. See vajab sensori olemasolu antud sündmuse tüübile. </para
></listitem>
<listitem
><para
>Päritud sündmuse tüüp: virtuaalne sündmuse tüüp, mida näitab ainult kuva ja mis on määratud reaalsete sündmuste tüüpide põhjal teatud valemiga. </para
></listitem>
<listitem
><para
>Sündmuste kulu: teatud sündmuse tüübi sündmuste summa täitmise ajal, mis on seotud teatud kuluolemiga. Kulu on seotud olemiga. </para
></listitem>
</itemizedlist>
</para>
</chapter>

<chapter id="credits">


<title
>Autorid ja litsents</title>

<para
>&kappname; </para>
<para
>Tänud Julian Sewardile suurepärase &valgrind;i ja Nicholas Nethercote'ile &cachegrind;i lisanduse eest. Ilma nende programmideta ei oleks <application
>KCachegrind</application
>i lihtsalt olemas. Neilt on pärit ka hulk häid mõtteid &GUI; kohta. </para>
<para
>Tänud ka kõigile kasutajatele, kes saatsid vearaporteid ja ettepanekuid. </para>
&underFDL; </chapter>

<appendix id="installation">
<title
>Paigaldamine</title>

<sect1 id="getting-kcachegrind">
<title
>&kcachegrind;i hankimine</title>

<para
>&kcachegrind; kuulub &kde; paketi &package; koosseisu. Vahepealseid vähemtoetatud väljalaskeid, &callgrind;i ja täiendava dokumentatsiooni leiab veebileheküljelt <ulink url="http://kcachegrind.sf.net"
> http://kcachegrind.sf.net</ulink
>. Sealt leiab täpsemaid juhiseid paigaldamise ja kompileerimise kohta. </para>
</sect1>

<sect1 id="requirements">
<title
>Nõuded</title>

<para
>&kcachegrind;i edukaks kasutamiseks on vajalik &kde; 3.x. Profileerimisandmete tekitamiseks on soovitatav kasutada &cachegrind;i või &calltree;/&callgrind;i. </para>
</sect1>

<sect1 id="compilation">
<title
>Kompileerimine ja paigaldamine</title>
&install.compile.documentation; </sect1>

<sect1 id="configuration">
<title
>Seadistamine</title>

<para
>Kõik seadistamisvõimalused leiab seadistustedialoogis või kuvade kontekstimenüüdes. </para>

</sect1>

</appendix>

&documentation.index;
</book>
<!--
Local Variables:
mode: sgml
sgml-minimize-attributes:nil
sgml-general-insert-case:lower
sgml-indent-step:0
sgml-indent-data:nil
End:
-->













