Hlavní navigace

Ladění aplikací v Go s využitím GNU Debuggeru a debuggeru Delve

Pavel Tišnovský

Důležitou součástí vývoje aplikací je samozřejmě i jejich ladění, k němuž lze v případě jazyka Go využít jak GNU Debugger, tak i debugger nazvaný Delve, který je určen primárně pro potřeby Go.

Doba čtení: 37 minut

Sdílet

11. Debugger Delve

12. Spuštění debuggeru, načtení aplikace a nastavení breakpointů

13. Zobrazení obsahu datových typů programovacího jazyka Go

14. Zobrazení obsahu zásobníku

15. Demonstrační příklad využívající gorutiny

16. Základní informace o gorutinách

17. Demonstrační příklad používající kanály

18. Základní informace o kanálech

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Ladění aplikací naprogramovaných v Go s využitím GNU Debuggeru a debuggeru Delve

Důležitou součástí vývoje aplikací je samozřejmě i jejich ladění. V případě programovacího jazyka Go je možné využít hned několika prostředků usnadňujících ladění. Kromě nástrojů určených pro měření a následnou analýzu různých metrik (sem spadá i standardní profiler, nástroj pro trasování programu při testování, popř. standardní balíček expvar, který si popíšeme příště) se samozřejmě jedná o i o debuggery. Využít je možné klasický GNU Debugger, ovšem s několika omezeními, které vyplývají z toho, že programovací jazyk Go má některé vlastnosti, s nimiž se v GNU Debuggeru původně nepočítalo (například goroutiny). Základní seznámení s možnostmi GNU Debuggeru je věnována první část článku. Ovšem kromě toho existuje i debugger určený přímo pro ekosystém programovacího jazyka Go. Tento debugger se jmenuje Delve a jeho popisem se budeme zabývat ve druhé části dnešního článku.

Poznámka: jak GNU Debugger, tak i Delve se primárně ovládají z příkazového řádku. Ovšem oba debuggery jsou současně navrženy takovým způsobem, aby je bylo možné ovládat programově a vytvořit tak nad nimi textové uživatelské rozhraní, grafické uživatelské rozhraní či tyto debuggery zařadit do integrovaných vývojových prostředí. Dnes se ovšem budeme zabývat ovládáním těchto debuggerů z příkazového řádku, což je způsob, který dokáže jejich možnosti využít bezezbytku.

Kromě debuggerů používaných pro aplikace běžící v uživatelském prostoru samozřejmě existují i nástroje využívané pro ladění jádra, popř. pro ladění základních knihoven. Jak může vypadat použití jednotlivých nástrojů na celém „stacku“, tj. od trasování/ladění samotné uživatelské aplikace až přes řešení problémů přímo v jádře operačního systému, je naznačeno na následujícím schématu:

+----------+
|          |..... gdb
| aplikace |
|          |..... SystemTap
+----------+
     |
     |
     |...... ltrace
     |
     v
+----------+
|          |..... gdb
|  glibc   |
|          |..... SystemTap
+----------+
     |
     |
     |...... strace
     |
     v
+----------+
|          |..... SystemTap
|  jádro   |
|          |..... KGDB
+----------+

Vidíme, že dnes popisovaný GNU Debugger je možné použít jak na úrovni ladění aplikace, tak i knihoven. Kromě toho ho lze využít i pro „pitvu“ těch nativních aplikací, které byly kvůli nějakému problému zabity jádrem systému a byl pro ně vygenerován takzvaný „core dump“.

Poznámka: způsob využití strace společně s aplikacemi naprogramovanými v Go si ukážeme v některém z dalších pokračování tohoto seriálu.

2. Klasický GNU Debugger

Nejprve si popíšeme základní operace, které nám nabízí GNU Debugger při ladění aplikací vyvinutých v programovacím jazyku Go. GNU Debugger v současnosti oficiálně podporuje následující programovací jazyky:

# Jazyk
1 Ada
2 Assembly
3 C
4 C++
5 D
6 Fortran
7 Go
8 Objective-C
9 OpenCL
10 Modula-2
11 Pascal
12 Rust
Poznámka: jazyky jsou seřazeny podle abecedy, nikoli podle frekvence používání či oblíbenosti.

Nástroj GNU Debugger, který je taktéž někdy podle své spustitelné (binární) části pojmenováván gdb, primárně používá ke komunikaci s uživatelem příkazový řádek, ale alternativně lze použít i jednoduché TUI (textové uživatelské rozhraní) či protokol pro nepřímé ovládání debuggeru a v případě potřeby je možné k laděné aplikaci přidat relativně krátký „stub“ sloužící pro přímé ladění takové aplikace (touto nepochybně zajímavou problematikou se však dnes nebudeme zabývat). Většina často používaných příkazů zapisovaných na příkazový řádek má i svoji zkrácenou podobu (b=break, bt=backtrace, c=continue, f=frame) a navíc je možné používat klávesu [Tab] určenou pro automatické doplnění celého jména příkazu či jeho parametru (doplňování je kontextové). Pokud je správně nastavený terminál, bude fungovat i historie příkazového řádku, a to stejným způsobem, jaký známe ze shellu (BASH atd.). Alternativně je možné využít gdbtui s celoobrazovkovým výstupem a alespoň částečně se tak přiblížit možnostem debuggerů s plnohodnotným grafickým uživatelským rozhraním.

Práce s příkazovým řádkem GNU Debuggeru sice programátorům přináší velkou flexibilitu, ovšem je pochopitelné, že některým uživatelům příkazová řádka nemusí vyhovovat. To však – alespoň teoreticky – nemusí být nepřekonatelným problémem, protože již v úvodní kapitole jsme si řekli, že GDB je navržen takovým způsobem, aby se nad ním daly vytvářet různé nadstavby vybavené více či méně konformním textovým či grafickým uživatelským rozhraním. O nadstavbách vytvořených nad GNU Debuggerem jsem již napsal krátký seriál na „konkurenčním“ webu http://www.mojefedora.cz, takže si dnes o každé nadstavbě řekneme pouze základní informace (nejdůležitější informací je v době Googlu samozřejmě jméno nadstavby :-), zbytek se již dá dohledat na stránkách vývojářů toho kterého nástroje).

Nástroj nazvaný cgdb, o němž jsem se zmínil zde, je založený na knihovně curses, resp. ncurses, tudíž ho je možné využít v terminálu, na stroji připojeném přes SSH atd. Ve svém základním nastavení nástroj cgdb rozděluje okno terminálu (konzole) na dvě části. V horní části je zobrazen zdrojový kód laděné aplikace a v části dolní pak rozhraní samotného GNU Debuggeru. Mezi oběma částmi je možné se s využitím několika klávesových zkratek přepínat, přičemž je nutné poznamenat, že většinu složitějších příkazů je možné zadávat jen v rozhraní GNU Debuggeru. Horní část slouží zejména pro dobrou orientaci v laděném programu, pro zobrazení nastavených breakpointů (v základním nastavení je použita červená barva) a taktéž pro zobrazení místa, v němž se právě nachází laděný program (v základním nastavení je tento řádek zobrazen zeleně).

Obrázek 1: Ladění programu v cgdb. Na řádku 21 je nastaven breakpoint, proto je tento řádek zvýrazněn červeně. Řízení programu přešlo na řádek číslo 23 (zvýrazněno zeleně) a v dolní části si vývojář s využitím příkazu print vypsal obsah dvou lokálních proměnných.

