1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
|
<?xml version="1.0" ?>
<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
<!ENTITY tdecachegrind '<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 "&tdecachegrind;">
<!ENTITY package "tdesdk">
<!ENTITY % addindex "IGNORE">
<!ENTITY % Swedish "INCLUDE">
]>
<!-- ................................................................ -->
<book lang="&language;">
<bookinfo>
<title
>Handbok &tdecachegrind;</title>
<authorgroup>
<author
><firstname
>Josef</firstname
> <surname
>Weidendorfer</surname
> <affiliation
> <address
><email
>Josef.Weidendorfer@gmx.de</email
></address>
</affiliation>
</author>
<othercredit role="translator"
><firstname
>Stefan</firstname
> <surname
>Asserhäll</surname
><affiliation
><address
><email
>stefan.asserhall@comhem.se</email
></address
></affiliation
><contrib
>Översättare</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
>&tdecachegrind; är ett visualiseringsverktyg för profileringsdata, som är skrivet för &kde;-miljön. </para>
</abstract>
<keywordset>
<keyword
>KDE</keyword>
<keyword
>tdesdk</keyword>
<keyword
>Cachegrind</keyword>
<keyword
>Callgrind</keyword>
<keyword
>Valgrind</keyword>
<keyword
>Profilering</keyword>
</keywordset>
</bookinfo>
<chapter id="introduction">
<title
>Inledning</title>
<para
>&kappname; är en bläddrare för data som producerats av profileringsverktyg. Det här kapitlet förklarar vad profilering är till för, hur den görs, och ger några exempel på tillgängliga profileringsverktyg. </para>
<sect1 id="introduction-profiling">
<title
>Profilering</title>
<para
>När ett program utvecklas, omfattar ett av de sista stegen ofta prestandaoptimering. Eftersom det inte är vettigt att optimera funktioner som sällan används, vilket skulle vara bortkastad tid, måste man veta i vilka delar av programmet som den största delen av tiden går åt. </para>
<para
>För sekvensiell kod är det ofta tillräckligt att samla statistisk data om programmets beteende under körning, som åtgången tid i funktioner och per kodrad. Det kallas profilering. Programmet körs under övervakning av ett profileringsverktyg, som ger summeringen av en programkörning vid slutet. Däremot, för parallell kod, uppstår prestandaproblem oftast när en processor väntar på data från en annan. Eftersom väntetiden oftast inte enkelt kan fördelas, är det bättre att skapa tidsstämplade händelseföljder. Kcachegrind kan inte visualisera den sortens data. </para>
<para
>Efter att ha analyserat skapad profileringsdata ska det vara enkelt att se utsatta ställen och flaskhalsar i koden. Till exempel kan antaganden om antal anrop kontrolleras, och identifierade kodavsnitt kan optimeras. Efteråt bör optimeringens resultat verifieras med ytterligare en profileringskörning. </para>
</sect1>
<sect1 id="introduction-methods">
<title
>Profileringsmetoder</title>
<para
>För att exakt mäta tiden som går eller spela in händelser som inträffar under körning av ett kodavsnitt (t.ex. en funktion) krävs att ytterligare uppmätningskod infogas innan och efter det givna området. Den här koden läser tiden eller en global händelseräknare, och beräknar skillnader. Alltså måste originalkoden ändras innan körning. Det kallas instrumentering. Instrumentering kan göras av programmeraren själv, av kompilatorn eller av körningssystemet. Eftersom intressanta områden ofta är i flera nivåer, påverkar tiden som går åt för mätningen alltid mätresultatet. Därför måste instrumentering göras selektivt och resultaten måste tolkas noggrant. Detta gör förstås prestandaanalys med exakta mätningar till en mycket komplex process.</para>
<para
>Exakta mätningar är möjliga på grund av räknare i hårdvara (inklusive räknare som ökas när tiden tickar), som tillhandahålls i moderna processorer, och som ökas så fort en händelse inträffar. Eftersom vi vill tilldela händelser till kodavsnitt, skulle vi behöva hantera varje händelse genom att öka en räknare för det aktuella kodavsnittet själva, om inte räknarna fanns. Att göra detta i programvara är förstås inte möjligt. Men med antagandet att distributionen av händelser i källkoden är liknande om man bara tittar på var n:e händelse istället för varje, har en mätmetod som är justerbar med avseende på tiden som går åt för mätningen skapats. Den kallas sampling. Tidsbaserad sampling (TBS) använder tidmätning för att regelbundet titta på programräknaren för att skapa ett histogram av programmets kod. Händelsebaserad sampling (EBS) utnyttjar hårdvaruräknarna i moderna processorer, och använder ett läge där en avbrottshanterare anropas när en räknare går förbi nollvärdet, och skapar ett histogram av motsvarande händelsefördelning. I avbrottshanteraren initieras räknaren alltid om till n i samplingsmetoden. Fördelen med sampling är att koden inte behöver ändras, men det är fortfarande en kompromiss: antagandet ovan är riktigare om n är litet, men ju mindre n är, desto större är tiden som går åt i avbrottshanteraren.</para>
<para
>En annan mätmetod är att simulera det som händer i ett datorsystem när en given kod körs, dvs. körningsstyrd simulering. Simuleringen härleds alltid från en mer eller mindre noggrann modell av datorn. För mycket detaljerade modeller som är nära verkligheten, kan simuleringstiden dock vara oacceptabelt hög i praktiken. Fördelen med simulering är att godtyckligt komplex mätnings- och simuleringskod kan infogas i en given kod utan att störa resultaten. Att göra detta direkt innan körningen (vilket kallas instrumentering vid körning) med det ursprungliga binärprogrammet, är mycket bekvämt för användaren: Ingen omkompilering behövs. Simulering blir användbar om bara delar av en dator simulerats med en enkel modell. En annan fördel är att resultat som skapas av enkla modeller ofta är mycket enklare att förstå: problemet med riktig hårdvara är ofta att resultaten innehåller överlappande effekter från olika delar av datorn.</para>
</sect1>
<sect1 id="introduction-tools">
<title
>Profileringsverktyg</title>
<para
>Mest känt är GCC:s profileringsverktyg <application
>gprof</application
>. Man måste kompilera programmet med väljaren <option
>-pg</option
>, köra programmet för att skapa filen <filename
>gmon.out</filename
>, som kan översättas till läsbar form med <command
>gprof</command
>. En nackdel är omkompileringssteget för att förbereda det körbara programmet, som också måste länkas statiskt. Metoden som används här är instrumentering skapad av kompilatorn, som mäter anrop som sker mellan funktioner och motsvarande antal anrop, tillsammans med tidsbaserad sampling, vilket ger ett histogram av tidsdistributionen i koden. Med båda typerna av information är det möjligt att heuristiskt beräkna samlad tid i funktioner, dvs. tiden som går åt i en funktion tillsammans med alla funktioner som anropas från den. </para>
<para
>För exakt mätning av händelser som inträffar, finns det bibliotek med funktioner som kan läsa ut hårdvaruprestandaräknare. Mest välkänd är programfixen PerfCtr för Linux, och de arkitekturoberoende biblioteken PAPI och PCL. Exakta mätningar behöver ändå instrumentering av koden, som tidigare beskrivits. Antingen använder man biblioteken själv, eller automatiska instrumenteringssystem som ADAPTOR (för instrumentering av FORTRAN källkod) eller DynaProf (kodinjicering via DynInst).</para>
<para
>&oprofile; är ett systemprofileringsverktyg för Linux som använder sampling.</para>
<para
>I många avseenden är ett bekvämt sätt att utföra profilering att använda Cachegrind eller Callgrind, vilka är simulatorer som använder ramverket &valgrind; för instrumentering vid körning. Eftersom det inte finns något behov av att komma åt hårdvaruräknare (ofta svårt med dagens Linux-installationer), och binärprogram som ska profileras kan lämnas oförändrade, är det ett bra alternativt sätt jämfört med andra profileringsverktyg. Nackdelen med långsammare körning på grund av simuleringen kan reduceras genom att bara utföra simuleringen för intressanta programavsnitt, och kanske bara under några få iterationer av en snurra. Utan instrumentering för mätning och simulering leder Valgrinds användning bara till en körning som är 3 till 5 gånger långsammare. Dessutom, om bara anropsdiagrammet och anropsantalen är intressanta, kan cachesimuleringen stängas av. </para>
<para
>Cachesimulering är det första steget i att approximera realtid, eftersom i moderna system är körtiden mycket känslig för hur så kallade cacher utnyttjas (små och snabba buffrar som snabbar upp upprepade åtkomster till samma celler i huvudminnet). &cachegrind; simulerar cacher genom att lagra minnesaccesser i cacherna. Den data som skapas omfattar antal åtkomster till instruktions- och dataminnet och cachemissar i första och andra nivåns cacher, och den relateras till källkodsrader och funktioner i programmet som kör. Genom att kombinera antal missar, och använda latenstider för missar för typiska processorer, kan en uppskattning av åtgången tid ges. </para>
<para
>Callgrind är en utökning av &cachegrind; som bygger upp anropsträdet för ett program i farten, dvs. hur funktionerna anropar varandra och hur många händelser som inträffar när en funktion körs. Dessutom kan den profileringsdata som måste samlas in delas upp enligt trådar och anropskedjans sammanhang. Det kan tillhandahålla profileringsdata på instruktionsnivå för att göra det möjligt att kommentera disassemblerad kod. </para>
</sect1>
<sect1 id="introduction-visualization">
<title
>Visualisering</title>
<para
>Profileringsverktyg skapar typiskt en stor mängd data. Önskan att snabbt bläddra uppåt och neråt i anropsdiagrammet, tillsammans med snabbt byte av sorteringsläge för funktioner och visning av olika händelsetyper, motiverar ett grafiskt gränssnitt för att utföra uppgiften. </para>
<para
>&kappname; är ett visualiseringsverktyg för profileringsdata som uppfyller dessa önskemål. Även om det först programmerats med bläddring i data från &cachegrind; och &calltree; i åtanke, finns det konverteringsprogram tillgängliga för att kunna visa profileringsdata som skapats av andra verktyg. I appendix ges en beskrivning av filformatet som används av Cachegrind/Callgrind. </para>
<para
>Förutom en lista med funktioner sorterade enligt mätetal över enskild eller samlad kostnad, eventuellt grupperade enligt källkodsfil, delat bibliotek eller C++ klass, erbjuder &kappname; diverse visualiseringsvyer för en vald funktion, närmare bestämt: <itemizedlist>
<listitem
><para
>en anropsdiagramvy, som visar en del av anropsdiagrammet omkring den valda funktionen,</para>
</listitem>
<listitem
><para
>en trädkarta, som gör det möjligt att visualisera anropsförhållanden i flera nivåer tillsammans med mätetal över samlad kostnad för snabb visuell detektering av problematiska funktioner,</para>
</listitem>
<listitem
><para
>vyer för källkods- och assemblerkommentarer, som gör det möjligt att se kostnadsinformation relaterat till källkodsrader och assemblerinstruktioner.</para>
</listitem>
</itemizedlist>
</para>
</sect1>
</chapter>
<chapter id="using-tdecachegrind">
<title
>Att använda &tdecachegrind;</title>
<sect1 id="using-profile">
<title
>Skapa data att visualisera</title>
<para
>Först vill man skapa prestandainformation genom att mäta olika aspekter av ett programs beteende under körning, genom att använda ett profileringsverktyg. &tdecachegrind; själv inkluderar inte något profileringsverktyg, men är bra på att användas tillsammans med &callgrind;, och kan också användas för att visualisera data som skapats av &oprofile;, genom att använda ett konverteringsprogram. Även om syftet med den här handboken inte är att dokumentera profilering med dessa verktyg, ger nästa avsnitt korta handledningar för att du snabbt ska komma igång. </para>
<sect2>
<title
>&callgrind;</title>
<para
>&callgrind; är tillgänglig från <ulink url="http://tdecachegrind.sf.net"
>http://tdecachegrind.sf.net</ulink
>. Observera att det tidigare kallades &calltree; men det namnet var missvisande. </para>
<para
>Den vanligaste användningen är att inleda kommandot för att starta ditt program med <application
>callgrind</application
>, som i <blockquote
><para
><command
>callgrind mitt_program mina_argument</command
></para
></blockquote
> När programmet avslutas skapas filen <filename
>callgrind.out.pid</filename
>, som kan laddas i &tdecachegrind;. </para>
<para
>Mer avancerad användning är att lagra profileringsdata så fort en given funktion i programmet anropas. För att t.ex. bara se profileringsdata när en webbsida ritas upp i <command
>konqueror</command
>, skulle du kunna bestämma att lagra data så fort du väljer menyalternativet Visa/Uppdatera. Det motsvarar ett anrop till <symbol
>KonqMainWindow::slotReload</symbol
>. Använd <blockquote
><para
><command
>callgrind --dump-before=KonqMainWindow::slotReload konqueror </command
></para
></blockquote
> Det skapar flera profileringsdatafiler med ett ytterligare sekvensnummer i slutet på filnamnet. En fil utan ett sådant nummer (som bara slutar med process-id) skapas också. Genom att ladda den filen i &tdecachegrind;, så laddas alla övriga också, och kan ses i översikten över delar och i listan med delar. </para>
</sect2>
<sect2>
<title
>&oprofile;</title>
<para
>&oprofile; är tillgänglig från <ulink url="http://oprofile.sf.net"
>http://oprofile.sf.net</ulink
>. Följ installeringsinstruktionerna på webbplatsen, men innan du gör det kontrollera om din distribution inte redan tillhandahåller det som ett paket (som SuSE). </para>
<para
>Systemprofilering är bara tillåtet för systemadministratören, eftersom alla åtgärder i systemet kan observeras. Därför måste följande göras som systemadministratör. Anpassa först profileringsprocessen med det grafiska gränssnittet <command
>oprof_start</command
>, eller kommandoradverktyget opcontrol. Standardinställningen ska vara tidsläge (TBS, se inledningen). För att starta mätningen, kör <command
>opcontrol -s</command
>. Kör därefter programmet du är intresserad av, och skriv efteråt <command
>opcontrol -d</command
>. Det skriver ut mätresultaten i filer under katalogen <filename
>/var/lib/oprofile/samples/</filename
>. För att kunna visualisera data i &tdecachegrind; gör följande i en tom katalog: <blockquote
><para
><command
>opreport -gdf | op2callgrind</command
></para
></blockquote
> Det skapar många filer, en för varje program som kördes på systemet. Var och en kan laddas i &tdecachegrind; för sig. </para>
</sect2>
</sect1>
<sect1 id="using-basics">
<title
>Grunderna i användargränssnittet</title>
<para
>När &tdecachegrind; startas med en profileringsdatafil som argument, eller efter en fil har laddats med Arkiv/Öppna, ser du en sidopanel som innehåller funktionslistan till vänster, och till höger huvudområdet för visualiseringar av den valda funktionen. Visualiseringsområdet kan ställas in godtyckligt för att visa flera visualiseringar samtidigt. </para>
<para
>Efter första starten, är området uppdelat i en övre och en undre del, var och en med olika visualiseringar som kan väljas med flikar. För att flytta visualiseringsvyer, använd flikarnas sammanhangsberoende meny, och justera avdelaren mellan visualiseringarna. För att snabbt byta mellan olika visualiseringslayouter, använd Visa/Layout/Duplicera, ändra layouten och byt mellan layouter med Visa/Layout/Gå till nästa (eller ännu bättre, använd motsvarande snabbtangenter). </para>
<para
>Den aktiva händelsetypen är viktig för visualiseringarna: för &callgrind; är det till exempel cachemissar eller cykeluppskattningar, för &oprofile; är det "timer" i det enklaste fallet. Du kan ändra händelsetyp via en kombinationsruta i verktygsraden eller i händelsetypvyn. En första översikt över beteendet under körning bör ges när du väljer funktionen <symbol
>main</symbol
> i listan till vänster, och tittar på visualiseringen anropsdiagram. Där ser du anropen som sker i programmet. Observera att anropsdiagramvyn bara visar funktioner med ett högt händelseantal. Genom att dubbelklicka på en funktion i diagrammet ändras det så att anropade funktioner omkring den valda visas. </para>
<para
>För att utforska det grafiska gränssnittet ytterligare, förutom den här handboken, ta också en titt på dokumentationsavsnittet på webbsidan <ulink url="http://tdecachegrind.sf.net"
>http://tdecachegrind.sf.net</ulink
>. Förutom detta, har varje grafisk komponent i &tdecachegrind; hjälp via <quote
>Vad är det här?</quote
>. </para>
</sect1>
</chapter>
<chapter id="tdecachegrind-concepts">
<title
>Grundläggande begrepp</title>
<para
>Det här kapitlet förklarar några begrepp i &tdecachegrind; och introducerar termer som används i gränssnittet. </para>
<sect1 id="concepts-model">
<title
>Datamodellen för profileringsdata</title>
<sect2>
<title
>Kostnadsenheter</title>
<para
>Kostnadsantal för händelsetyper (som L2 missar) tilldelas till kostnadsenheter, som är objekt med förhållanden till källkod eller datastrukturer i ett givet program. Kostnadsenheter kan inte bara vara enkla kod- eller datapositioner, utan också sammansatta positioner. Ett anrop kan till exempel ha en källa och ett mål, eller en dataadress kan ha en datatyp och en kodposition där data har skapats. </para>
<para
>Kostnadsenheterna som Kcachegrind känner till anges här. Enkla positioner: <itemizedlist
><listitem
><para
>Instruktion. En assemblerinstruktion på en given adress.</para
></listitem
><listitem
><para
>Källkodsrad i en funktion. Alla instruktioner som kompilatorn (via avlusningsinformation) avbildar på en given källkodsrad angiven med källkodsfilnamn och radnummer, och som körs i ett visst funktionssammanhang. Instruktioner utan en avbildning till en verklig källkodsrad använder radnummer 0 i filen "???".</para
></listitem
><listitem
><para
>Funktion. En given funktion består av alla källkodsrader i själva funktionen. En funktion anges av sitt namn och sin plats i ett visst binärobjekt om tillgängligt. Det senare behövs eftersom binärobjekt i ett enda program vart och ett kan innehålla funktioner med samma namn (de kan t. ex. kommas åt med dlopen/dlsym. Länkaren löser upp funktioner i en given sökordning för binärobjekt som används vid körning). Om ett profileringsverktyg inte kan detektera en funktions symbolnamn, t.ex. på grund av att avlusningsinformation inte är tillgänglig, används typiskt antingen adressen för den första instruktionen som körs, eller "???".</para
></listitem
><listitem
><para
>Binärobjekt. Alla funktioner vars kod är inne i ett givet binärobjekts område, antingen i det körbara huvudprogrammet eller ett delat bibliotek.</para
></listitem
><listitem
><para
>Källkodsfil. Alla funktioner vars första instruktion avbildas till en rad i den givna källkodsfilen.</para
></listitem
><listitem
><para
>Klass. Symbolnamn i funktioner är ofta ordnade i hierarkiska namnrymder, t.ex. C++ namnrymder, eller klasser i objektorienterade språk. Därför kan en klass själv innehålla funktioner i klassen eller inbäddade klasser.</para
></listitem
><listitem
><para
>Profileringsdel. Ett visst tidsavsnitt av en profileringskörning, med ett givet tråd-id, process-id och kommandorad som kördes.</para
></listitem
></itemizedlist
> Som syns i listan, definierar en uppsättning kostnadsenheter ofta en annan kostnadsenhet. Därför finns det en hierarki med ingående kostnadsenheter, som bör vara uppenbar från beskrivningen ovan. </para>
<para
>Positionsinformation: <itemizedlist
><listitem
><para
>Anrop från instruktionsadress till målfunktion.</para
></listitem
><listitem
><para
>Anrop från källkodsrad till målfunktion.</para
></listitem
><listitem
><para
>Anrop från källfunktion till målfunktion.</para
></listitem
><listitem
><para
>(O)villkorligt hopp från käll- till målinstruktion.</para
></listitem
><listitem
><para
>(O)villkorligt hopp från käll- till målrad.</para
></listitem
></itemizedlist
> Hopp mellan funktioner tillåts inte, eftersom det inte är vettigt i ett anropsdiagram. Därför måste konstruktioner som undantagshantering och långa hopp i C översättas genom att gå bakåt i anropsstacken efter behov. </para>
</sect2>
<sect2>
<title
>Händelsetyper</title>
<para
>Godtyckliga händelsetyper kan anges i profileringsdata genom att ge dem ett namn. Deras kostnad i förhållande till en kostnadsenhet är ett 64-bitars heltal. </para>
<para
>Händelsetyper vars kostnad anges i en profileringsdatafil kallas verkliga händelser. Dessutom kan man ange formler för händelsetyper som beräknas från verkliga händelser, som kallas ärvda händelser. </para>
</sect2>
</sect1>
<sect1 id="concepts-state">
<title
>Visualiseringstillstånd</title>
<para
>Visualiseringstillståndet i ett fönster i Kcachegrind omfattar: <itemizedlist
> <listitem
><para
>den primära och sekundära händelsetypen som valts att visas, </para
></listitem
><listitem
><para
>funktionsgrupperingen (används i funktionsprofileringslistan och enhetsfärgningen),</para
></listitem
><listitem
><para
>profileringsdelarna vars kostnad ska ingå i visualiseringen,</para
></listitem
><listitem
><para
>en aktiv kostnadsenhet (t.ex. en funktion som valts i sidopanelen för funktionsprofilering), </para
></listitem
><listitem
><para
>en vald kostnadsenhet. </para
></listitem
></itemizedlist
> Tillståndet påverkar visualiseringarna. </para>
<para
>Visualiseringar visas bara för en kostnadsenhet, den aktiva. Om en given visualisering inte är lämplig för en kostnadsenhet, inaktiveras den (t.ex. om ett ELF-objekt väljs i grupplistan genom att dubbelklicka, eftersom källkodskommentarer för ett ELF-objekt inte är vettiga). </para>
<para
>För en aktiv funktion visar till exempel listan över de som blir anropade alla funktioner som anropas från den aktiva funktionen. Man kan välja en av funktionerna utan att göra den aktiv. Om anropsdiagrammet dessutom visas intill, väljes automatiskt samma funktion där. </para>
</sect1>
<sect1 id="concepts-guiparts">
<title
>Delar i det grafiska gränssnittet</title>
<sect2>
<title
>Sidopaneler</title>
<para
>Sidopaneler är sidofönster som kan placeras vid vilken kant som helst i ett fönster i Kcachegrind. De innehåller alltid en lista med kostnadsenheter sorterade på något sätt. <itemizedlist>
<listitem
><para
>Funktionsprofilering. Funktionsprofileringen är en lista med funktioner som visar kostnaden som uppstår i och utanför funktionen, namn och funktionens position. </para
></listitem>
<listitem
><para
>Översikt över delar </para
></listitem>
<listitem
><para
>Anropsstack </para
></listitem>
</itemizedlist>
</para>
</sect2>
<sect2>
<title
>Visualiseringsområde</title>
<para
>Visualiseringsområdet, typiskt den högra delen av ett huvudfönster i Kcachegrind, består av en (förvalt värde) eller flera flikvyer, antingen uppradade horisontellt eller vertikalt. Varje flikvy innehåller olika visualiseringsvyer av en enda kostnadsenhet åt gången. Namnet på enheten visas längst upp i flikvyn. Om det finns flera flikvyer, är bara en aktiv. Enhetsnamnet i den aktiva flikvyn visas med fetstil, och avgör den aktiva kostnadsenheten i Kcachegrinds fönster. </para>
</sect2>
<sect2>
<title
>Områden i en flikvy</title>
<para
>Varje flikvy kan innehålla upp till fyra visningsområden, närmare bestämt uppe, höger, vänster och nere. Varje område kan innehålla flera visualiseringsvyer ovanpå varandra. Den synliga delen av ett område väljes med en flikrad. Flikrader för det övre och högra området är längst upp, flikrader för det vänstra och nedre området är längst ner. Du kan ange vilka sorters visualiseringar som ska hamna i de olika områdena genom att använda flikarnas sammanhangsberoende menyer. </para>
</sect2>
<sect2>
<title
>Synkroniserad visualisering via vald enhet i en flikvy</title>
<para
>Förutom en aktiv enhet, har varje flikvy en vald enhet. Eftersom de flesta visualiseringstyper visar flera enheter med den aktiva centrerad på något sätt, kan du ändra valt objekt genom att navigera i en visualisering (genom att klicka med musen eller använda tangentbordet). Ofta visas valda objekt med markeringar. Genom att ändra vald enhet i en av visualiseringarna i flikvyn, markeras den nyvalda enheten i alla andra visualiseringar i flikvyn på motsvarande sätt. </para>
</sect2>
<sect2>
<title
>Synkronisering mellan flikvyer</title>
<para
>Om det finns flera flikvyer, gör en ändring av markeringen i en flikvy att en aktivering ändras i nästa flikvy (till höger eller nedanför). Den här sortens länkning bör till exempel möjliggöra snabb bläddring i anropsdiagram. </para>
</sect2>
<sect2>
<title
>Layouter</title>
<para
>Layouten för alla flikvyerna i ett fönster kan sparas (se menyalternativet Visa/Layout). Efter nuvarande layout har duplicerats (Ctrl+Plus eller meny) och någon storlek har ändrats eller en visualiseringsvy har flyttats till ett annat område i en flikvy, kan du snabbt byta mellan den gamla och den nya layouten via Ctrl+Vänsterpil eller Ctrl+Högerpil. Layoutuppsättningarna sparas mellan sessioner i Kcachegrind med samma profileringskommando. Du kan göra den nuvarande layoutuppsättningen standard för nya sessioner i Kcachegrind, eller återställa standarduppsättningen. </para>
</sect2>
</sect1>
<sect1 id="concepts-sidedocks">
<title
>Sidopaneler</title>
<sect2>
<title
>Flat profil</title>
<para
>Den flata profilen innehåller en grupp- och en funktionsvalslista. Grupplistan innehåller alla grupper där kostnader uppstår, beroende på markerad grupptyp. Grupplistan döljs när gruppering stängs av. </para>
<para
>Funktionslistan innehåller funktionerna i den valda gruppen (eller alla funktioner om gruppering är avstängd), ordnade enligt någon kolumn, t.ex. ingående kostnad eller använd egenkostnad. Det finns ett maximalt antal funktioner som visas i listan, vilket kan ställas in med Inställningar -> Anpassa Kcachegrind. </para>
</sect2>
<sect2>
<title
>Översikt över delar</title>
<para
>Under en profileringskörning kan flera profileringsdatafiler skapas, som kan laddas tillsammans i Kcachegrind. Sidorutan översikt över delar visar dem, horisontellt ordnade enligt tiden de skapades. Storleken på rektanglarna är proportionell mot kostnaden som uppstått i delarna. Du kan välja en eller flera delar för att begränsa kostnaderna som visas i övriga vyer i Kcachegrind till bara dessa delar. </para>
<para
>Delarna är ytterligare uppdelade: Det finns ett uppdelningsläge och ett samlat delningsläge: <itemizedlist>
<listitem
><para
>Uppdelning: Du ser en uppdelning i grupper för en spårningsdel, enligt vald grupptyp. Om till exempel ELF-objektgrupper är valt, ser du färgade rektanglar för varje använt ELF-objekt (delat bibliotek eller körbart program), med storlek enligt ingående kostnad. </para
></listitem>
<listitem
><para
>Samlad delning: En rektangel som visar samlad kostnad för aktuell markerad funktion i spårningsdelen visas. Den delas återigen upp, för att visa samlade kostnader för anropade funktioner. </para
></listitem>
</itemizedlist>
</para>
</sect2>
<sect2>
<title
>Anropsstack</title>
<para
>Det här är en rent uppdiktad 'mest trolig' anropsstack. Den byggs upp genom att börja med aktuell markerad funktion och lägga till de som anropar och anropade med högst kostnad längst upp och längst ner. </para>
<para
>Kolumnerna 'Kostnad' och 'Anrop' visar kostnad som behövs för alla anrop från funktionen på raden ovan. </para>
</sect2>
</sect1>
<sect1 id="concepts-visualizations">
<title
>Visualiseringar</title>
<sect2>
<title
>Händelsetyper</title>
<para
>Det här listan visar alla tillgängliga kostnadsslag och vad som är egenkostnaden och samlade kostnaden för aktuell markerad funktion för kostnadsslagen. </para>
<para
>Genom att välja en kostnadsslag i listan, ändrar du kostnadsslag för kostnader som visas överallt i Kcachegrind till det valda. </para>
</sect2>
<sect2>
<title
>Anropslistor</title>
<para
>Listorna visar anrop till/från den nuvarande aktiva funktionen. Med 'alla' de som anropar och 'alla' anropade, menas funktioner som kan nås i båda riktningarna, även om andra funktioner finns emellan. </para>
<para
>Anropslistans vy omfattar: <itemizedlist>
<listitem
><para
>De som direkt anropar </para
></listitem>
<listitem
><para
>Direkta anrop </para
></listitem>
<listitem
><para
>Alla som anropar </para
></listitem>
<listitem
><para
>Alla som anropas </para
></listitem>
</itemizedlist>
</para>
</sect2>
<sect2>
<title
>Mappningar</title>
<para
>En visualisering med träddiagram av den primära händelsetypen, uppåt eller neråt i anropshierarkin. Varje färglagd rektangel motsvarar en funktion. Storleken försöker vara proportionell mot kostnaden som uppstår i den, medan den aktiva funktionen kör (det finns dock begränsningar i uppritningen). </para>
<para
>För kartan över de som anropar, visar diagrammet hierarkin i flera nivåer för alla de som anropar den aktuella aktiverade funktionen. För kartan över de som blir anropade visar det hierarkin i flera nivåer för alla de som blir anropade av den aktuella aktiverade funktionen. </para>
<para
>Utseendealternativ hittas i den sammanhangsberoende menyn. För att få exakta storleksförhållanden, välj 'Bara riktiga kanter". Eftersom läget kan ta mycket lång tid, kanske du först vill begränsa maximalt antal uppritade nivåer. 'Bäst' avgör delningsriktningen för inre funktioner från den yttres proportion. 'Alltid bäst' beslutar om återstående utrymme för varje funktion på samma nivå. 'Ignorera proportioner' tar utrymme för att rita funktionsnamnet innan inre funktioner ritas. Observera att storleksförhållanden kan bli väsentligt felaktiga. </para>
<para
>Tangentbordsnavigering är tillgänglig med vänster/höger piltangenter för att gå igenom objekt på samma nivå, och uppåt/neråt piltangenter för att gå upp eller ner en nivå, Returtangenten aktiverar aktuellt objekt. </para>
</sect2>
<sect2>
<title
>Anropsdiagram</title>
<para
>Den här vyn visar omgivningen till anropsdiagrammet för den aktiva funktionen. Kostnaden som visas är bara kostnaden som uppstår när den aktiva funktionen verkligen körde, dvs. kostnaden som visas för main(), om det syns, ska vara samma som kostanden för den aktiva funktionen, eftersom det är den del av den tillhörande kostnaden som uppstår i main() medan den aktiva funktionen kör. </para>
<para
>För cykler, anger blåa anropspilar att det här är ett artificiellt anrop som lagts till för att riktig uppritning, som i själva verket aldrig inträffat. </para>
<para
>Om diagrammet är större än komponentens yta, visas en översiktsruta i ena hörnet. Det finns liknande visualiseringsalternativ som i anropsträdkartan. Den valda funktionen markeras. </para>
</sect2>
<sect2>
<title
>Kommentarer</title>
<para
>Listan över assemblerkod med kommentarer visar maskinkodsinstruktionerna för aktuell markerad funktion tillsammans med (egen)kostnaden som uppstår när en instruktion utförs. Om det är en anropsinstruktion, infogas rader med information om anropet som sker i koden: Detta är den samlade kostnaden som uppstår inne i anropet, antal anrop som sker, och anropsmålet. </para>
<para
>Markera en sådan rad med anropsinformation för att aktivera anropsmålet. </para>
</sect2>
</sect1>
</chapter>
<chapter id="commands">
<title
>Kommandoreferens</title>
<sect1 id="tdecachegrind-mainwindow">
<title
>&tdecachegrind;s huvudfönster</title>
<para
></para>
<sect2>
<title
>Menyn <guimenu
>Arkiv</guimenu
></title>
<para>
<variablelist>
<varlistentry>
<term
><menuchoice
><shortcut
> <keycombo
>&Ctrl;<keycap
>N</keycap
></keycombo
> </shortcut
> <guimenu
>Arkiv</guimenu
> <guimenuitem
>Ny</guimenuitem
> </menuchoice
></term>
<listitem
><para
><action
>Öppnar ett tomt toppnivåfönster där du kan ladda profileringsdata.</action
> Det här alternativet behövs egentligen inte, eftersom Arkiv/Öppna ger ett nytt toppnivåfönster om det nuvarande redan visar någon data. </para
></listitem>
</varlistentry>
<varlistentry>
<term
><menuchoice
><shortcut
> <keycombo
>&Ctrl;<keycap
>O</keycap
></keycombo
> </shortcut
> <guimenu
>Arkiv</guimenu
> <guimenuitem
>Öppna</guimenuitem
> </menuchoice
></term>
<listitem
><para
><action
>Visar fildialogrutan för att välja en profileringsdatafil som ska laddas.</action
> Om någon data redan visas i det nuvarande toppnivåfönstret, öppnar detta ett nytt fönster. Om du vill lägga till ytterligare profileringsdata i nuvarande fönster, använd Arkiv/Lägg till. </para>
<para
>Profileringsdatafilernas namn slutar oftast med ..-, där och är valfritt och används av flera profileringsdatafiler som tillhör en programkörning. Genom att ladda en fil som bara slutar med ., laddas också befintliga datafiler för den här körningen med andra filändelser. </para>
<para
>Till exempel om profileringsdatafilerna cachegrind.out.123 och cachegrind.out.123.1 finns, laddas också den andra automatiskt genom att ladda den första. </para
></listitem>
</varlistentry>
<varlistentry>
<term
><menuchoice
><guimenu
>Arkiv</guimenu
> <guimenuitem
>Lägg till</guimenuitem
> </menuchoice
></term>
<listitem
><para
><action
>Lägger till en profileringsdatafil i nuvarande fönster.</action
> Genom att använda det kan du tvinga fram att flera datafiler laddas i samma toppnivåfönster även om de inte kommer från samma körning givet av konventionen för namngivning av profileringsdatafiler. Det kan till exempel användas för jämförelser. </para
></listitem>
</varlistentry>
<varlistentry>
<term
><menuchoice
><guimenu
>Arkiv</guimenu
> <guimenuitem
>Uppdatera</guimenuitem
> </menuchoice
></term>
<listitem
><para
><action
>Ladda om profileringsdata.</action
> Det är intressantast när en annan profileringsdatafil skapats för en programkörning som redan laddats. </para
></listitem>
</varlistentry>
<varlistentry>
<term
><menuchoice
><shortcut
> <keycombo
>&Ctrl;<keycap
>Q</keycap
></keycombo
> </shortcut
> <guimenu
>Arkiv</guimenu
> <guimenuitem
>Avsluta</guimenuitem
> </menuchoice
></term>
<listitem
><para
><action
>Avslutar</action
> &kappname;</para
></listitem>
</varlistentry>
</variablelist>
</para>
</sect2>
<sect2>
<title
>Menyn <guimenu
>Visa</guimenu
></title>
<para>
<variablelist>
<varlistentry>
<term
><menuchoice
><guimenu
>Visa</guimenu
> <guimenuitem
>Primär händelsetyp</guimenuitem
> </menuchoice
></term>
<listitem
><para
><action
>(Att göra)</action
></para
></listitem>
</varlistentry>
<varlistentry>
<term
><menuchoice
><guimenu
>Visa</guimenu
> <guimenuitem
>Sekundär händelsetyp</guimenuitem
> </menuchoice
></term>
<listitem
><para
><action
>(Att göra)</action
></para
></listitem>
</varlistentry>
<varlistentry>
<term
><menuchoice
><guimenu
>Visa</guimenu
> <guimenuitem
>Gruppering</guimenuitem
> </menuchoice
></term>
<listitem
><para
><action
>(Att göra)</action
></para
></listitem>
</varlistentry>
<varlistentry>
<term
><menuchoice
><guimenu
>Visa</guimenu
> <guimenuitem
>Layout</guimenuitem
> </menuchoice
></term>
<listitem
><para
><action
>(Att göra)</action
></para
></listitem>
</varlistentry>
<varlistentry>
<term
><menuchoice
><guimenu
>Visa</guimenu
> <guimenuitem
>Dela</guimenuitem
> </menuchoice
></term>
<listitem
><para
><action
>(Att göra)</action
></para
></listitem>
</varlistentry>
</variablelist>
</para>
</sect2>
</sect1>
</chapter>
<chapter id="faq">
<title
>Vanliga frågor</title>
&reporting.bugs; &updating.documentation; <qandaset id="faqlist">
<qandaentry>
<question>
<para
>Vad är &tdecachegrind; till för? Jag har ingen aning. </para>
</question>
<answer>
<para
>&tdecachegrind; är till hjälp vid ett sent steg i programutveckling som kallas profilering. Om du inte utvecklar program, behöver du inte &tdecachegrind;. </para>
</answer>
</qandaentry>
<qandaentry>
<question>
<para
>Vad är skillnaden mellan 'Inkl.' och 'Själv'? </para>
</question>
<answer>
<para
>De är kostnadsegenskaper för funktioner med avseende på en viss händelsetyp. Eftersom funktioner kan anropa varandra, är det rimligt att skilja på funktionens egen kostnad ('Själv') och den samlade kostnaden inklusive alla anropade funktioner ('Inkl.'). 'Själv' kallas också ibland egenkostnad. </para>
<para
>Alltså kommer du till exempel alltid att ha en samlad kostnad av nästan 100 % för main(), medan egenkostnaden är försumbar när det verkliga arbetet utförs i en annan funktion. </para>
</answer>
</qandaentry>
<qandaentry>
<question>
<para
>Verktygsraden och menyraden i min Kcachegrind ser så spartansk ut. Är det normalt?</para>
</question>
<answer>
<para
>Uppenbarligen är Kcachegrind felinstallerat på ditt system. Det rekommenderas att kompilera med installeringsprefixet satt till KDE:s baskatalog i systemet, som <command
>configure --prefix=/opt/kde3; make install</command
>. Om du väljer en annan katalog, som $HOME/kde, måste du ställa in miljövariabeln TDEDIR till den katalogen innan du kör Kcachegrind. </para>
</answer>
</qandaentry>
<qandaentry>
<question>
<para
>Om jag dubbelklickar på en funktion i anropsdiagramvyn, visas samma kostnad för main som för den aktiverade funktionen. Är det inte meningen att den alltid ska vara 100 %? </para>
</question>
<answer>
<para
>Du har aktiverat en funktion under main() som har en lägre kostnad än main(). Bara den del av funktionens totala kostnad som används när den aktiverade funktionen kör visas för en funktion, dvs. kostnaden som visas för en funktion kan aldrig vara större än kostnaden för den aktiverade funktionen. </para>
</answer>
</qandaentry>
</qandaset>
</chapter>
<chapter id="glossary">
<title
>Ordlista</title>
<para
>Det följande är en blandad lista med termer. <itemizedlist>
<listitem
><para
>Profilering: Processen att samla in statistisk information om beteende under körning från programkörningar. </para
></listitem>
<listitem
><para
>Spåra: Processen att övervaka en programkörning och lagra händelser som inträffar sorterade enligt tidsstämpling i en utdatafil, kallad spårning. </para
></listitem>
<listitem
><para
>Spårning: En följd av tidsstämplade händelser som inträffat medan en programkörning spåras. Storleken är typiskt linjär i förhållande till programkörningens körtid. </para
></listitem>
<listitem
><para
>Profileringsdatafil: En fil som innehåller data som mätts upp i ett profileringsexperiment (eller del av ett sådant) eller skapats vid efterbehandling av en spårning. Dess storlek är typiskt linjär i förhållande till programmets kodstorlek. </para
></listitem>
<listitem
><para
>Profileringsdatadel (inkorrekt används också spårningsdel): Data från en profileringsdatafil. </para
></listitem>
<listitem
><para
>Profileringsexperiment: En programkörning övervakad av ett profileringsverktyg, som möjligen skapar flera profileringsdatafiler från delar av och/eller trådar i körningen. </para
></listitem>
<listitem
><para
>Profileringsprojekt: En inställning för profileringsexperiment som används för ett program som ska profileras, kanske i flera versioner. Jämförelser av profileringsdata är ofta bara meningsfullt mellan profileringsdata som skapas av experiment som görs inom ett profileringsprojekt. </para
></listitem>
<listitem
><para
>Kostnadsenhet: Ett abstrakt objekt som hör ihop med källkod som kan tilldelas händelseantal. Dimensioner för kostnadsenheter är kodposition (t.ex. källkodsrad, funktion), dataposition (t.ex. använd datatyp, dataobjekt), körposition (t.ex. tråd, process) och kombinationer av nämnda positioner (t.ex. anrop, objekt använda av satser, data utkastad från cache). </para
></listitem>
<listitem
><para
>Händelsetyp: Den sortens händelse vars kostnad kan tilldelas till en kostnadsenhet. Det finns både verkliga händelsetyper och ärvda händelsetyper. </para
></listitem>
<listitem
><para
>Verklig händelsetyp: En händelstyp som kan mätas av ett verktyg. Det kräver att en sensor existerar för den givna händelsetypen. </para
></listitem>
<listitem
><para
>Ärvd händelsetyp: En virtuell händelsetyp som bara syns i visualiseringen, som är definierad av en formel som beräknas från verkliga händelsetyper. </para
></listitem>
<listitem
><para
>Händelsekostnader: Summering av händelser av en viss händelsetyp som inträffar medan körningen är kopplad till en viss kostnadsenhet. Kostnaden tilldelas till enheten. </para
></listitem>
</itemizedlist>
</para>
</chapter>
<chapter id="credits">
<title
>Tack till och licens</title>
<para
>&kappname; </para>
<para
>Tack till Julian Seward för det utmärkta verktyget &valgrind;, och Nicholas Nethercote för tillägget &cachegrind;. Utan dessa program, skulle inte <application
>KCachegrind</application
> finnas. Vissa av idéerna för det grafiska gränssnittet kommer också från dem. </para>
<para
>Och tack för alla felrapporter och förslag från olika användare. </para>
&underFDL; </chapter>
<appendix id="installation">
<title
>Installation</title>
<sect1 id="getting-tdecachegrind">
<title
>Hur man skaffar &tdecachegrind;</title>
<para
>&tdecachegrind; är en del av paketet &package; i &kde;. För provisoriska utgåvor med mindre stöd, &callgrind; och ytterligare dokumentation, se hemsidan på <ulink url="http://tdecachegrind.sf.net"
> http://tdecachegrind.sf.net</ulink
>. Titta där för ytterligare installations- och kompileringsinstruktioner. </para>
</sect1>
<sect1 id="requirements">
<title
>Krav</title>
<para
>För att använda &tdecachegrind; med lyckat resultat, behöver du &kde; 3.x. För att skapa profileringsdata rekommenderas &cachegrind; eller &calltree;/&callgrind;. </para>
</sect1>
<sect1 id="compilation">
<title
>Kompilering och installation</title>
&install.compile.documentation; </sect1>
<sect1 id="configuration">
<title
>Anpassning</title>
<para
>Alla inställningsalternativ finns antingen i inställningsdialogrutan eller i sammanhangsberoende menyer i visualiseringarna. </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:
-->
|