Upgrade to Pro — share decks privately, control downloads, hide ads and more …

За пару мгновений до main()

За пару мгновений до main()

64a4ba69d50590e592cd8e572454daa8?s=128

Oleg Kovalov

April 23, 2021
Tweet

Transcript

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

    gogoapps.io olegk.dev
  2. - Опенсорс-зависимый гофер Олег 2

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

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

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

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

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

    из ведущих подкаста Generic Talks - Веселый и полезный @oleg_log - А еще Twitter/Telegram @go_perf - ... olegk.dev Олег 7
  8. План доклада 8

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

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

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

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

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

  14. Зачем??? 14

  15. Зачем??? 15

  16. Зачем??? 16

  17. Зачем??? 17

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

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

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

    rodata types
  21. Правда из жизни рантайма 21 Хорошая тулза от Brad Fitzpatrick

    https://github.com/bradfitz/shotizam (нооо пока не работает с Go 1.16) pclntab rodata types
  22. Да что это такое этот ваш pclntab 22

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

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

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

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

    26
  27. - Проблема вроде бы есть - Но действительно проблематит не

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

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

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

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

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

  33. ❯ cat main.go package main func main() { // ʕ◔ϖ◔ʔ

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

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

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

    } ❯ go build -o main.exe main.go ❯ du -sh main.exec 1.1M main.exec ❯ 👀 Куда уж проще? 36
  37. ❯ go build -o main.exe main.go ❯ cat main.exe Что

    в нашем *.exe? /1 37
  38. cat 38

  39. ❯ go build -o main.exe main.go ❯ go list -json

    . > main.json Что в нашем *.exe? /2 39
  40. 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" ] }
  41. ❯ go build -o main.exe main.go ❯ go tool objdump

    -S main.exe > main.dump Что в нашем *.exe? /3 41
  42. - Дизассемблирует исполняемые файлы go tool objdump 42

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

    Go code alongside assembly") go tool objdump 43
  44. - Дизассемблирует исполняемые файлы 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
  45. Пример из main.dump 45

  46. ❯ 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
  47. ❯ 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
  48. ❯ 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
  49. Пойдем в main.dump 49

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

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

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

    всеми возможными объявлениями - Полезно, если делаете низкоуровневые оптимизации - Или какой-то там доклад
  53. Так и запишем /0 53 - ... - main()

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

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

    Эм...main.main ? 55
  56. Настоящий 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
  57. Настоящий 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
  58. 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
  59. Что к чему в 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() // ...
  60. Так и запишем /1 60 - ... - runtime.main() -

    main_main ~ main.main // магия линковщика - main.main // это мы!
  61. Вот только это не всё 61

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

    62
  63. - Есть странные runtime/asm_<arch>.s файлы - где arch - некая

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

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

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

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

    архитектура (amd64, arm64, mips, …) - *.s - Assembler - 👀 - Значит пойдём разбираться - Посмотрим на asm_amd64.s - всего лишь 2к строк - в остальных + - 1к - arm64.s тоже тяжелый Вот только это не всё 67
  68. - 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
  69. - 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
  70. // _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
  71. // 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<ABIInternal>(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
  72. Пойдем дальше 72 TEXT runtime·rt0_go<ABIInternal>(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)
  73. Пойдем дальше 73 TEXT runtime·rt0_go<ABIInternal>(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)
  74. - Да, содержат платформозависимые вещи runtime/os_<os>.go 74

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

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

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

    таймеры - Рандом - Размеры тредов и прочих системных вещей - Энвы и аргументы программы - речь о environment variables и argc & argv - Проверка адекватности машины - o! runtime/os_<os>.go 77
  78. - В файле runtime1.go есть интересная функция - check() Проверка

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

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

    проверяем размеры примитивных типов - d uint16 - if unsafe.Sizeof(d) != 2 { throw("bad d") } - check() вызывает testAtomic64() Проверка адекватности машины 80
  81. - В файле 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
  82. Пойдем дальше 82 TEXT runtime·rt0_go<ABIInternal>(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
  83. /Users/olegkovalov/go/src/golang/go/src/runtime/runtime1.go func args(c int32, v **byte) { argc = c

    argv = v sysargs(c, v) } Пойдем дальше 83
  84. Пойдем дальше 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)) // ... }
  85. Пойдем дальше 85 TEXT runtime·rt0_go<ABIInternal>(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)
  86. Пойдем дальше 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() }
  87. Пойдем дальше 87 TEXT runtime·rt0_go<ABIInternal>(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)
  88. Так и запишем /2 88 - runtime·_rt0_amd64 // или какая

    там у вас архитектура - runtime·rt0_go - создадим Вселенную - check - args - osinit - schedinit - runtime.main() - main_main ~ main.main // магия линковщика - main.main // это мы!
  89. Пойдем дальше 89 // The bootstrap sequence is: // //

    call osinit // call schedinit // make & queue new G // call runtime·mstart // // The new G calls runtime·main. func schedinit() { // ...
  90. - 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
  91. 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
  92. 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
  93. 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
  94. TEXT runtime·rt0_go<ABIInternal>(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<ABIInternal>(SB) GLOBL runtime·mainPC(SB),RODATA,$8 Еще чуть-чуть Ассемблера 94
  95. Последнее из Ассемблера, честно 95 TEXT runtime·rt0_go<ABIInternal>(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
  96. 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() } }) }
  97. // 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
  98. 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() } }) }
  99. 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())
  100. Наконец-то 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
  101. 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) }
  102. Так и запишем /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 // это мы!
  103. Так и запишем /3.1 (WebAssembly) 103

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

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

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

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

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

    runtime-овые вещи (кэп) - управлением стеком - выполнение defer - различные паники - помощь для дебага - любимый cgo - gc barriers - таймеры - сигналы - хеши Оке, а почему там 2к линий? 108
  109. Пойдем дальше // func memhash(p unsafe.Pointer, h, s uintptr) uintptr

    // hash function using AES hardware instructions TEXT runtime·memhash<ABIInternal>(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<ABIInternal>(SB) /Users/olegkovalov/go/src/golang/go/src/runtime/hash64.go func memhashFallback(p unsafe.Pointer, seed, s uintptr) uintptr { 109
  110. - add GOAMD64 architecture variants #25489 - https://github.com/golang/go/issues/25489 - Сейчас

    можно найти похожие строки - x86HasSSE41 = cpu.X86.HasSSE41 - на самом деле их сильно больше - Из-за чего мы теряем такты процессора - каждая наносекунда на счету :( GOAMD64 proposal 110
  111. Что там в init()-ах?? 111

  112. - В runtime init-ов не много - Большинство из них

    это просто проверки машины - какие размеры типов - какие флаги у процессора - Но есть 1 интересный в runtime (потом обсудим некоторые пакеты из stdlib) Так что там в init()-ах?? 112
  113. /Users/olegkovalov/go/src/golang/go/src/runtime/proc.go // start forcegc helper goroutine func init() { go

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

    forcegchelper() } // 5k lines later... Популярная претензия к GC 114
  115. Популярная претензия к 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
  116. func init() { http.HandleFunc("/debug/vars", expvarHandler) Publish("cmdline", Func(cmdline)) Publish("memstats", Func(memstats)) }

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

    crypto/* 117
  118. func init() { crypto.RegisterHash(crypto.MD5, New) } // Именно поэтому в

    импортах мы делаем: import ( _ "crypto/sha256" // to register a sha256 _ "crypto/sha512" // to register a sha384/512 ) Так что там в crypto/* 118
  119. 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.<Something_Just_To_Import> Так что там в crypto/* 119
  120. func init() { // ... registerBasics() // ... } Так

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

    registerBasics() { Register(int(0)) Register(int8(0)) Register(int16(0)) Register(int32(0)) Register(int64(0)) // ... Так что там в encoding/gob 121
  122. Так что там в 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
  123. Так что там в 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
  124. // 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
  125. 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
  126. // 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
  127. 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
  128. Так что там в 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
  129. Так что там в time/tzdata 129

  130. // 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
  131. // 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
  132. Так что там в 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
  133. Так что там в 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
  134. Интересности в 1 слайд 134

  135. - Часто можно увидеть объявление функции перед использованием - мы

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

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

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

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

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

  141. Вывод 141 - Близко знакомимся с машиной - Создаём горутину

    - Создаём тред, который её выполнит - Выполняем и смотрим что выполнить еще - Всё
  142. - 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
  143. - Anton Repushko - Bogdan Storozhuk - Iskander Sharipov Благодарности

    143
  144. - Anton Repushko - Bogdan Storozhuk - Iskander Sharipov -

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

    И тебе тоже GopherCon team 👌 Благодарности 145
  146. Thank you Questions? Telegram: @olegkovalov Twitter: @oleg_kovalov That’s all folks