O nástroji nazvaném Data Display Debugger, který je známý i pod svojí zkratkou DDD, taktéž vznikl článek na serveru mojefedora.cz, takže si zde pouze ve stručnosti řekněme, že Data Display Debugger je nadstavbou nad GNU Debuggerem, která nabízí uživatelům při ladění aplikací plnohodnotné grafické uživatelské rozhraní, jehož jednotlivé ovládací prvky a jejich chování sice mohou působit zastarale (což je způsobeno použitou GUI knihovnou), ve skutečnosti je však celé uživatelské rozhraní Data Display Debuggeru velmi flexibilní, což vynikne především při použití dvou a více monitorů (to je dnes pro vývojáře asi standardní konfigurace). Příkladem flexibility GUI jsou například „odtrhávací“ menu (tear off menu), které je možné velmi snadno změnit na nemodální dialogy s nabídkou příkazů. Data Display Debugger lze využít i pro komunikaci s dalšími debuggery, například s debuggerem jazyka Perl, debuggerem skriptů psaných v BASHi a v neposlední řadě taktéž pro ovládání pydb, což je jeden z debuggerů používaných vývojáři používajícími programovací jazyk Python.

Obrázek 2: Ukázka grafického uživatelského rozhraní programu Data Display Debugger. V horní části můžeme vidět okno se zdrojovým kódem, v prostřední části pak výpis odpovídajícího strojového kódu a spodní třetina GUI obsahuje klasický příkazový řádek GNU Debuggeru. K dispozici je i dialogové okno se základními a často používanými příkazy.

I o dalším nástroji pojmenovaném Nemiver již vyšel samostatný článek. Tento nástroj opět komunikuje s GNU Debuggerem, ovšem na rozdíl od DDD postaveného nad postarší GUI knihovnou je Nemiver primárně určený pro desktopové prostředí GNOME se všemi přednostmi a zápory, které to přináší. Nemiver tak uživatelům nabízí přehledné prostředí, které ovšem není tak flexibilní jako DDD a taktéž například možnosti zobrazení složitějších datových struktur (různých lineárně vázaných seznamů, stromů či obecnějších grafových struktur) jsou v Nemiveru omezené. Pokud ale někdo pouze potřebuje odkrokovat několik funkcí a neprovádět žádné složitější operace, může být tento nástroj pro takového uživatele dobrým řešením.

Obrázek 3: Ukázka grafického uživatelského rozhraní nástroje Nemiver při ladění uživatelské aplikace. V dolní části si povšimněte „oušek“ jednotlivých podoken.

3. Příprava aplikací napsaných v Go pro ladění v GNU Debuggeru

U aplikací naprogramovaných například v céčku je jediným požadavkem na bezproblémové použití GNU Debuggeru vypnutí optimalizací a přidání ladicích informací. Podobně je tomu i v případě, že budeme chtít ladit aplikace vytvořené v jazyku Go, navíc se ovšem ještě kvůli lepší kompatibilitě doporučuje vypnutí komprimace ladicích informací ve výsledném binárním souboru.

První pokusy s GNU Debuggerem učiníme při ladění a krokování následujícího prográmku, který po svém spuštění sečte dvě hodnoty a vypíše výsledek výpočtu na standardní výstup:

package main
 
import "fmt"
 
func main() {
        x := 10
        y := 20
 
        z := x + y
 
        fmt.Printf("%d + %d = %d\n", x, y, z)
}

Tento program přeložíme, ovšem při samotném překladu explicitně nastavíme některé nestandardní parametry překladače i linkeru. Zejména se vypne inlining funkcí a automatické použití registrů pro některé proměnné. Taktéž se zakáže již zmíněná komprimace ladicích informací:

$ go build -ldflags=-compressdwarf=false -gcflags=all="-N -l" 01_add.go

Po prvním spuštění GNU Debuggeru se zobrazí varování, že nebylo možné nakonfigurovat pomocný modul používaný při ladění aplikací naprogramovaných v Go. Současně se zobrazí i přesný návod, jak tento problém napravit:

Obrázek 4: První spuštění GNU Debuggeru s informacemi o tom, že se nepodařilo inicializovat pomocný skript používaný při ladění Go aplikací.

Jakmile výše navržené nastavení provedeme, bude již nové spuštění GNU Debuggeru bezproblémové:

Obrázek 5: Načtení přeloženého programu do GNU Debuggeru.

4. Orientace v laděném kódu

V případě, že jsou do binárního souboru laděné aplikace přiloženy i ladicí symboly, lze v GNU Debuggeru relativně snadno pracovat přímo se zdrojovým kódem a využívat jména funkcí, jména proměnných, parametrů funkcí atd. Taktéž je možné prohlížet obsah datových struktur, polí, popř. i řezů, který bude správně interpretován. Nyní si ukažme některé příkazy, které využijete prakticky při každém ladění. Debugger přitom bude spuštěn a bude do něho načtena binární verze prvního příkladu:

$ gdb 01_add

Základním příkazem, který se může hodit například při nastavování breakpointů, je příkaz info functions, který vypíše jak všechny funkce z aplikace, tak i přímo či nepřímo volané funkce (pro zápis tohoto příkazu použijte klávesu [Tab] pro kontextové doplňování):

Obrázek 6: Zobrazení seznamu všech funkcí nalezených v načteném binárním programu a/nebo binární knihovně po zadání příkazu list functions.

Poznámka: povšimněte si důležité novinky – na rozdíl od jazyka C mají všechny funkce naprogramované v Go po překladu plný název obsahující mj. i jméno balíčku. To v praxi znamená, že vstupní funkce se nejmenuje pouze main, ale balíček.main, v našem případě main.main. Tento formát je dodržen i u názvů metod, u nichž je specifikován příjemce v závorkách.

Dalším užitečným příkazem je příkaz list, který ve svém výchozím nastavení vypíše deset řádků zdrojového kódu. Opakováním příkazu (či pouhým stlačením klávesy Enter, což má stejný význam, jako explicitní opakování posledního příkazu) se zobrazí dalších deset řádků atd.:

