Slide 1

Slide 1 text

За пару мгновений до main() GopherCon Russia 2021 Oleg Kovalov gogoapps.io olegk.dev

Slide 2

Slide 2 text

- Опенсорс-зависимый гофер Олег 2

Slide 3

Slide 3 text

- Опенсорс-зависимый гофер - Фанат линтеров (соавтор go-critic) Олег 3

Slide 4

Slide 4 text

- Опенсорс-зависимый гофер - Фанат линтеров (соавтор go-critic) - Один из ведущих подкаста Generic Talks Олег 4

Slide 5

Slide 5 text

Олег 5 - Опенсорс-зависимый гофер - Фанат линтеров (соавтор go-critic) - Один из ведущих подкаста Generic Talks - Веселый и полезный @oleg_log

Slide 6

Slide 6 text

Олег 6 - Опенсорс-зависимый гофер - Фанат линтеров (соавтор go-critic) - Один из ведущих подкаста Generic Talks - Веселый и полезный @oleg_log - А еще Twitter/Telegram @go_perf

Slide 7

Slide 7 text

- Опенсорс-зависимый гофер - Фанат линтеров (соавтор go-critic) - Один из ведущих подкаста Generic Talks - Веселый и полезный @oleg_log - А еще Twitter/Telegram @go_perf - ... olegk.dev Олег 7

Slide 8

Slide 8 text

План доклада 8

Slide 9

Slide 9 text

- Зачем??? План доклада 9

Slide 10

Slide 10 text

- Зачем??? - У нас есть *.exe - Попробуем получить код - Пробежимся по коду План доклада 10

Slide 11

Slide 11 text

- Зачем??? - У нас есть *.exe - Попробуем получить код - Пробежимся по коду - Пробежимся по коду - Пробежимся по коду - Пробежимся по коду - Пробежимся по коду - Пробежимся по коду - Пробежимся по коду План доклада 11

Slide 12

Slide 12 text

- Зачем??? - У нас есть *.exe - Попробуем получить код - Пробежимся по коду - Пробежимся по коду - Пробежимся по коду - Пробежимся по коду - Пробежимся по коду - Пробежимся по коду - Пробежимся по коду - Интересное из stdlib - Exit Go 1.16.3, OS Darwin (macOS Big Sur), YMMV План доклада 12

Slide 13

Slide 13 text

Зачем??? 13

Slide 14

Slide 14 text

Зачем??? 14

Slide 15

Slide 15 text

Зачем??? 15

Slide 16

Slide 16 text

Зачем??? 16

Slide 17

Slide 17 text

Зачем??? 17

Slide 18

Slide 18 text

Правда из жизни 18

Slide 19

Slide 19 text

Правда из жизни 19 Сгенерировано вот этим https://github.com/knz/go-binsize-viz

Slide 20

Slide 20 text

Правда из жизни рантайма 20 Сгенерировано вот этим https://github.com/knz/go-binsize-viz pclntab rodata types

Slide 21

Slide 21 text

Правда из жизни рантайма 21 Хорошая тулза от Brad Fitzpatrick https://github.com/bradfitz/shotizam (нооо пока не работает с Go 1.16) pclntab rodata types

Slide 22

Slide 22 text

Да что это такое этот ваш pclntab 22

Slide 23

Slide 23 text

Это все pclntab, расходимся 23

Slide 24

Slide 24 text

Хотя pclntab не во всём виноват 24 https://lobste.rs/s/gvtstv/my_go_executable_files_are_still_getting#c_dbfije

Slide 25

Slide 25 text

Так о чём это я 25 -

Slide 26

Slide 26 text

- Проблема вроде бы есть Так о чём это я 26

Slide 27

Slide 27 text

- Проблема вроде бы есть - Но действительно проблематит не всех Так о чём это я 27

Slide 28

Slide 28 text

- Проблема вроде бы есть - Но действительно проблематит не всех - размер Docker image чаще выступает проблемой - если вы на Ubuntu/Debian строитесь Так о чём это я 28

Slide 29

Slide 29 text

- Проблема вроде бы есть - Но действительно проблематит не всех - размер Docker image чаще выступает проблемой - если вы на Ubuntu/Debian строитесь - + можно через UPX пожать бинарь Так о чём это я 29

Slide 30

Slide 30 text

Так о чём это я 30 - Проблема вроде бы есть - Но действительно проблематит не всех - размер Docker image чаще выступает проблемой - если вы на Ubuntu/Debian строитесь - + можно через UPX пожать бинарь - Но что там минимально-нужного?

Slide 31

Slide 31 text

Так о чём это я 31 - Проблема вроде бы есть - Но действительно проблематит не всех - размер Docker image чаще выступает проблемой - если вы на Ubuntu/Debian строитесь - + можно через UPX пожать бинарь - Но что там минимально-нужного? - Про это и поговорим

