Pomôcky k cvičeniam

Pomôcky k cvičeniam

Náročnosť úloh na cvičení

Pri každej úlohe je vyznačená jej zložitosť:

  • Ľahká: vypracovanie trvá menej ako hodinu. Tieto úlohy sú zahrievacie na vnorenie sa do problematiky.

  • Stredná: 1 – 2 hodiny.

  • Ťažká: viac ako 2 hodiny. Tieto úlohy väčšinou nevyžadujú veľa kódu, ale napísať ho správne je náročné.

Dané časy sú iba odhad. Niektoré voliteľné úlohy sme neriešili a ich náročnosť iba tipujeme. Ak riešenie úlohy trvá dlhšie, ozvite sa na cvičení/Discorde.

Vo všeobecnosti platí, že úlohy z cvičení nevyžadujú veľa riadkov kódu (v priemere desiatky až niekoľko stoviek riadkov). Samotný kód je ale koncepčne komplikovaný a veľmi záleží na detailoch. Pred riešením cvičení nezabudnite preštudovať odporúčané materiály, zdrojové kódy, s ktorými budete pracovať a dokumentáciu (manuály RISC-V a iné sú na stránke predmetu v časti literatúra). Samotné riešenie implementujte malými krokmi (v zadaní je často problém rozložený na niekoľko podproblémov). Implementáciu po každom kroku otestujte.

Varovanie: najefektívnejšie je rozložiť si riešenie cvičení na niekoľko dní. Určité chyby môžu byť veľmi zarážajúce a vyžadujú veľa rozmýšľania a dôsledné ladenie.

Ladiace tipy

Niekoľko tipov, ktoré môžete využiť počas ladenia:

  • Dobre si naštudujte jazyk C a smerníky. Kniha „The C programming language (second edition)“ od Kernighana a Ritchieho je stručným popisom céčka. Užitočné cvičenia na smerníky nájdete tu. Ak nemáte hlboké znalosti C, určite si toto cvičenie vypracujte – mali by ste rozumieť, prečo tento program dáva takéto výstupy.

    Zapamätajte si tieto použitia smerníkov:

    • Ak int *p = (int*)100, potom (int)p + 1 and (int)(p + 1) sú rozdielne čísla. Prvé je 101 a druhé 104. Keď pripočítate celé číslo k smerníku (ako v druhom prípade), číslo je implicitne vynásobené veľkosťou objektu, na ktorý smerník ukazuje.
    • p[i] je z definície to isté ako *(p+i), znamená to i-ty objekt od objektu s adresou p. Vďaka predchádzajúcemu pravidlu tento zápis funguje aj vtedy, keď je veľkosť objektu väčšia ako jeden bajt.
    • &p[i] je to isté ako (p+i). Jedná sa o adresu i-teho objektu v pamäti od objektu s adresou p.

    Väčšina programov písaná v céčku nepotrebuje pretypovať smerníky na celé čísla a opačne. Operačné systémy to ale často robia. Ak uvidíte operáciu sčítania s pamäťovou adresou ako operandom, zamyslite sa, či sa jedná o celočíselnú alebo smerníkovú aritmetiku.

  • Ak vami riešená úloha už čiastočne funguje, zaznamenajte si váš postup do repozitára. Ak sa niečo pokazí, viete sa vrátiť späť a pokračovať pomalšími krokmi od miesta, kde váš kód fungoval. Viac o gite nájdete v používateľskej príručke.

  • Ak vám zlyhá test, zistite dôvod zlyhania. Použite ladenie výpismi, aby ste pochopili, čo sa deje.

  • Ak vaše ladiace výpisy produkujú veľa výstupu a chcete v nich vyhľadávať, môžete spustiť make qemu v programe script (prečítajte si manuál man script). Program script zaznamená všetok výstup do súboru, v ktorom potom môžete vyhľadávať. Nezabudnite script ukončiť.

  • Vo veľa prípadoch vám budú stačiť ladiace výpisy, ale niekedy budete musieť krokovať kód assembly alebo čítať premenné na zásobníku. Aby ste mohli použiť ladiaci nástroj gdb, najprv spustite make qemu-gdb v jednom okne a potom gdb-multiarch (alebo riscv64-linux-gnu-gdb) v druhom. Nastavte breakpoint, zadajte príkaz c (continue) a xv6 bude bežať až kým nenarazí na vami určený breakpoint. (Pozrite si prezentáciu Používanie GDB (EN) od našich kolegov z MIT pre ďalšie tipy.)

  • Ak chcete vidieť kód assembly, ktorý generuje kompilátor pre jadro alebo chcete nájsť inštrukciu na nejakej adrese jadra, pozrite súbor kernel/kernel.asm, ktorý je vytvorený vždy po kompilácii jadra. (Súbor Makefile obsahuje príkazy, ktoré generujú súbory .asm pre jadro a všetky používateľské programy.)

  • Ak jadro vyvolá neočakávanú chybu (napr. pristúpi k neplatnej pamäťovej adrese), vypíše chybovú hlášku s hodnotou počítadla inštrukcií (inštrukčný smerník sepc). Túto hodnotu môžete vyhľadať v súbore kernel.asm. Tým zistíte, v ktorej funkcii program padol. Alternatívne môžete spustiť addr2line -e kernel/kernel hodnota-pc (pozrite manuál man addr2line pre viac informácií). Ak chcete vidieť backtrace, reštartujte systém cez gdb: spustite make qemu-gdb v jednom okne a gdb (alebo riscv64-linux-gnu-gdb) v druhom, nastavte breakpoint vo funkcii panic (b panic) a zadajte príkaz c (continue). Keď jadro narazí na breakpoint, zadajte bt na vypísanie backtrace.

  • Ak jadro zamrzne (napríklad kvôli deadlocku), môžete použiť gdb na zistenie miesta, kde jadro zamrzlo. Spustite make qemu-gdb v jednom okne a gdb-multiarch (alebo riscv64-linux-gnu-gdb) v druhom a zadajte príkaz c (continue). Keď jadro zamrzne, stlačte Ctrl+C v okne qemu-gdb a napíšte bt na výpis backtrace.

  • Qemu má „monitor“, ktorý umožňuje zistiť stav virtualizovaného stroja. Otvoríte ho postupným stlačením Ctrl+A C (C je skratka pre console). Príklad výpisu tabuľky stránok: príkazom cpu vyberte jadro a potom príkazom info mem vypíšete jeho tabuľku stránok. Qemu je možné tiež spustiť iba s jedným jadrom (make CPUS=1 qemu), kedy pred výpisom tabuľky stránok nemusíte explicitne vybrať jadro.