(gdb) list
1       package main
2
3       import "fmt"
4
5       func main() {
6               x := 10
7               y := 20
8
9               z := x + y
10

K příkazu list je možné explicitně přidat i číslo prvního a posledního vypisovaného řádku, což je výhodné v případě, že z nějakého chybového hlášení víte, na kterém řádku k chybě došlo.

(gdb) list 5,10
5       func main() {
6               x := 10
7               y := 20
8
9               z := x + y
10

Užitečnější je však použití příkazu list společně se jménem funkce, protože nemusíte složitě zjišťovat, na jakém řádku je hledaná funkce zapsána (ani v jakém je souboru):

(gdb) list main.main
1       package main
2
3       import "fmt"
4
5       func main() {
6               x := 10
7               y := 20
8
9               z := x + y
10

Pokud chcete jméno funkce vybrat ze seznamu, stačí napsat jen příkaz list, mezeru a potom stlačit klávesu [Tab].

Užitečné mohou být i další varianty příkazu info:

(gdb) info
info address -- Describe where symbol SYM is stored
info all-registers -- List of all registers and their contents
info args -- Argument variables of current stack frame
info auto-load -- Print current status of auto-loaded files
info auto-load-scripts -- Print the list of automatically loaded Python scripts
info auxv -- Display the inferior's auxiliary vector
...
...
...
info warranty -- Various kinds of warranty you do not have
info watchpoints -- Status of specified watchpoints (all watchpoints if no argument)
info win -- List of all displayed windows
 
Type "help info" followed by info subcommand name for full documentation.
Type "apropos word" to search for commands related to "word".
Command name abbreviations are allowed if unambiguous.

Například:

(gdb) info line
Line 5 of "/home/tester/go-root/article_25/01_add.go" starts at address 0x49dc40 <main.main>
   and ends at 0x49dc58 <main.main+24>.

5. Spuštění programu, nastavení breakpointů a krokování

Nyní se podíváme na způsob spuštění laděné aplikace přímo v GNU Debuggeru. To je ve skutečnosti velmi jednoduché a přímočaré, protože postačuje zadat příkaz run či jen r:

(gdb) r
Starting program: /home/tester/temp/go-root/article_25/01_add
10 + 20 = 30
[Inferior 1 (process 4244) exited normally]
Poznámka: při prvních sezeních s GNU Debuggerem je vhodné si vytisknout GDB Cheatsheet.

Na libovolnou funkci, řádek či dokonce jednotlivou strojovou instrukci je možné zaregistrovat breakpoint. Pokud se běh programu dostane na breakpoint, je jeho běh přerušen a řízení se vrátí zpět uživateli do debuggeru. Nejjednodušeji se nastavuje breakpoint na vstupním bodě nějaké (libovolně zvolené) funkce. V tomto případě postačuje zadat příkaz break se jménem funkce:

Obrázek 7: Nastavení breakpointu na první příkaz ve funkcí main. Plné jméno této funkce musí obsahovat i balíček, tedy main.main.

Poznámka: opět si povšimněte, že plné jméno funkce je tvořeno názvem balíčku, tečkou a samotným jménem funkce (viz předchozí kapitolu, kde jsme zmínili i metody). V některých případech však GNU Debugger nemusí tečku správně interpretovat, takže je nutné úplné jméno funkce zapsat do uvozovek.

Pokud nyní program znovu spustíme, zastaví se na právě nastaveném breakpointu:

Obrázek 8: Po spuštění programu příkazem r nebo run se jeho běh zastaví na breakpointu, tj. na prvním příkazu ve funkci main.

Nyní máme možnost pokračovat v běhu (c/continue), ale zajímavější je použít příkaz n/next, který vykoná pouze jediný programový řádek a posléze se běh programu opět zastaví. Vykonávaný řádek se přitom vždy vypíše (pokud tedy nemáme zobrazeno TUI, potom se GNU Debugger chová odlišně):

Obrázek 9: Krokování jednotlivými příkazy pomocí příkazu n nebo next.

Poznámka: užitečná je i další vlastnost GNU Debuggeru – při stisku klávesy Enter se zopakuje poslední příkaz, což je zvlášť výhodné u příkazu n.

O tom, že GNU Debugger není žádné ořezávátko, možná svědčí i to, že se v průběhu ladění může použít mj. i příkaz print akceptující různé typy výrazů, lokální i globální proměnné, formátování atd.:

Obrázek 10: Po vykonání posledního příkazu ve funkci main se program pochopitelně ukončí.

6. Využití TUI GNU Debuggeru

Pro úplnost se ještě podívejme na to, jak vypadá TUI (textové uživatelské rozhraní) zabudované do GNU Debuggeru, přesněji řečeno do jeho novějších verzí. TUI je nutné povolit, a to buď spuštěním GNU Debuggeru nikoli příkazem gdb, ale příkazem gdbtui, popř. kdykoli při běhu GNU Debuggeru příkazem:

(gdb) tui enable

Obrázek 11: GNU Debugger po zobrazení textového uživatelského rozhraní ve chvíli, kdy je nastaven breakpoint na vstupní bod (přesněji řečeno na první příkaz) ve funkci main. Na levém okraji se zobrazují jak breakpointy, tak i ukazatel na právě prováděný či krokovaný příkaz.

V základním nastavení je terminál rozdělen do dvou oken – první okno obsahuje zdrojový kód, druhé pak klasické rozhraní příkazového řádku. Ve skutečnosti je však možné zobrazit i další okna, například speciální okno s registry atd. V okně se zdrojovým kódem se navíc zobrazují breakpointy a další důležité informace, takže „režim TUI“ může být pro mnohé vývojáře velmi užitečný (dokonce si dovolím říct, že je časově výhodnější se naučit pracovat v tomto režimu, než řešit problémy s různými GUI nadstavbami nad GNU Debuggerem).

Obrázek 12: Krokování programu (příkaz n/next) při zobrazeném TUI. Zatímco znak breakpointu zůstal (pochopitelně) na svém místě, ukazatel na právě prováděný příkaz se přesunul.

Poznámka: podrobnější informace o TUI získáte příkazem help tui, všechny příkazy, které se TUI zabývají pak příkazem apropos tui:
(gdb) apropos tui
set tui -- TUI configuration variables
set tui active-border-mode -- Set the attribute mode to use for the active TUI window border
set tui border-kind -- Set the kind of border for TUI windows
set tui border-mode -- Set the attribute mode to use for the TUI window borders
show tui -- TUI configuration variables
show tui active-border-mode -- Show the attribute mode to use for the active TUI window border
show tui border-kind -- Show the kind of border for TUI windows
show tui border-mode -- Show the attribute mode to use for the TUI window borders
tui -- Text User Interface commands
tui reg -- TUI commands to control the register window

7. Příkaz display

Již v předchozích kapitolách jsme si řekli, že libovolnou proměnnou (či dokonce výsledek nějakého složitějšího výrazu) je možné zobrazit příkazem print. Velmi často, zejména při ladění různých programových smyček, je však zapotřebí neustále zjišťovat hodnoty několika proměnných, a to v každém sledovaném kroku. V tomto případě je pochopitelně opakované použití příkazu print nepříjemné, ovšem GNU Debugger obsahuje ještě jeden příkaz: display:

(gdb) help display
Print value of expression EXP each time the program stops.
/FMT may be used before EXP as in the "print" command.
/FMT "i" or "s" or including a size-letter is allowed,
as in the "x" command, and then EXP is used to get the address to examine
and examining is done as in the "x" command.
 
With no argument, display all currently requested auto-display expressions.
Use "undisplay" to cancel display requests previously made.

Tímto příkazem je tedy možné „zaregistrovat“ ty výrazy, které se mají neustále vyhodnocovat, a to v každém kroku ladění. Podívejme se na praktické použití tohoto velmi užitečného příkazu:

Obrázek 13: Při krokování funkcí main.main budeme sledovat obsah lokálních proměnných x, y a z. Povšimněte si, že ihned po vstupu do funkce mají tyto proměnné nedefinovanou hodnotu.

Obrázek 14: Třikrát jsme spustili příkaz next, takže se provedly první tři řádky s příkazy ve funkci main.main. Před provedením příkazu na řádku 11 jsou všechny sledované proměnné inicializované a mají očekávané hodnoty.

8. Informace o lokálních proměnných, zobrazení datových typů programovacího jazyka Go

Nyní si ukážeme, jak dobře (či špatně) dokáže GNU Debugger sledovat hodnoty lokálních proměnných ve chvíli, kdy jsou použity datové typy specifické pro programovací jazyk Go. Začneme aplikací, v níž se používá jedno pole a několik řezů (slice):

package main
 
import "fmt"
 
func main() {
        var a1 [20]int
 
        for i := 0; i < len(a1); i++ {
                a1[i] = i + 1
        }
 
        var slice1 []int = a1[:]
        var slice2 []int = a1[5:15]
        slice3 := slice2[5:]
 
        fmt.Println(a1)
        fmt.Println(slice1)
        fmt.Println(slice2)
        fmt.Println(slice3)
}

Překlad aplikace:

$ go build -ldflags=-compressdwarf=false -gcflags=all="-N -l" 02_arrays_slices.go

Spuštění debuggeru:

$ gdb 02_arrays_slices

Nyní již můžeme sledovat chování debuggeru při krokování jednotlivými příkazy ve funkci main.main:

Obrázek 15: Načtení aplikace do GNU Debuggeru a zobrazení jejího zdrojového kódu.

Obrázek 16: Po nastavení breakpointu a spuštění programu se dostaneme před první příkaz funkce main.main.

Obrázek 17: Příkazem info locals zobrazíme informaci o všech lokálních proměnných funkce, ve které se nacházíme.

Obrázek 18: Příkazem n přejdeme až na řádek, v němž jsou všechny lokální proměnné inicializovány.

Obrázek 19: Zobrazení obsahu řezů (slices).

Obrázek 20: Pseudofunkcemi $cap() a $len() můžeme získat informace o kapacitě řezu a o jeho délce (což jsou dvě hodnoty, které se od sebe mohou lišit).

9. Zobrazení prvků struktur

Podobným způsobem si můžeme vyzkoušet, jak dobře dokáže GNU Debugger zobrazit informace o strukturách (záznamech). Pro toto otestování použijeme zdrojový kód s novým datovým typem představujícím uživatelsky definovanou datovou strukturu pojmenovanou jednoduše User:

package main
 
import "fmt"
 
type User struct {
        id      uint32
        name    string
        surname string
}
 
func main() {
        user1 := User{
                id:      1,
                name:    "Linus",
                surname: "Torvalds"}
 
        fmt.Println(user1)
 
        user1.id = 2
        user1.name = "Steve"
        user1.surname = "Ballmer"
 
        fmt.Println(user1)
}

Opět si ukažme chování GNU Debuggeru:

Obrázek 21: Načtení aplikace do GNU Debuggeru a zobrazení jejího zdrojového kódu. Nastavení breakpointu a zobrazení informace o datovém typu User.

Použili jsme zde nový příkaz whatis:

(gdb) help whatis
Print data type of expression EXP.
Only one level of typedefs is unrolled.  See also "ptype".

Obrázek 22: Zajímavá (ale nijak nebezpečná) situace, která nastala ve chvíli, kdy není obsah datové struktury inicializován, tj. její prvky mohou obsahovat jakoukoli hodnotu.

Obrázek 23: I samotná inicializace datové struktury je krokovatelná; v tomto případě se skládá ze třech kroků.

10. Informace o zásobníkových rámcích a o vláknech

Při ladění se velmi často dostaneme do situace, kdy je nutné sledovat obsah zásobníkových rámců (stack frame). V GNU Debuggeru se pro tento účel používá příkaz info frame, který je použitelný i při ladění aplikací naprogramovaných v jazyku Go (i když tento jazyk používá jiný formát rámců, mj. i kvůli podpoře gorutin atd.). Tuto funkcionalitu otestujeme na příkladu, v němž se rekurzivně počítá hodnota faktoriálu:

package main
 
import "fmt"
 
func factorial(x uint) uint {
        if x == 0 {
                return 1
        } else {
                return x * factorial(x-1)
        }
}
 
func main() {
        fmt.Println(factorial(4))
}

Breakpoint přitom nastavíme na příkaz return 1, který je umístěn v podmínce zaručující, že již došlo k celé zavinovací (winding) fázi rekurze:

Obrázek 24: Načtení laděného programu a nastavení breakpointu.

Obrázek 25: Spuštění programu (ten se zastaví na breakpointu) a vytištění obsahu aktuálního zásobníkového rámce příkazem info frame.

Obrázek 26: Zobrazit si můžeme i takzvaný backtrace, z něhož lze vyčíst historii zavinovací fáze rekurzivního výpočtu.

Obrázek 27: Získat je možné i seznam běžících gorutin, popř. fyzických vláken. Zásobníkové rámce se poté vypíšou pro vybrané vlákno.

11. Debugger Delve

Ve druhé části dnešního článku se seznámíme s některými základními možnostmi, které jsou nabízeny debuggerem Delve. Na rozdíl od GNU Debuggeru, který podporuje mnoho programovacích jazyků i formátů souborů/knihoven, je Delve určen primárně pro použití těmi vývojáři, kteří používají programovací jazyk Go. Delve je přitom pro Go programátory doporučovaným primárním debuggerem, což mj. znamená, že práce na vylepšení kooperace mezi Go a GNU Debuggerem mají relativně nízkou prioritu. I to může být jeden z důvodů, proč je doporučován přechod na tento debugger, i když je nutné již na úvod poznamenat, že ne všechny funkce, které jsou dostupné v GNU Debuggeru nalezneme i v nástroji Delve.

Obrázek 28: Přepínače, s nimiž je možné spustit debugger Delve.

Debugger Delve se nainstaluje podobně, jako jakýkoli jiný balíček připravený pro programovací jazyk Go:

$ go get -u github.com/go-delve/delve/cmd/dlv

Po instalaci se pro jistotu přesvědčte, že v proměnné prostředí PATH je nastavena i cesta do adresáře ~/go/bin/.

Obrázek 29: Část seznamu všech příkazů debuggeru Delve.

12. Spuštění debuggeru, načtení aplikace a nastavení breakpointů

Debugger Delve se spouští příkazem dlv. Při spuštění je nutné specifikovat, jaká operace se má provést. Pro ladění a krokování aplikací se použije operace debug a v tomto případě je debuggeru nutné předat i jméno balíčku, popř. jméno souboru se zdrojovým kódem (samotný překlad nemusíme provést):

$ dlv debug 01_add.go

Breakpoint se nastavuje stejným příkazem b, jako v GNU Debuggeru:

(dlv) b man.main

Následně se může program spustit příkazem c (continue).

Obrázek 30: Spuštění debuggeru, inicializace laděné aplikace, nastavení breakpointu a doskok na breakpoint.

Příkazy n a locals pracují podobně, jako v GNU Debuggeru:

Obrázek 31: Výpis obsahu lokálních proměnných, přičemž pouze dvě z nich jsou v daný okamžik inicializovány.

Obrázek 32: Krokovat je možné i kódem, který se spouští při opouštění aplikace.

13. Zobrazení obsahu datových typů programovacího jazyka Go

Debugger Delve pochopitelně dokáže pracovat s hodnotami libovolného datového typu programovacího jazyka Go. Pro otestování těchto funkcí použijeme ty stejné demonstrační příklady, jako tomu bylo u GNU Debuggeru:

Obrázek 33: Zobrazení obsahu (prvků) pole a řezu. Povšimněte si, že v případě řezu se zobrazí jak kapacita, tak i aktuální délka.

Obrázek 34: Zobrazení obsahu uživatelsky definované datové struktury (záznamu).

Další možnosti jsou popsány v nápovědě:

(dlv) help print
Evaluate an expression.
 
        [goroutine ] [frame ] print
 
See $GOPATH/src/github.com/go-delve/delve/Documentation/cli/expr.md for a description of supported expressions.
(dlv) help locals
Print local variables.
 
        [goroutine <n>] [frame <m>] locals [-v] [<regex>]
 
The name of variables that are shadowed in the current scope will be shown in parenthesis.
 
If regex is specified only local variables with a name matching it will be returned. If -v is specified more information about each local variable will be shown.

14. Zobrazení obsahu zásobníku

I v Delve je pochopitelně možné zobrazit obsah zásobníku, resp. přesněji řečeno obsah jednotlivých zásobníkových rámců. Tuto funkcionalitu si opět vyzkoušíme na demonstračním příkladu, v němž je implementován rekurzivní výpočet faktoriálu. Breakpoint nastavíme na příkaz return 1, jenž je zavolaný při ukončení zavinovací fáze rekurze:

Obrázek 35: Zastavení běhu programu (resp. aktuální gorutiny) na řádku return 1.

Následně je možné použít příkaz stack pro zobrazení historie volání funkcí:

(dlv) help stack
Print stack trace.
 
        [goroutine <n>] [frame <m>] stack [<depth>] [-full] [-offsets] [-defer] [-a <n>] [-adepth <depth>]
 
        -full           every stackframe is decorated with the value of its local variables and arguments.
        -offsets        prints frame offset of each frame.
        -defer          prints deferred function call stack for each frame.
        -a <n>          prints stacktrace of n ancestors of the selected goroutine (target process must have tracebackancestors enabled)
        -adepth <depth> configures depth of ancestor stacktrace

Obrázek 36: Zobrazení obsahu zásobníkových rámců.

Podrobnější informace poskytne příkaz stack -full:

(dlv) stack -full
 
0  0x00000000004a0b70 in main.factorial
   at ./04_factorial.go:7
       x = 0
       ~r1 = 0
 
1  0x00000000004a0b94 in main.factorial
   at ./04_factorial.go:9
       x = 1
       ~r1 = 0
 
2  0x00000000004a0b94 in main.factorial
   at ./04_factorial.go:9
       x = 2
       ~r1 = 0
 
3  0x00000000004a0b94 in main.factorial
   at ./04_factorial.go:9
       x = 3
       ~r1 = 0
 
4  0x00000000004a0b94 in main.factorial
   at ./04_factorial.go:9
       x = 4
       ~r1 = 0
 
5  0x00000000004a0bee in main.main
   at ./04_factorial.go:14
 
6  0x000000000042da0f in runtime.main
   at /opt/go/src/runtime/proc.go:200
       needUnlock = false
       g = (*runtime.g)(0xc000000180)
       fn = (unreadable empty OP stack)
 
7  0x0000000000457a81 in runtime.goexit
   at /opt/go/src/runtime/asm_amd64.s:1337

15. Demonstrační příklad využívající gorutiny

Pro zjištění informací o gorutinách použijeme jiný demonstrační příklad, který po svém spuštění inicializuje a spustí tři další gorutiny, jenž budou pracovat současně:

package main
 
import (
        "fmt"
        "time"
)
 
func print_chars() {
        for ch := 'a'; ch <= 'z'; ch++ {
                fmt.Printf("%c", ch)
                time.Sleep(200 * time.Millisecond)
        }
}
 
func print_dots() {
        for i := 0; i < 30; i++ {
                fmt.Print(".")
                time.Sleep(200 * time.Millisecond)
        }
}
 
func print_spaces() {
        for i := 0; i < 60; i++ {
                fmt.Print(" ")
                time.Sleep(110 * time.Millisecond)
        }
}
 
func main() {
        fmt.Println("main begin")
        go print_chars()
        go print_spaces()
        go print_dots()
        time.Sleep(6 * time.Second)
        fmt.Println("main end")
}

16. Základní informace o gorutinách

Po nastavení breakpointu na řádek:

time.Sleep(6 * time.Second)

si můžeme zobrazit všechny gorutiny, a to příkazem goroutines. Příkaz gorutine (bez „s“ na konci) slouží pro zobrazení podrobnějších informací o aktuálně sledované gorutině:

Obrázek 37: Informace o aktuálně sledované gorutině i seznam všech dalších gorutin.

Poznámka: povšimněte si, že Delve nepoužívá příkaz info …něco…, což poněkud prodlužuje seznam základních příkazů.

Další informace samozřejmě opět poskytne nápověda:

(dlv) help goroutine
Shows or changes current goroutine
...
...
...

a:

(dlv) help goroutines
List program goroutines.
 
        goroutines [-u (default: user location)|-r (runtime location)|-g (go statement location)|-s (start location)] [ -t (stack trace)]
 
Print out info for every goroutine. The flag controls what information is shown along with each goroutine:
 
        -u      displays location of topmost stackframe in user code
        -r      displays location of topmost stackframe (including frames inside private runtime functions)
        -g      displays location of go instruction that created the goroutine
        -s      displays location of the start function
        -t      displays stack trace of goroutine
 
If no flag is specified the default is -u.

17. Demonstrační příklad používající kanály

Kanály slouží mj. i pro předávání dat mezi gorutinami. Z pohledu debuggeru Delve se jedná o běžný datový typ (i když interně dosti komplikovaný), jehož obsah lze snadno zobrazit. To si ověříme na následujícím příkladu:

package main
 
import "fmt"
 
func message(id int, channel chan int) {
        fmt.Printf("gorutina %d\n", id)
        channel <- 1
}
 
func main() {
        channel := make(chan int)
 
        fmt.Println("main begin")
        go message(1, channel)
 
        fmt.Println("waiting...")
 
        code, status := <-channel
 
        fmt.Printf("received code: %d and status: %t\n", code, status)
        fmt.Println("main end")
}

18. Základní informace o kanálech

Informace o kanálu, resp. přesněji řečeno o obsahu proměnné typu kanál, se zobrazí příkazem print nebo zkráceně jen p:

Obrázek 38: Zobrazení informací o kanálu po jeho inicializaci v debuggeru.

Obrázek 39: Druhá obrazovka naznačující, jak je kanál ve skutečnosti složitou datovou strukturou.

Obrázek 40: V této chvíli běží v aplikaci dvě gorutiny.

Podrobnější informace o frontě vytvořené pro předávání zpráv přes kanál:

(dlv) p channel.sendq
waitq<int> {
        first: *sudog<int> {
                g: *(*runtime.g)(0xc000001080),
                isSelect: false,
                next: *runtime.sudog nil,
                prev: *runtime.sudog nil,
                elem: *1,
                acquiretime: 0,
                releasetime: 0,
                ticket: 0,
                parent: *runtime.sudog nil,
                waitlink: *runtime.sudog nil,
                waittail: *runtime.sudog nil,
                c: *(*runtime.hchan)(0xc000080060),},
        last: *sudog<int> {
                g: *(*runtime.g)(0xc000001080),
                isSelect: false,
                next: *runtime.sudog nil,
                prev: *runtime.sudog nil,
                elem: *1,
                acquiretime: 0,
                releasetime: 0,
                ticket: 0,
                parent: *runtime.sudog nil,
                waitlink: *runtime.sudog nil,
                waittail: *runtime.sudog nil,
                c: *(*runtime.hchan)(0xc000080060),},}

Obrázek 41: Přepnutí na jinou gorutinu, která se bude sledovat.

19. Repositář s demonstračními příklady

Zdrojové kódy všech šesti dnes použitých demonstračních příkladů byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/go-root (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má přibližně dva megabajty), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:

# Soubor Popis Cesta
1 01_add.go jednoduchý program, který sečte dvojici celočíselných hodnot https://github.com/tisnik/go-root/blob/master/article25/01_add.go
2 02_arrays_slices.go základní operace s poli a s řezy https://github.com/tisnik/go-root/blob/master/article25/02_a­rrays_slices.go
3 03_structs.go práce se strukturami (záznamy) https://github.com/tisnik/go-root/blob/master/article25/03_struc­ts.go
4 04_factorial.go rekurzivní výpočet faktoriálu https://github.com/tisnik/go-root/blob/master/article25/04_fac­torial.go
5 05_goroutines.go spuštění dvou gorutin https://github.com/tisnik/go-root/blob/master/article25/05_go­routines.go
6 06_channels.go gorutiny a kanály https://github.com/tisnik/go-root/blob/master/article25/06_chan­nels.go

20. Odkazy na Internetu

  1. Delve: a debugger for the Go programming language.
    https://github.com/go-delve/delve
  2. Příkazy debuggeru Delve
    https://github.com/go-delve/delve/tree/master/Do­cumentation/cli
  3. Debuggery a jejich nadstavby v Linuxu
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/
  4. Debuggery a jejich nadstavby v Linuxu (2. část)
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/
  5. Debuggery a jejich nadstavby v Linuxu (3): Nemiver
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/
  6. Debuggery a jejich nadstavby v Linuxu (4): KDbg
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/
  7. Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/
  8. Debugging Go Code with GDB
    https://golang.org/doc/gdb
  9. Debugging Go (golang) programs with gdb
    https://thornydev.blogspot­.com/2014/01/debugging-go-golang-programs-with-gdb.html
  10. GDB – Dokumentace
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/
  11. GDB – Supported Languages
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/Suppor­ted-Languages.html#Supported-Languages
  12. GNU Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Debugger
  13. The LLDB Debugger
    http://lldb.llvm.org/
  14. Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/Debugger
  15. 13 Linux Debuggers for C++ Reviewed
    http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817
  16. Go is on a Trajectory to Become the Next Enterprise Programming Language
    https://hackernoon.com/go-is-on-a-trajectory-to-become-the-next-enterprise-programming-language-3b75d70544e
  17. Go Proverbs: Simple, Poetic, Pithy
    https://go-proverbs.github.io/
  18. Handling Sparse Files on Linux
    https://www.systutorials.com/136652/han­dling-sparse-files-on-linux/
  19. Gzip (Wikipedia)
    https://en.wikipedia.org/wiki/Gzip
  20. Deflate
    https://en.wikipedia.org/wiki/DEFLATE
  21. 10 tools written in Go that every developer needs to know
    https://gustavohenrique.net/en/2019/01/10-tools-written-in-go-that-every-dev-needs-to-know/
  22. Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
    https://www.root.cz/clanky/he­xadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/
  23. Hex dump
    https://en.wikipedia.org/wi­ki/Hex_dump
  24. Rozhraní io.ByteReader
    https://golang.org/pkg/io/#ByteReader
  25. Rozhraní io.RuneReader
    https://golang.org/pkg/io/#RuneReader
  26. Rozhraní io.ByteScanner
    https://golang.org/pkg/io/#By­teScanner
  27. Rozhraní io.RuneScanner
    https://golang.org/pkg/io/#Ru­neScanner
  28. Rozhraní io.Closer
    https://golang.org/pkg/io/#Closer
  29. Rozhraní io.Reader
    https://golang.org/pkg/io/#Reader
  30. Rozhraní io.Writer
    https://golang.org/pkg/io/#Writer
  31. Typ Strings.Reader
    https://golang.org/pkg/strin­gs/#Reader
  32. VACUUM (SQL)
    https://www.sqlite.org/lan­g_vacuum.html
  33. VACUUM (Postgres)
    https://www.postgresql.or­g/docs/8.4/sql-vacuum.html
  34. go-cron
    https://github.com/rk/go-cron
  35. gocron
    https://github.com/jasonlvhit/gocron
  36. clockwork
    https://github.com/whiteShtef/cloc­kwork
  37. clockwerk
    https://github.com/onatm/clockwerk
  38. JobRunner
    https://github.com/bamzi/jobrunner
  39. Rethinking Cron
    https://adam.herokuapp.com/pas­t/2010/4/13/rethinking_cron/
  40. In the Beginning was the Command Line
    https://web.archive.org/web/20180218045352/htt­p://www.cryptonomicon.com/be­ginning.html
  41. repl.it (REPL pro různé jazyky)
    https://repl.it/languages
  42. GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
    https://github.com/jroimartin/gocui
  43. Read–eval–print loop
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  44. go-prompt
    https://github.com/c-bata/go-prompt
  45. readline
    https://github.com/chzyer/readline
  46. A pure golang implementation for GNU-Readline kind library
    https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/
  47. go-readline
    https://github.com/fiorix/go-readline
  48. 4 Python libraries for building great command-line user interfaces
    https://opensource.com/article/17/5/4-practical-python-libraries
  49. prompt_toolkit 2.0.3 na PyPi
    https://pypi.org/project/prom­pt_toolkit/
  50. python-prompt-toolkit na GitHubu
    https://github.com/jonathan­slenders/python-prompt-toolkit
  51. The GNU Readline Library
    https://tiswww.case.edu/php/chet/re­adline/rltop.html
  52. GNU Readline (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Readline
  53. readline — GNU readline interface (Python 3.x)
    https://docs.python.org/3/li­brary/readline.html
  54. readline — GNU readline interface (Python 2.x)
    https://docs.python.org/2/li­brary/readline.html
  55. GNU Readline Library – command line editing
    https://tiswww.cwru.edu/php/chet/re­adline/readline.html
  56. gnureadline 6.3.8 na PyPi
    https://pypi.org/project/gnureadline/
  57. Editline Library (libedit)
    http://thrysoee.dk/editline/
  58. Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
    https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/
  59. libedit or editline
    http://www.cs.utah.edu/~bi­gler/code/libedit.html
  60. WinEditLine
    http://mingweditline.sourceforge.net/
  61. rlcompleter — Completion function for GNU readline
    https://docs.python.org/3/li­brary/rlcompleter.html
  62. rlwrap na GitHubu
    https://github.com/hanslub42/rlwrap
  63. rlwrap(1) – Linux man page
    https://linux.die.net/man/1/rlwrap
  64. readline(3) – Linux man page
    https://linux.die.net/man/3/readline
  65. history(3) – Linux man page
    https://linux.die.net/man/3/history
  66. Dokumentace k balíčku oglematchers
    https://godoc.org/github.com/ja­cobsa/oglematchers
  67. Balíček oglematchers
    https://github.com/jacobsa/o­glematchers
  68. Dokumentace k balíčku ogletest
    https://godoc.org/github.com/ja­cobsa/ogletest
  69. Balíček ogletest
    https://github.com/jacobsa/ogletest
  70. Dokumentace k balíčku assert
    https://godoc.org/github.com/stret­chr/testify/assert
  71. Testify – Thou Shalt Write Tests
    https://github.com/stretchr/testify/
  72. package testing
    https://golang.org/pkg/testing/
  73. Golang basics – writing unit tests
    https://blog.alexellis.io/golang-writing-unit-tests/
  74. An Introduction to Programming in Go / Testing
    https://www.golang-book.com/books/intro/12
  75. An Introduction to Testing in Go
    https://tutorialedge.net/golang/intro-testing-in-go/
  76. Advanced Go Testing Tutorial
    https://tutorialedge.net/go­lang/advanced-go-testing-tutorial/
  77. GoConvey
    http://goconvey.co/
  78. Testing Techniques
    https://talks.golang.org/2014/tes­ting.slide
  79. 5 simple tips and tricks for writing unit tests in #golang
    https://medium.com/@matryer/5-simple-tips-and-tricks-for-writing-unit-tests-in-golang-619653f90742
  80. Afinní transformace
    https://cs.wikibooks.org/wi­ki/Geometrie/Afinn%C3%AD_tran­sformace_sou%C5%99adnic
  81. package gg
    https://godoc.org/github.com/fo­gleman/gg
  82. Generate an animated GIF with Golang
    http://tech.nitoyon.com/en/blog/2016/01/07/­go-animated-gif-gen/
  83. Generate an image programmatically with Golang
    http://tech.nitoyon.com/en/blog/2015/12/31/­go-image-gen/
  84. The Go image package
    https://blog.golang.org/go-image-package
  85. Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
    https://github.com/llgcode/draw2d
  86. Draw a rectangle in Golang?
    https://stackoverflow.com/qu­estions/28992396/draw-a-rectangle-in-golang
  87. YAML
    https://yaml.org/
  88. edn
    https://github.com/edn-format/edn
  89. Smile
    https://github.com/FasterXML/smile-format-specification
  90. Protocol-Buffers
    https://developers.google.com/protocol-buffers/
  91. Marshalling (computer science)
    https://en.wikipedia.org/wi­ki/Marshalling_(computer_sci­ence)
  92. Unmarshalling
    https://en.wikipedia.org/wi­ki/Unmarshalling
  93. Introducing JSON
    http://json.org/
  94. Package json
    https://golang.org/pkg/encoding/json/
  95. The Go Blog: JSON and Go
    https://blog.golang.org/json-and-go
  96. Go by Example: JSON
    https://gobyexample.com/json
  97. Writing Web Applications
    https://golang.org/doc/articles/wiki/
  98. Golang Web Apps
    https://www.reinbach.com/blog/golang-webapps-1/
  99. Build web application with Golang
    https://legacy.gitbook.com/bo­ok/astaxie/build-web-application-with-golang/details
  100. Golang Templates – Golang Web Pages
    https://www.youtube.com/wat­ch?v=TkNIETmF-RU
  101. Simple Golang HTTPS/TLS Examples
    https://github.com/denji/golang-tls
  102. Playing with images in HTTP response in golang
    https://www.sanarias.com/blog/1214Pla­yingwithimagesinHTTPrespon­seingolang
  103. MIME Types List
    https://www.freeformatter.com/mime-types-list.html
  104. Go Mutex Tutorial
    https://tutorialedge.net/golang/go-mutex-tutorial/
  105. Creating A Simple Web Server With Golang
    https://tutorialedge.net/go­lang/creating-simple-web-server-with-golang/
  106. Building a Web Server in Go
    https://thenewstack.io/building-a-web-server-in-go/
  107. How big is the pipe buffer?
    https://unix.stackexchange­.com/questions/11946/how-big-is-the-pipe-buffer
  108. How to turn off buffering of stdout in C
    https://stackoverflow.com/qu­estions/7876660/how-to-turn-off-buffering-of-stdout-in-c
  109. setbuf(3) – Linux man page
    https://linux.die.net/man/3/setbuf
  110. setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
    https://linux.die.net/man/3/setvbuf
  111. Select waits on a group of channels
    https://yourbasic.org/golang/select-explained/
  112. Rob Pike: Simplicity is Complicated (video)
    http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893
  113. Algorithms to Go
    https://yourbasic.org/
  114. Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
    https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/
  115. Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů: vlastní filtry a lexery
    https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu-vlastni-filtry-a-lexery/
  116. Go Defer Simplified with Practical Visuals
    https://blog.learngoprogram­ming.com/golang-defer-simplified-77d3b2b817ff
  117. 5 More Gotchas of Defer in Go — Part II
    https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa
  118. The Go Blog: Defer, Panic, and Recover
    https://blog.golang.org/defer-panic-and-recover
  119. The defer keyword in Swift 2: try/finally done right
    https://www.hackingwithswift.com/new-syntax-swift-2-defer
  120. Swift Defer Statement
    https://andybargh.com/swift-defer-statement/
  121. Modulo operation (Wikipedia)
    https://en.wikipedia.org/wi­ki/Modulo_operation
  122. Node.js vs Golang: Battle of the Next-Gen Languages
    https://www.hostingadvice­.com/blog/nodejs-vs-golang/
  123. The Go Programming Language (home page)
    https://golang.org/
  124. GoDoc
    https://godoc.org/
  125. Go (programming language), Wikipedia
    https://en.wikipedia.org/wi­ki/Go_(programming_langua­ge)
  126. Go Books (kniha o jazyku Go)
    https://github.com/dariubs/GoBooks
  127. The Go Programming Language Specification
    https://golang.org/ref/spec
  128. Go: the Good, the Bad and the Ugly
    https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/
  129. Package builtin
    https://golang.org/pkg/builtin/
  130. Package fmt
    https://golang.org/pkg/fmt/
  131. The Little Go Book (další kniha)
    https://github.com/dariubs/GoBooks
  132. The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
    https://www.safaribookson­line.com/library/view/the-go-programming/9780134190570/e­book_split010.html
  133. Learning Go
    https://www.miek.nl/go/
  134. Go Bootcamp
    http://www.golangbootcamp.com/
  135. Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
    http://www.informit.com/sto­re/programming-in-go-creating-applications-for-the-21st-9780321774637
  136. Introducing Go (Build Reliable, Scalable Programs)
    http://shop.oreilly.com/pro­duct/0636920046516.do
  137. Learning Go Programming
    https://www.packtpub.com/application-development/learning-go-programming
  138. The Go Blog
    https://blog.golang.org/
  139. Getting to Go: The Journey of Go's Garbage Collector
    https://blog.golang.org/ismmkeynote
  140. Go (programovací jazyk, Wikipedia)
    https://cs.wikipedia.org/wi­ki/Go_(programovac%C3%AD_ja­zyk)
  141. Rychle, rychleji až úplně nejrychleji s jazykem Go
    https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/
  142. Installing Go on the Raspberry Pi
    https://dave.cheney.net/2012/09/25/in­stalling-go-on-the-raspberry-pi
  143. How the Go runtime implements maps efficiently (without generics)
    https://dave.cheney.net/2018/05/29/how-the-go-runtime-implements-maps-efficiently-without-generics
  144. Niečo málo o Go – Golang (slovensky)
    http://golangsk.logdown.com/
  145. How Many Go Developers Are There?
    https://research.swtch.com/gop­hercount
  146. Most Popular Technologies (Stack Overflow Survery 2018)
    https://insights.stackover­flow.com/survey/2018/#most-popular-technologies
  147. Most Popular Technologies (Stack Overflow Survery 2017)
    https://insights.stackover­flow.com/survey/2017#techno­logy
  148. JavaScript vs. Golang for IoT: Is Gopher Winning?
    https://www.iotforall.com/javascript-vs-golang-iot/
  149. The Go Programming Language: Release History
    https://golang.org/doc/de­vel/release.html
  150. Go 1.11 Release Notes
    https://golang.org/doc/go1.11
  151. Go 1.10 Release Notes
    https://golang.org/doc/go1.10
  152. Go 1.9 Release Notes (tato verze je stále používána)
    https://golang.org/doc/go1.9
  153. Go 1.8 Release Notes (i tato verze je stále používána)
    https://golang.org/doc/go1.8
  154. Go on Fedora
    https://developer.fedorapro­ject.org/tech/languages/go/go-installation.html
  155. Writing Go programs
    https://developer.fedorapro­ject.org/tech/languages/go/go-programs.html
  156. The GOPATH environment variable
    https://tip.golang.org/doc/co­de.html#GOPATH
  157. Command gofmt
    https://tip.golang.org/cmd/gofmt/
  158. The Go Blog: go fmt your code
    https://blog.golang.org/go-fmt-your-code
  159. C? Go? Cgo!
    https://blog.golang.org/c-go-cgo
  160. Spaces vs. Tabs: A 20-Year Debate Reignited by Google’s Golang
    https://thenewstack.io/spaces-vs-tabs-a-20-year-debate-and-now-this-what-the-hell-is-wrong-with-go/
  161. 400,000 GitHub repositories, 1 billion files, 14 terabytes of code: Spaces or Tabs?
    https://medium.com/@hoffa/400–000-github-repositories-1-billion-files-14-terabytes-of-code-spaces-or-tabs-7cfe0b5dd7fd
  162. Gofmt No Longer Allows Spaces. Tabs Only
    https://news.ycombinator.com/i­tem?id=7914523
  163. Why does Go „go fmt“ uses tabs instead of whitespaces?
    https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces
  164. Interactive: The Top Programming Languages 2018
    https://spectrum.ieee.org/sta­tic/interactive-the-top-programming-languages-2018
  165. Go vs. Python
    https://www.peterbe.com/plog/govspy
  166. PackageManagementTools
    https://github.com/golang/go/wi­ki/PackageManagementTools
  167. A Tour of Go: Type inference
    https://tour.golang.org/basics/14
  168. Go Slices: usage and internals
    https://blog.golang.org/go-slices-usage-and-internals
  169. Go by Example: Slices
    https://gobyexample.com/slices
  170. What is the point of slice type in Go?
    https://stackoverflow.com/qu­estions/2098874/what-is-the-point-of-slice-type-in-go
  171. The curious case of Golang array and slices
    https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335
  172. Introduction to Slices in Golang
    https://www.callicoder.com/golang-slices/
  173. Golang: Understanding ‚null‘ and nil
    https://newfivefour.com/golang-null-nil.html
  174. What does nil mean in golang?
    https://stackoverflow.com/qu­estions/35983118/what-does-nil-mean-in-golang
  175. nils In Go
    https://go101.org/article/nil.html
  176. Go slices are not dynamic arrays
    https://appliedgo.net/slices/
  177. Go-is-no-good (nelze brát doslova)
    https://github.com/ksimka/go-is-not-good
  178. Rust vs. Go
    https://news.ycombinator.com/i­tem?id=13430108
  179. Seriál Programovací jazyk Rust
    https://www.root.cz/seria­ly/programovaci-jazyk-rust/
  180. Modern garbage collection: A look at the Go GC strategy
    https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e
  181. Go GC: Prioritizing low latency and simplicity
    https://blog.golang.org/go15gc
  182. Is Golang a good language for embedded systems?
    https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems
  183. Running GoLang on an STM32 MCU. A quick tutorial.
    https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial
  184. Go, Robot, Go! Golang Powered Robotics
    https://gobot.io/
  185. Emgo: Bare metal Go (language for programming embedded systems)
    https://github.com/ziutek/emgo
  186. UTF-8 history
    https://www.cl.cam.ac.uk/~mgk25/uc­s/utf-8-history.txt
  187. Less is exponentially more
    https://commandcenter.blog­spot.com/2012/06/less-is-exponentially-more.html
  188. Should I Rust, or Should I Go
    https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9
  189. Setting up and using gccgo
    https://golang.org/doc/install/gccgo
  190. Elastic Tabstops
    http://nickgravgaard.com/elastic-tabstops/
  191. Strings, bytes, runes and characters in Go
    https://blog.golang.org/strings
  192. Datový typ
    https://cs.wikipedia.org/wi­ki/Datov%C3%BD_typ
  193. Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
    https://www.root.cz/clanky/pro­gramovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09
  194. Seriál o programovacím jazyku Rust: Vytvoření „řezu“ z pole
    https://www.root.cz/clanky/prace-s-poli-v-programovacim-jazyku-rust/#k06
  195. Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
    https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05
  196. Printf Format Strings
    https://www.cprogramming.com/tu­torial/printf-format-strings.html
  197. Java: String.format
    https://docs.oracle.com/ja­vase/8/docs/api/java/lang/Strin­g.html#format-java.lang.String-java.lang.Object…-
  198. Java: format string syntax
    https://docs.oracle.com/ja­vase/8/docs/api/java/util/For­matter.html#syntax
  199. Selectors
    https://golang.org/ref/spec#Selectors
  200. Calling Go code from Python code
    http://savorywatt.com/2015/09/18/ca­lling-go-code-from-python-code/
  201. Go Data Structures: Interfaces
    https://research.swtch.com/interfaces
  202. How to use interfaces in Go
    http://jordanorelli.com/pos­t/32665860244/how-to-use-interfaces-in-go
  203. Interfaces in Go (part I)
    https://medium.com/golangspec/in­terfaces-in-go-part-i-4ae53a97479c
  204. Part 21: Goroutines
    https://golangbot.com/goroutines/
  205. Part 22: Channels
    https://golangbot.com/channels/
  206. [Go] Lightweight eventbus with async compatibility for Go
    https://github.com/asaske­vich/EventBus
  207. What about Trait support in Golang?
    https://www.reddit.com/r/go­lang/comments/8mfykl/what_a­bout_trait_support_in_golan­g/
  208. Don't Get Bitten by Pointer vs Non-Pointer Method Receivers in Golang
    https://nathanleclaire.com/blog/2014/08/09/dont-get-bitten-by-pointer-vs-non-pointer-method-receivers-in-golang/
  209. Control Flow
    https://en.wikipedia.org/wi­ki/Control_flow
  210. Structured programming
    https://en.wikipedia.org/wi­ki/Structured_programming
  211. Control Structures
    https://www.golang-book.com/books/intro/5
  212. Control structures – Go if else statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-if-else-statement.html
  213. Control structures – Go switch case statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-switch-case.html
  214. Control structures – Go for loop, break, continue, range
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-for-loop-break.html
  215. Single Function Exit Point
    http://wiki.c2.com/?Single­FunctionExitPoint
  216. Entry point
    https://en.wikipedia.org/wi­ki/Entry_point
  217. Why does Go have a GOTO statement?!
    https://www.reddit.com/r/go­lang/comments/kag5q/why_do­es_go_have_a_goto_statemen­t/
  218. Effective Go
    https://golang.org/doc/ef­fective_go.html
  219. GoClipse: an Eclipse IDE for the Go programming language
    http://goclipse.github.io/
  220. GoClipse Installation
    https://github.com/GoClip­se/goclipse/blob/latest/do­cumentation/Installation.md#in­stallation