Slide 32

Slide 32 text

Куда уж проще? 32

Slide 33

Slide 33 text

❯ cat main.go package main func main() { // ʕ◔ϖ◔ʔ } Куда уж проще? 33

Slide 34

Slide 34 text

❯ cat main.go package main func main() { // ʕ◔ϖ◔ʔ } ❯ go build -o main.exe main.go Куда уж проще? 34

Slide 35

Slide 35 text

❯ cat main.go package main func main() { // ʕ◔ϖ◔ʔ } ❯ go build -o main.exe main.go ❯ du -sh main.exec 1.1M main.exec Куда уж проще? 35

Slide 36

Slide 36 text

❯ cat main.go package main func main() { // ʕ◔ϖ◔ʔ } ❯ go build -o main.exe main.go ❯ du -sh main.exec 1.1M main.exec ❯ 👀 Куда уж проще? 36

Slide 37

Slide 37 text

❯ go build -o main.exe main.go ❯ cat main.exe Что в нашем *.exe? /1 37

Slide 38

Slide 38 text

cat 38

Slide 39

Slide 39 text

❯ go build -o main.exe main.go ❯ go list -json . > main.json Что в нашем *.exe? /2 39

Slide 40

Slide 40 text

go list -json 40 { "Dir": "/Users/olegkovalov/go/src/github.com/cristaloleg/funf", "ImportPath": "github.com/cristaloleg/funf", "Name": "main", "Module": {...}, "GoFiles": [ "main.go" ], "Deps": [ "internal/bytealg", "internal/cpu", "runtime", "runtime/internal/atomic", "runtime/internal/math", "runtime/internal/sys", "unsafe" ] }

Slide 41

Slide 41 text

❯ go build -o main.exe main.go ❯ go tool objdump -S main.exe > main.dump Что в нашем *.exe? /3 41

Slide 42

Slide 42 text

- Дизассемблирует исполняемые файлы go tool objdump 42

Slide 43

Slide 43 text

- Дизассемблирует исполняемые файлы var printCode = flag.Bool("S", false, "print Go code alongside assembly") go tool objdump 43

Slide 44

Slide 44 text

- Дизассемблирует исполняемые файлы var printCode = flag.Bool("S", false, "print Go code alongside assembly") - Еще есть флаг symregexp flag.String("s", "", "only dump symbols matching this regexp") ❯ go tool objdump -S -s runtime.close main.exec go tool objdump 44

Slide 45

Slide 45 text

Пример из main.dump 45

Slide 46

Slide 46 text

❯ go build -o main.exe main.go ❯ go tool objdump -S main.exe > main.dump ❯ du -sh main.dump 5.1M main.dump Итог objdump 46

Slide 47

Slide 47 text

❯ go build -o main.exe main.go ❯ go tool objdump -S main.exe > main.dump ❯ du -sh main.dump 5.1M main.dump ❯ rg "TEXT runtime." -c main.dump 939 Итог objdump 47

Slide 48

Slide 48 text

❯ go build -o main.exe main.go ❯ go tool objdump -S main.exe > main.dump ❯ du -sh main.dump 5.1M main.dump ❯ rg "TEXT runtime." -c main.dump 939 ❯ 🔥🔥🔥 Итог objdump 48

Slide 49

Slide 49 text

Пойдем в main.dump 49

Slide 50

Slide 50 text

- Обычный текстовый файл Пойдем в main.dump 50

Slide 51

Slide 51 text

- Обычный текстовый файл - Со всеми возможными объявлениями Пойдем в main.dump 51

Slide 52

Slide 52 text

Пойдем в main.dump 52 - Обычный текстовый файл - Со всеми возможными объявлениями - Полезно, если делаете низкоуровневые оптимизации - Или какой-то там доклад

Slide 53

Slide 53 text

Так и запишем /0 53 - ... - main()

Slide 54

Slide 54 text

...124k lines later TEXT main.main(SB) /Users/olegkovalov/go/src/cristaloleg/funf/main.go } 0x105e180 c3 RET Вот и любимый main() 54

Slide 55

Slide 55 text

...124k lines later TEXT main.main(SB) /Users/olegkovalov/go/src/cristaloleg/funf/main.go } 0x105e180 c3 RET Эм...main.main ? 55

Slide 56

Slide 56 text

Настоящий main 56 ...60k lines 0x102fc2b 488b6c2448 MOVQ 0x48(SP), BP 0x102fc30 4883c450 ADDQ $0x50, SP 0x102fc34 c3 RET func main() { 0x102fc35 e846980200 CALL runtime.morestack_noctxt(SB) 0x102fc3a e941fcffff JMP runtime.main(SB) 0x102fc3f cc INT $0x3 ...60k lines

Slide 57

Slide 57 text

Настоящий main...уже ближе 57 ...60k lines 0x102fc2b 488b6c2448 MOVQ 0x48(SP), BP 0x102fc30 4883c450 ADDQ $0x50, SP 0x102fc34 c3 RET func main() { 0x102fc35 e846980200 CALL runtime.morestack_noctxt(SB) 0x102fc3a e941fcffff JMP runtime.main(SB) 0x102fc3f cc INT $0x3 ...60k lines

Slide 58

Slide 58 text

runtime.main 58 TEXT runtime.main(SB) /usr/local/Cellar/go/1.16/libexec/src/runtime/proc.go func main() { 0x102f880 65488b0c2530000000 MOVQ GS:0x30, CX 0x102f889 483b6110 CMPQ 0x10(CX), SP 0x102f88d 0f86a2030000 JBE 0x102fc35 0x102f893 4883ec50 SUBQ $0x50, SP 0x102f897 48896c2448 MOVQ BP, 0x48(SP) 0x102f89c 488d6c2448 LEAQ 0x48(SP), BP 0x102f8a1 0f57c0 XORPS X0, X0 0x102f8a4 0f11442438 MOVUPS X0, 0x38(SP) 0x102f8a9 c644242700 MOVB $0x0, 0x27(SP) g := getg() 0x102f8ae 65488b042530000000 MOVQ GS:0x30, AX 0x102f8b7 4889442430 MOVQ AX, 0x30(SP) g.m.g0.racectx = 0

Slide 59

Slide 59 text

Что к чему в runtime/proc.go 59 //go:linkname main_main main.main func main_main() // ... // The main goroutine. func main() { g := getg() // ... fn := main_main // make an indirect call, // as the linker doesn't know the address // of the main package when laying down the runtime fn() // ...

Slide 60

Slide 60 text

Так и запишем /1 60 - ... - runtime.main() - main_main ~ main.main // магия линковщика - main.main // это мы!

Slide 61

Slide 61 text

Вот только это не всё 61

Slide 62

Slide 62 text

- Есть странные runtime/asm_.s файлы Вот только это не всё 62

Slide 63

Slide 63 text

- Есть странные runtime/asm_.s файлы - где arch - некая архитектура (amd64, arm64, mips, …) - *.s - Assembler Вот только это не всё 63

Slide 64

Slide 64 text

- Есть странные runtime/asm_.s файлы - где arch - некая архитектура (amd64, arm64, mips, …) - *.s - Assembler - 👀 Вот только это не всё 64

Slide 65

Slide 65 text

- Есть странные runtime/asm_.s файлы - где arch - некая архитектура (amd64, arm64, mips, …) - *.s - Assembler - 👀 - Значит пойдём разбираться Вот только это не всё 65

Slide 66

Slide 66 text

- Есть странные runtime/asm_.s файлы - где arch - некая архитектура (amd64, arm64, mips, …) - *.s - Assembler - 👀 - Значит пойдём разбираться - Посмотрим на asm_amd64.s - всего лишь 2к строк Вот только это не всё 66

Slide 67

Slide 67 text

- Есть странные runtime/asm_.s файлы - где arch - некая архитектура (amd64, arm64, mips, …) - *.s - Assembler - 👀 - Значит пойдём разбираться - Посмотрим на asm_amd64.s - всего лишь 2к строк - в остальных + - 1к - arm64.s тоже тяжелый Вот только это не всё 67

Slide 68

Slide 68 text

- FP: Frame pointer: arguments and locals. - PC: Program counter: jumps and branches. - SB: Static base pointer: global symbols. - SP: Stack pointer: top of stack. Словарик читателя Go assembler 68

Slide 69

Slide 69 text

- FP: Frame pointer: arguments and locals. - PC: Program counter: jumps and branches. - SB: Static base pointer: global symbols. - SP: Stack pointer: top of stack. - JMP: jump - LEA: load effective address - MOV dst, src: move src - CALL: invoke function - DATA(+GLOBL): data symbols (global) Словарик читателя Go assembler 69

Slide 70

Slide 70 text

// _rt0_amd64 is common startup code for most amd64 systems when using // internal linking. This is the entry point for the program from the // kernel for an ordinary -buildmode=exe program. The stack holds the // number of arguments and the C-style argv. TEXT _rt0_amd64(SB),NOSPLIT,$-8 MOVQ 0(SP), DI // argc LEAQ 8(SP), SI // argv JMP runtime·rt0_go(SB) Ах вот оно что! 70

Slide 71

Slide 71 text

// Defined as ABIInternal since it does not use the stack-based Go ABI (and // in addition there are no calls to this entry point from Go code). TEXT runtime·rt0_go(SB),NOSPLIT,$0 // copy arguments forward on an even stack MOVQ DI, AX // argc MOVQ SI, BX // argv // ... // create istack out of the given (operating system) stack. MOVQ $runtime·g0(SB), DI // ... // find out information about the processor we're on MOVL $0, AX CPUID // ... Пойдем дальше 71

Slide 72

Slide 72 text

Пойдем дальше 72 TEXT runtime·rt0_go(SB),NOSPLIT,$0 // Figure out how to serialize RDTSC. // On Intel processors LFENCE is enough. AMD requires MFENCE. // Don't know about the rest, so let's do MFENCE. // ... ok: // set the per-goroutine and per-mach "registers" get_tls(BX) LEAQ runtime·g0(SB), CX MOVQ CX, g(BX) LEAQ runtime·m0(SB), AX // save m->g0 = g0 MOVQ CX, m_g0(AX) // save m0 to g0->m MOVQ AX, g_m(CX)

Slide 73

Slide 73 text

Пойдем дальше 73 TEXT runtime·rt0_go(SB),NOSPLIT,$0 // ... CALL runtime·check(SB) MOVL 16(SP), AX // copy argc MOVL AX, 0(SP) MOVQ 24(SP), AX // copy argv MOVQ AX, 8(SP) CALL runtime·args(SB) CALL runtime·osinit(SB) CALL runtime·schedinit(SB)

Slide 74

Slide 74 text

- Да, содержат платформозависимые вещи runtime/os_.go 74

Slide 75

Slide 75 text

- Да, содержат платформозависимые вещи - Обработка сигналов - Иногда таймеры runtime/os_.go 75

Slide 76

Slide 76 text

- Да, содержат платформозависимые вещи - Обработка сигналов - Иногда таймеры - Рандом - Размеры тредов и прочих системных вещей - Энвы и аргументы программы - речь о environment variables и argc & argv runtime/os_.go 76

Slide 77

Slide 77 text

- Да, содержат платформозависимые вещи - Обработка сигналов - Иногда таймеры - Рандом - Размеры тредов и прочих системных вещей - Энвы и аргументы программы - речь о environment variables и argc & argv - Проверка адекватности машины - o! runtime/os_.go 77

Slide 78

Slide 78 text

- В файле runtime1.go есть интересная функция - check() Проверка адекватности машины 78

Slide 79

Slide 79 text

- В файле runtime1.go есть интересная функция - check() - проверяем размеры примитивных типов - d uint16 - if unsafe.Sizeof(d) != 2 { throw("bad d") } Проверка адекватности машины 79

Slide 80

Slide 80 text

- В файле runtime1.go есть интересная функция - check() - проверяем размеры примитивных типов - d uint16 - if unsafe.Sizeof(d) != 2 { throw("bad d") } - check() вызывает testAtomic64() Проверка адекватности машины 80

Slide 81

Slide 81 text

- В файле runtime1.go есть интересная функция - check() - проверяем размеры примитивных типов - d uint16 - if unsafe.Sizeof(d) != 2 { throw("bad d") } - check() вызывает testAtomic64() - Как можно догадаться, testAtomic64 про атомики - test_z64 = 42, test_x64 = 0 - if atomic.Cas64(&test_z64, test_x64, 1) { throw("cas64 failed") } Проверка адекватности машины 81

Slide 82

Slide 82 text

Пойдем дальше 82 TEXT runtime·rt0_go(SB),NOSPLIT,$0 // ... CALL runtime·check(SB) MOVL 16(SP), AX // copy argc MOVL AX, 0(SP) MOVQ 24(SP), AX // copy argv MOVQ AX, 8(SP) CALL runtime·args(SB) CALL runtime·osinit(SB) CALL runtime·schedinit(SB) // при помощи магии и директив линкера это и будет os.Args

Slide 83

Slide 83 text

/Users/olegkovalov/go/src/golang/go/src/runtime/runtime1.go func args(c int32, v **byte) { argc = c argv = v sysargs(c, v) } Пойдем дальше 83

Slide 84

Slide 84 text

Пойдем дальше 84 /Users/olegkovalov/go/src/golang/go/src/runtime/runtime1.go func args(c int32, v **byte) { argc = c argv = v sysargs(c, v) } // os_darwin.go func sysargs(argc int32, argv **byte) { // skip over argv, envv and the first string will be the path n := argc + 1 for argv_index(argv, n) != nil { n++ } executablePath = gostringnocopy(argv_index(argv, n+1)) // ... }

Slide 85

Slide 85 text

Пойдем дальше 85 TEXT runtime·rt0_go(SB),NOSPLIT,$0 // ... CALL runtime·check(SB) MOVL 16(SP), AX // copy argc MOVL AX, 0(SP) MOVQ 24(SP), AX // copy argv MOVQ AX, 8(SP) CALL runtime·args(SB) CALL runtime·osinit(SB) CALL runtime·schedinit(SB)

Slide 86

Slide 86 text

Пойдем дальше 86 // os_darwin.go // BSD interface for threading. func osinit() { // pthread_create delayed until end of goenvs so that we // can look at the environment first. ncpu = getncpu() physPageSize = getPageSize() }

Slide 87

Slide 87 text

Пойдем дальше 87 TEXT runtime·rt0_go(SB),NOSPLIT,$0 // ... CALL runtime·check(SB) MOVL 16(SP), AX // copy argc MOVL AX, 0(SP) MOVQ 24(SP), AX // copy argv MOVQ AX, 8(SP) CALL runtime·args(SB) CALL runtime·osinit(SB) CALL runtime·schedinit(SB)

Slide 88

Slide 88 text

Так и запишем /2 88 - runtime·_rt0_amd64 // или какая там у вас архитектура - runtime·rt0_go - создадим Вселенную - check - args - osinit - schedinit - runtime.main() - main_main ~ main.main // магия линковщика - main.main // это мы!

Slide 89

Slide 89 text

Пойдем дальше 89 // The bootstrap sequence is: // // call osinit // call schedinit // make & queue new G // call runtime·mstart // // The new G calls runtime·main. func schedinit() { // ...

Slide 90

Slide 90 text

- G - goroutine - M - worker thread, or machine. - P - processor, a resource that is required to execute Go code. - getg() - returns the pointer to the current g - _g_ := getg() - p := getg().m.p.ptr() - tls - thread-local storage Остальное это if-ы, атомики и непонятные алгоритмы. Вроде изян. Словарик читателя runtime 90

Slide 91

Slide 91 text

func schedinit() { // ... _g_ := getg() sched.maxmcount = 10000 // maximum number of m's allowed (or die) // The world starts stopped. worldStopped() moduledataverify() // pclntab is correct stackinit() // mallocinit() fastrandinit() // must run before mcommoninit mcommoninit(_g_.m, -1) schedinit /1 91

Slide 92

Slide 92 text

func schedinit() { // ... _g_ := getg() sched.maxmcount = 10000 // maximum number of m's allowed (or die) // The world starts stopped. worldStopped() moduledataverify() // pclntab is correct stackinit() // mallocinit() fastrandinit() // must run before mcommoninit mcommoninit(_g_.m, -1) cpuinit() // must run before alginit alginit() // maps must not be used before this call modulesinit() // provides activeModules typelinksinit() // uses maps, activeModules itabsinit() // uses activeModules schedinit /1 92

Slide 93

Slide 93 text

func schedinit() { // ... goargs() goenvs() parsedebugvars() gcinit() lock(&sched.lock) // ... if procresize(procs) != nil { throw("unknown runnable goroutine during bootstrap") } unlock(&sched.lock) // World is effectively started now, as P's can run. worldStarted() // ... } schedinit /2 93

Slide 94

Slide 94 text

TEXT runtime·rt0_go(SB),NOSPLIT,$0 // ... // create a new goroutine to start program MOVQ $runtime·mainPC(SB), AX // entry PUSHQ AX PUSHQ $0 // arg size CALL runtime·newproc(SB) POPQ AX POPQ AX ... // mainPC is a function value for runtime.main, to be passed to newproc. // The reference to runtime.main is made via ABIInternal, since the // actual function (not the ABI0 wrapper) is needed by newproc. DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) GLOBL runtime·mainPC(SB),RODATA,$8 Еще чуть-чуть Ассемблера 94

Slide 95

Slide 95 text

Последнее из Ассемблера, честно 95 TEXT runtime·rt0_go(SB),NOSPLIT,$0 // ... // create a new goroutine to start program MOVQ $runtime·mainPC(SB), AX // entry PUSHQ AX PUSHQ $0 // arg size CALL runtime·newproc(SB) POPQ AX POPQ AX // start this M CALL runtime·mstart(SB) CALL runtime·abort(SB)// mstart should never return RET TEXT runtime·mstart(SB),NOSPLIT|TOPFRAME,$0 CALL runtime·mstart0(SB) RET // not reached

Slide 96

Slide 96 text

newproc 96 // Create a new g running fn with siz bytes of arguments. // Put it on the queue of g's waiting to run. // The compiler turns a go statement into a call to this. // ... func newproc(siz int32, fn *funcval) { argp := add(unsafe.Pointer(&fn), sys.PtrSize) gp := getg() pc := getcallerpc() systemstack(func() { newg := newproc1(fn, argp, siz, gp, pc) _p_ := getg().m.p.ptr() runqput(_p_, newg, true) if mainStarted { wakep() } }) }

Slide 97

Slide 97 text

// Create a new g running fn with siz bytes of arguments. // Put it on the queue of g's waiting to run. // The compiler turns a go statement into a call to this. // ... func newproc(siz int32, fn *funcval) { argp := add(unsafe.Pointer(&fn), sys.PtrSize) gp := getg() pc := getcallerpc() systemstack(func() { newg := newproc1(fn, argp, siz, gp, pc) _p_ := getg().m.p.ptr() runqput(_p_, newg, true) if mainStarted { wakep() } }) } newproc 97

Slide 98

Slide 98 text

newproc 98 // Create a new g running fn with siz bytes of arguments. // Put it on the queue of g's waiting to run. // The compiler turns a go statement into a call to this. // ... func newproc(siz int32, fn *funcval) { argp := add(unsafe.Pointer(&fn), sys.PtrSize) gp := getg() pc := getcallerpc() systemstack(func() { newg := newproc1(fn, argp, siz, gp, pc) _p_ := getg().m.p.ptr() runqput(_p_, newg, true) if mainStarted { wakep() } }) }

Slide 99

Slide 99 text

systemstack 99 // systemstack runs fn on a system stack. // If systemstack is called from the per-OS-thread (g0) stack, or // if systemstack is called from the signal handling (gsignal) stack, // systemstack calls fn directly and returns. // // Otherwise, systemstack is being called from the limited stack // of an ordinary goroutine. In this case, systemstack switches // to the per-OS-thread stack, calls fn, and switches back. // //go:noescape func systemstack(fn func())

Slide 100

Slide 100 text

Наконец-то scheduler // mstart is the entry-point for new Ms. func mstart() // mstart0 is the Go entry-point for new Ms. func mstart0() { _g_ := getg() // ... mstart1() } func mstart1() { _g_ := getg() // ... schedule() } 100

Slide 101

Slide 101 text

schedule 101 // One round of scheduler: find a runnable goroutine and execute it. // Never returns. func schedule() { _g_ := getg() top: pp := _g_.m.p.ptr() pp.preempt = false var gp *g // get some gp execute(gp, inheritTime) }

Slide 102

Slide 102 text

Так и запишем /3 102 - runtime·_rt0_amd64 // или что там у вас по архитектуре и ОС - runtime·rt0_go - check - args - osinit - schedinit - ... - newproc создаст горутину для runtime.main - mstart для выполнения горутины выше - mstart0 -> mstart1 - schedule - runtime.main() - main_main ~ main.main // магия линковщика - main.main // это мы!

Slide 103

Slide 103 text

Так и запишем /3.1 (WebAssembly) 103

Slide 104

Slide 104 text

- Все озвученное ранее это 250 линий Оке, а почему там 2к линий? 104

Slide 105

Slide 105 text

- Все озвученное ранее это 250 линий - Остальное это runtime-овые вещи (кэп) Оке, а почему там 2к линий? 105

Slide 106

Slide 106 text

- Все озвученное ранее это 250 линий - Остальное это runtime-овые вещи (кэп) - управлением стеком - выполнение defer - различные паники Оке, а почему там 2к линий? 106

Slide 107

Slide 107 text

- Все озвученное ранее это 250 линий - Остальное это runtime-овые вещи (кэп) - управлением стеком - выполнение defer - различные паники - помощь для дебага - любимый cgo - gc barriers Оке, а почему там 2к линий? 107

Slide 108

Slide 108 text

- Все озвученное ранее это 250 линий - Остальное это runtime-овые вещи (кэп) - управлением стеком - выполнение defer - различные паники - помощь для дебага - любимый cgo - gc barriers - таймеры - сигналы - хеши Оке, а почему там 2к линий? 108

Slide 109

Slide 109 text

Пойдем дальше // func memhash(p unsafe.Pointer, h, s uintptr) uintptr // hash function using AES hardware instructions TEXT runtime·memhash(SB),NOSPLIT,$0-32 CMPB runtime·useAeshash(SB), $0 JEQ noaes MOVQ p+0(FP), AX // ptr to data MOVQ s+16(FP), CX // size LEAQ ret+24(FP), DX JMP aeshashbody<>(SB) noaes: JMP runtime·memhashFallback(SB) /Users/olegkovalov/go/src/golang/go/src/runtime/hash64.go func memhashFallback(p unsafe.Pointer, seed, s uintptr) uintptr { 109

Slide 110

Slide 110 text

- add GOAMD64 architecture variants #25489 - https://github.com/golang/go/issues/25489 - Сейчас можно найти похожие строки - x86HasSSE41 = cpu.X86.HasSSE41 - на самом деле их сильно больше - Из-за чего мы теряем такты процессора - каждая наносекунда на счету :( GOAMD64 proposal 110

Slide 111

Slide 111 text

Что там в init()-ах?? 111

Slide 112

Slide 112 text

- В runtime init-ов не много - Большинство из них это просто проверки машины - какие размеры типов - какие флаги у процессора - Но есть 1 интересный в runtime (потом обсудим некоторые пакеты из stdlib) Так что там в init()-ах?? 112

Slide 113

Slide 113 text

/Users/olegkovalov/go/src/golang/go/src/runtime/proc.go // start forcegc helper goroutine func init() { go forcegchelper() } Популярная претензия к GC 113

Slide 114

Slide 114 text

/Users/olegkovalov/go/src/golang/go/src/runtime/proc.go // start forcegc helper goroutine func init() { go forcegchelper() } // 5k lines later... Популярная претензия к GC 114

Slide 115

Slide 115 text

Популярная претензия к GC 115 /Users/olegkovalov/go/src/golang/go/src/runtime/proc.go // start forcegc helper goroutine func init() { go forcegchelper() } // 5k lines later... // forcegcperiod is the maximum time in nanoseconds between garbage // collections. If we go this long without a garbage collection, one // is forced to run. // // This is a variable for testing purposes. It normally doesn't change. var forcegcperiod int64 = 2 * 60 * 1e9

Slide 116

Slide 116 text

func init() { http.HandleFunc("/debug/vars", expvarHandler) Publish("cmdline", Func(cmdline)) Publish("memstats", Func(memstats)) } Так что там в expvar 116

Slide 117

Slide 117 text

func init() { crypto.RegisterHash(crypto.MD5, New) } Так что там в crypto/* 117

Slide 118

Slide 118 text

func init() { crypto.RegisterHash(crypto.MD5, New) } // Именно поэтому в импортах мы делаем: import ( _ "crypto/sha256" // to register a sha256 _ "crypto/sha512" // to register a sha384/512 ) Так что там в crypto/* 118

Slide 119

Slide 119 text

func init() { crypto.RegisterHash(crypto.MD5, New) } // Именно поэтому в импортах мы делаем: import ( _ "crypto/sha256" // to register a sha256 _ "crypto/sha512" // to register a sha384/512 ) // а НЕ это: import ( _ "crypto/sha256" //nolint "crypto/sha512" ) var _ = sha512. Так что там в crypto/* 119

Slide 120

Slide 120 text

func init() { // ... registerBasics() // ... } Так что там в encoding/gob 120

Slide 121

Slide 121 text

func init() { // ... registerBasics() // ... } func registerBasics() { Register(int(0)) Register(int8(0)) Register(int16(0)) Register(int32(0)) Register(int64(0)) // ... Так что там в encoding/gob 121

Slide 122

Slide 122 text

Так что там в net 122 // net/conf_netcgo.go //go:build netcgo // +build netcgo package net // The build tag "netcgo" forces use of the cgo DNS resolver. // It is the opposite of "netgo". func init() { netCgo = true } // net/conf.go netGo bool // go DNS resolution forced netCgo bool // cgo DNS resolution forced

Slide 123

Slide 123 text

Так что там в net 123 // net/conf_netcgo.go //go:build netcgo // +build netcgo package net // The build tag "netcgo" forces use of the cgo DNS resolver. // It is the opposite of "netgo". func init() { netCgo = true } // net/conf.go netGo bool // go DNS resolution forced netCgo bool // cgo DNS resolution forced

Slide 124

Slide 124 text

// When debugging a particular http server-based test, // this flag lets you run // go test -run=BrokenTest -httptest.serve=127.0.0.1:8000 // to start the broken server so you can interact with it manually. // ... // isn't really part of our API. Don't depend on this. var serveFlag string func init() { if has(os.Args, "-httptest.serve=") { flag.StringVar(&serveFlag, "httptest.serve", "", "...") } } Так что там в net/http/httptest 124

Slide 125

Slide 125 text

func init() { http.HandleFunc("/debug/pprof/", Index) http.HandleFunc("/debug/pprof/cmdline", Cmdline) http.HandleFunc("/debug/pprof/profile", Profile) http.HandleFunc("/debug/pprof/symbol", Symbol) http.HandleFunc("/debug/pprof/trace", Trace) } Так что там в net/http/pprof 125

Slide 126

Slide 126 text

// mime/type_freebsd.go func init() { typeFiles = append(typeFiles, "/usr/local/etc/mime.types") } // mime/type_openbsd.go func init() { typeFiles = append(typeFiles, "/usr/share/misc/mime.types") } ... Так что там в mime 126 MIME == Multipurpose Internet Mail Extensions

Slide 127

Slide 127 text

func init() { runtime_registerPoolCleanup(poolCleanup) } func poolCleanup() { for _, p := range oldPools { // ... } for _, p := range allPools { // ... } // The pools with non-empty primary caches now have non-empty // victim caches and no pools have primary caches. oldPools, allPools = allPools, nil } Так что там в sync 127

Slide 128

Slide 128 text

Так что там в sync /2 // sync/runtime.go // Ensure that sync and runtime agree on size of notifyList. func runtime_notifyListCheck(size uintptr) func init() { var n notifyList runtime_notifyListCheck(unsafe.Sizeof(n)) } // runtime/sema.go //go:linkname notifyListCheck sync.runtime_notifyListCheck func notifyListCheck(sz uintptr) { if sz != unsafe.Sizeof(notifyList{}) { print("runtime: bad notifyList") throw("bad notifyList size") } } 128

Slide 129

Slide 129 text

Так что там в time/tzdata 129

Slide 130

Slide 130 text

// Package tzdata provides an embedded copy of the timezone database. // If this package is imported anywhere in the program, then if // the time package cannot find tzdata files on the system, // it will use this embedded information. // // Importing this package will increase the size of a program by about // 450 KB. // Так что там в time/tzdata 130

Slide 131

Slide 131 text

// Package tzdata provides an embedded copy of the timezone database. // If this package is imported anywhere in the program, then if // the time package cannot find tzdata files on the system, // it will use this embedded information. // // Importing this package will increase the size of a program by about // 450 KB. // Так что там в time/tzdata 131

Slide 132

Slide 132 text

Так что там в time/tzdata 132 // Package tzdata provides an embedded copy of the timezone database. // If this package is imported anywhere in the program, then if // the time package cannot find tzdata files on the system, // it will use this embedded information. // // Importing this package will increase the size of a program by about // 450 KB. // // This package should normally be imported by a program's main package, // not by a library. Libraries normally shouldn't decide whether to // include the timezone database in a program. // // This package will be automatically imported if you build with // -tags timetzdata. package tzdata

Slide 133

Slide 133 text

Так что там в time/tzdata 133 // Package tzdata provides an embedded copy of the timezone database. // If this package is imported anywhere in the program, then if // the time package cannot find tzdata files on the system, // it will use this embedded information. // // Importing this package will increase the size of a program by about // 450 KB. // // This package should normally be imported by a program's main package, // not by a library. Libraries normally shouldn't decide whether to // include the timezone database in a program. // // This package will be automatically imported if you build with // -tags timetzdata. package tzdata

Slide 134

Slide 134 text

Интересности в 1 слайд 134

Slide 135

Slide 135 text

- Часто можно увидеть объявление функции перед использованием - мы ведь помним, что когда-то Go был написан на Си :) Интересности в 1 слайд 135

Slide 136

Slide 136 text

- Часто можно увидеть объявление функции перед использованием - мы ведь помним, что когда-то Go был написан на Си :) - Также достаточное количество _ в именах переменных и функций - опять же наследие Си Интересности в 1 слайд 136

Slide 137

Slide 137 text

- Часто можно увидеть объявление функции перед использованием - мы ведь помним, что когда-то Go был написан на Си :) - Также достаточное количество _ в именах переменных и функций - опять же наследие Си - Windows, NetBSD, Wasm, JS и Plan9 особенные - часто из-за них есть осознанные костыли Интересности в 1 слайд 137

Slide 138

Slide 138 text

- Часто можно увидеть объявление функции перед использованием - мы ведь помним, что когда-то Go был написан на Си :) - Также достаточное количество _ в именах переменных и функций - опять же наследие Си - Windows, NetBSD, Wasm, JS и Plan9 особенные - часто из-за них есть осознанные костыли - Cgo is not Go, как говорил Пайк - и этим все сказано Интересности в 1 слайд 138

Slide 139

Slide 139 text

- Часто можно увидеть объявление функции перед использованием - мы ведь помним, что когда-то Go был написан на Си :) - Также достаточное количество _ в именах переменных и функций - опять же наследие Си - Windows, NetBSD, Wasm, JS и Plan9 особенные - часто из-за них есть осознанные костыли - Cgo is not Go, как говорил Пайк - и этим все сказано - Runtime is all about corner cases - такое моё ИМХО Интересности в 1 слайд 139

Slide 140

Slide 140 text

Вывод 140

Slide 141

Slide 141 text

Вывод 141 - Близко знакомимся с машиной - Создаём горутину - Создаём тред, который её выполнит - Выполняем и смотрим что выполнить еще - Всё

Slide 142

Slide 142 text

- A Quick Guide to Go's Assembler - https://golang.org/doc/asm - The Go Memory Model - https://golang.org/ref/mem - Go 1.2 Runtime Symbol Information (2013) - https://docs.google.com/document/d/1lyPIbmsYbXnpNj57a261hgOYVpNRcgydurVQIyZO z_o/pub - runtime/HACKING.md - https://github.com/golang/go/blob/master/src/runtime/HACKING.md Материалы 142

Slide 143

Slide 143 text

- Anton Repushko - Bogdan Storozhuk - Iskander Sharipov Благодарности 143

Slide 144

Slide 144 text

- Anton Repushko - Bogdan Storozhuk - Iskander Sharipov - И тебе тоже Благодарности 144

Slide 145

Slide 145 text

- Anton Repushko - Bogdan Storozhuk - Iskander Sharipov - И тебе тоже GopherCon team 👌 Благодарности 145

Slide 146

Slide 146 text

Thank you Questions? Telegram: @olegkovalov Twitter: @oleg_kovalov That’s all folks