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

Moments before main()

64a4ba69d50590e592cd8e572454daa8?s=47 Oleg Kovalov
September 15, 2021

Moments before main()

GopherCon Poland 2021

64a4ba69d50590e592cd8e572454daa8?s=128

Oleg Kovalov

September 15, 2021
Tweet

Transcript

  1. Moments before main() GopherCon Poland 2021 Oleg Kovalov 🌴 olegk.dev

  2. Me 2

  3. - Open source addicted gopher - Fan of linters (co-author

    go-critic) Me 3
  4. - Open source addicted gopher - Fan of linters (co-author

    go-critic) - Father of a labrador Me 4
  5. - Open source addicted gopher - Fan of linters (co-author

    go-critic) - Father of a labrador - Also Twitter/Telegram @go_perf - ... Me 5
  6. - Open source addicted gopher - Fan of linters (co-author

    go-critic) - Father of a labrador - Also Twitter/Telegram @go_perf - ... olegk.dev Me 6
  7. Agenda 7

  8. - Why??? Agenda 8

  9. - Why??? - We have *.exe Agenda 9

  10. - Why??? - We have *.exe - Let’s get code

    from binary - Let’s scroll the code Agenda 10
  11. - Why??? - We have *.exe - Let’s get code

    from binary - Let’s scroll the code - Let’s scroll the code - Let’s scroll the code - Let’s scroll the code - Let’s scroll the code - Let’s scroll the code - Let’s scroll the code Agenda 11
  12. - Why??? - We have *.exe - Let’s get code

    from binary - Let’s scroll the code - Let’s scroll the code - Let’s scroll the code - Let’s scroll the code - Let’s scroll the code - Let’s scroll the code - Let’s scroll the code - Inside stdlib Go 1.17.1, OS Darwin (macOS Big Sur), YMMV Agenda 12
  13. Why??? 13

  14. Why??? 14

  15. Why??? 15

  16. Why??? 16

  17. Why??? 17

  18. Life story 18

  19. Life story 19 Generated by https://github.com/knz/go-binsize-viz

  20. Life story: runtime part 20 Generated by https://github.com/knz/go-binsize-viz pclntab rodata

    types
  21. Good tool 21 Nice tool by Brad Fitzpatrick https://github.com/bradfitz/shotizam pclntab

    rodata types
  22. What is this pclntab? 22

  23. Ok that’s pclntab, problem solved 23

  24. Hm, maybe not pclntab only 24 https://lobste.rs/s/gvtstv/my_go_executable_files_are_still_getting#c_dbfije

  25. So what I’m trying to say 25

  26. - Looks like there is a problem So what I’m

    trying to say 26
  27. - Looks like there is a problem - But the

    problem is not so bad So what I’m trying to say 27
  28. - Looks like there is a problem - But the

    problem is not so bad - Docker image is more important - if your base image is Ubuntu/Debian So what I’m trying to say 28
  29. - Looks like there is a problem - But the

    problem is not so bad - Docker image is more important - if your base image is Ubuntu/Debian - also there is UPX So what I’m trying to say 29
  30. So what I’m trying to say 30 - Looks like

    there is a problem - But the problem is not so bad - Docker image is more important - if your base image is Ubuntu/Debian - also there is UPX - But what is inside?
  31. So what I’m trying to say 31 - Looks like

    there is a problem - But the problem is not so bad - Docker image is more important - if your base image is Ubuntu/Debian - also there is UPX - But what is inside? - Let’s talk about that
  32. Simplest program 32

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

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

    } ❯ go build -o main.exe main.go Simplest program 34
  35. ❯ cat main.go package main func main() { // ʕ◔ϖ◔ʔ

    } ❯ go build -o main.exe main.go ❯ du -sh main.exe 1.1M main.exe Simplest program 35
  36. ❯ cat main.go package main func main() { // ʕ◔ϖ◔ʔ

    } ❯ go build -o main.exe main.go ❯ du -sh main.exe 1.1M main.exe ❯ 👀 Simplest program 36
  37. ❯ go build -o main.exe main.go ❯ cat main.exe What

    is inside *.exe? /1 37
  38. cat 38

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

    . > main.json What is inside *.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 What is inside *.exe? /3 41
  42. - Disassembles executable files go tool objdump 42

  43. - Disassembles executable files var printCode = flag.Bool("S", false, "print

    Go code alongside assembly") go tool objdump 43
  44. - Disassembles executable files var printCode = flag.Bool("S", false, "print

    Go code alongside assembly") - Also symregexp flag flag.String("s", "", "only dump symbols matching this regexp") ❯ go tool objdump -S -s runtime.makech main.exe go tool objdump 44
  45. Example from main.dump 45

  46. ❯ go build -o main.exe main.go ❯ go tool objdump

    -S main.exe > main.dump ❯ du -sh main.dump 4.5M main.dump So objdump 46
  47. ❯ go build -o main.exe main.go ❯ go tool objdump

    -S main.exe > main.dump ❯ du -sh main.dump 4.5M main.dump ❯ rg "TEXT runtime." -c main.dump 945 So objdump 47
  48. ❯ go build -o main.exe main.go ❯ go tool objdump

    -S main.exe > main.dump ❯ du -sh main.dump 4.5M main.dump ❯ rg "TEXT runtime." -c main.dump 945 ❯ 🔥🔥🔥 So objdump 48
  49. Close look at main.dump 49

  50. - Just a text file Close look at main.dump 50

  51. - Just a text file - With all possible definitions

    Close look at main.dump 51
  52. Close look at main.dump 52 - Just a text file

    - With all possible definitions - Useful if you are doing low-level optimisations
  53. Close look at main.dump 53 - Just a text file

    - With all possible definitions - Useful if you are doing low-level optimisations - or a conference talk :)
  54. To sum up /0 54 - ... - main()

  55. ...124k lines later 0x1054bde cc INT $0x3 0x1054bdf cc INT

    $0x3 TEXT main.main(SB) /Users/olegkovalov/code/funf/main.go func main() {} 0x1054be0 c3 RET 0x1054be1 cc INT $0x3 0x1054be2 cc INT $0x3 0x1054be3 cc INT $0x3 Lovely main() 55
  56. ...124k lines later 0x1054bde cc INT $0x3 0x1054bdf cc INT

    $0x3 TEXT main.main(SB) /Users/olegkovalov/code/funf/main.go func main() {} 0x1054be0 c3 RET 0x1054be1 cc INT $0x3 0x1054be2 cc INT $0x3 0x1054be3 cc INT $0x3 Hm...main.main ? 56
  57. Real main 57 ...60k lines 0x102d7cd e8eebeffff CALL runtime.deferreturn(SB) 0x102d7d2

    488b6c2450 MOVQ 0x50(SP), BP 0x102d7d7 4883c458 ADDQ $0x58, SP 0x102d7db c3 RET func main() { 0x102d7dc 0f1f4000 NOPL 0(AX) 0x102d7e0 e8bb440200 CALL runtime.morestack_noctxt.abi0(SB) 0x102d7e5 e9b6fcffff JMP runtime.main(SB) 0x102d7ea cc INT $0x3 ...60k lines
  58. Real main...is closer ...60k lines 0x102d7cd e8eebeffff CALL runtime.deferreturn(SB) 0x102d7d2

    488b6c2450 MOVQ 0x50(SP), BP 0x102d7d7 4883c458 ADDQ $0x58, SP 0x102d7db c3 RET func main() { 0x102d7dc 0f1f4000 NOPL 0(AX) 0x102d7e0 e8bb440200 CALL runtime.morestack_noctxt.abi0(SB) 0x102d7e5 e9b6fcffff JMP runtime.main(SB) 0x102d7ea cc INT $0x3 ...60k lines 58
  59. runtime.main TEXT runtime.main(SB) /usr/local/Cellar/go/1.17/libexec/src/runtime/proc.go func main() { 0x102d4a0 493b6610 CMPQ

    0x10(R14), SP 0x102d4a4 0f8632030000 JBE 0x102d7dc 0x102d4aa 4883ec58 SUBQ $0x58, SP 0x102d4ae 48896c2450 MOVQ BP, 0x50(SP) 0x102d4b3 488d6c2450 LEAQ 0x50(SP), BP 0x102d4b8 49c7c500000000 MOVQ $0x0, R13 0x102d4bf 4c896c2448 MOVQ R13, 0x48(SP) 0x102d4c4 c644242700 MOVB $0x0, 0x27(SP) g := getg() 0x102d4c9 4c89742430 MOVQ R14, 0x30(SP) g.m.g0.racectx = 0 0x102d4ce 498b4630 MOVQ 0x30(R14), AX 0x102d4d2 488b00 MOVQ 0(AX), AX 0x102d4d5 48c7804001000000000000 MOVQ $0x0, 0x140(AX) maxstacksize = 1000000000 59
  60. Inside runtime/proc.go 60 //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() // ...
  61. To sum up /1 61 - ... - runtime.main() -

    main_main ~ main.main // linker’s magic - main.main // that’s us!
  62. But that's not all 62

  63. - There are strange runtime/asm_<arch>.s files But that's not all

    63
  64. - There are strange runtime/asm_<arch>.s files - arch is some

    CPU architecture (amd64, arm64, mips, …) - *.s - Assembler But that's not all 64
  65. - There are strange runtime/asm_<arch>.s files - arch is some

    CPU architecture (amd64, arm64, mips, …) - *.s - Assembler - 👀 But that's not all 65
  66. - There are strange runtime/asm_<arch>.s files - arch is some

    CPU architecture (amd64, arm64, mips, …) - *.s - Assembler - 👀 - Let’s check asm_amd64.s - just 2k lines But that's not all 66
  67. - There are strange runtime/asm_<arch>.s files - arch is some

    CPU architecture (amd64, arm64, mips, …) - *.s - Assembler - 👀 - Let’s check asm_amd64.s - just 2k lines - others are + - 1k - arm64.s is also heavy But that's not all 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 cheat sheet 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 to dst - CALL: invoke function - DATA(+GLOBL): data symbols (global) Go assembler cheat sheet 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) Ah, that the trick 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(SB),NOSPLIT|TOPFRAME,$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 // ... Scroll further 71
  72. Scroll further 72 TEXT runtime·rt0_go(SB),NOSPLIT|TOPFRAME,$0 // ... 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. Scroll further 73 TEXT runtime·rt0_go(SB),NOSPLIT|TOPFRAME,$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. - Platform dependent things runtime/os_<os>.go 74

  75. - Platform dependent things - Signal handlers - Timers runtime/os_<os>.go

    75
  76. - Platform dependent things - Signal handlers - Timers -

    Random - Thread sizes and other consts - Envs and cmd params - I mean environment variables и argc & argv runtime/os_<os>.go 76
  77. - Platform dependent things - Signal handlers - Timers -

    Random - Thread sizes and other consts - Envs and cmd params - I mean environment variables и argc & argv - Machine sanity checks - Oh, interesting! runtime/os_<os>.go 77
  78. - In runtime1.go there is an interesting function - check()

    Machine sanity checks 78
  79. - In runtime1.go there is an interesting function - check()

    - checks primitive type sizes - d uint16 - if unsafe.Sizeof(d) != 2 { throw("bad d") } Machine sanity checks 79
  80. - In runtime1.go there is an interesting function - check()

    - checks primitive type sizes - d uint16 - if unsafe.Sizeof(d) != 2 { throw("bad d") } - check() calls testAtomic64() Machine sanity checks 80
  81. - In runtime1.go there is an interesting function - check()

    - checks primitive type sizes - d uint16 - if unsafe.Sizeof(d) != 2 { throw("bad d") } - check() calls testAtomic64() - As you can guess testAtomic64 is about atomics - test_z64 = 42, test_x64 = 0 - if atomic.Cas64(&test_z64, test_x64, 1) { throw("cas64 failed") } Machine sanity checks 81
  82. Scroll further 82 TEXT runtime·rt0_go(SB),NOSPLIT|TOPFRAME,$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) // with some magic and linker this will be 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) } Scroll further 83
  84. Scroll further 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. Scroll further 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. Scroll further 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. Scroll further 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. To sum up /2 88 - runtime·_rt0_amd64 // or which

    architecture you have - runtime·rt0_go - <creating universe> - check - args - osinit - schedinit - runtime.main() - main_main ~ main.main // linker’s magic - main.main // that’s us!
  89. Scroll further 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 Everything else is if’s, atomics & unclear algorithms. Ez. Go runtime cheat sheet 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(SB),NOSPLIT|TOPFRAME,$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 A bit of assembler 94
  95. One more bit of assembler 95 TEXT runtime·rt0_go(SB),NOSPLIT|TOPFRAME,$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 // Create a new g running fn. // Put

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

    on the queue of g's waiting to run. // The compiler turns a go statement into a call to this. func newproc(fn *funcval) { gp := getg() pc := getcallerpc() systemstack(func() { newg := newproc1(fn, 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. //

    Put it on the queue of g's waiting to run. // The compiler turns a go statement into a call to this. func newproc(fn *funcval) { gp := getg() pc := getcallerpc() systemstack(func() { newg := newproc1(fn, 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. Finally 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. To sum up /3 102 - runtime·_rt0_amd64 // or which

    architecture you have - runtime·rt0_go - check - args - osinit - schedinit - ... - newproc // will create goroutine for runtime.main - mstart // to start goroutine above - mstart0 -> mstart1 - schedule - runtime.main() - main_main ~ main.main // linker’s magic - main.main // that’s us!
  103. To sum up /3.1 (WebAssembly) 103

  104. - Everything discussed is ~250 lines Ok, but why 2k

    lines? 104
  105. - Everything discussed is ~250 lines - Everything else is

    runtime things (obvious) Ok, but why 2k lines? 105
  106. - Everything discussed is ~250 lines - Everything else is

    runtime things (obvious) - stack handling - defer’s - different panics Ok, but why 2k lines? 106
  107. - Everything discussed is ~250 lines - Everything else is

    runtime things (obvious) - stack handling - defer’s - different panics - debug helpers - lovely cgo - gc barriers Ok, but why 2k lines? 107
  108. - Everything discussed is ~250 lines - Everything else is

    runtime things (obvious) - stack handling - defer’s - different panics - debug helpers - lovely cgo - gc barriers - timers - signals - hashes Ok, but why 2k lines? 108
  109. Popular pattern // func memhash(p unsafe.Pointer, h, s uintptr) uintptr

    // hash function using AES hardware instructions TEXT runtime·memhash<ABIInternal>(SB),NOSPLIT,$0-32 // AX = ptr to data // BX = seed // CX = size CMPB runtime·useAeshash(SB), $0 JEQ noaes 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 - Now

    you can easily find such lines - x86HasSSE41 = cpu.X86.HasSSE41 - to be honest a lot of them - Wasting our CPU on nothing - every nanosecond counts :( GOAMD64 proposal 110
  111. What about init()’s ?? 111

  112. - Runtime packages has a lot inits - Most of

    them are OS/machine checks - type size - available CPU flags - But there is one interesting (then we will scroll through other stdlib packages) What about init()’s ?? 112
  113. /Users/olegkovalov/go/src/golang/go/src/runtime/proc.go // start forcegc helper goroutine func init() { go

    forcegchelper() } Popular claim regarding Go GC 113
  114. /Users/olegkovalov/go/src/golang/go/src/runtime/proc.go // start forcegc helper goroutine func init() { go

    forcegchelper() } // 5k lines later... Popular claim regarding Go GC 114
  115. Popular claim regarding Go 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)) }

    Inside expvar 116
  117. func init() { crypto.RegisterHash(crypto.MD5, New) } Inside crypto/* 117

  118. func init() { crypto.RegisterHash(crypto.MD5, New) } // do THIS with

    crypto packages: import ( _ "crypto/sha256" // to register a sha256 _ "crypto/sha512" // to register a sha384/512 ) Inside crypto/* 118
  119. func init() { crypto.RegisterHash(crypto.MD5, New) } // do THIS with

    crypto packages: import ( _ "crypto/sha256" // to register a sha256 _ "crypto/sha512" // to register a sha384/512 ) // and NOT this: import ( _ "crypto/sha256" //nolint "crypto/sha512" ) var _ = sha512.<Something_Just_To_Import> Inside crypto/* 119
  120. func init() { // ... registerBasics() // ... } Inside

    encoding/gob 120
  121. func init() { // ... registerBasics() // ... } func

    registerBasics() { Register(int(0)) Register(int8(0)) Register(int16(0)) Register(int32(0)) Register(int64(0)) // ... Inside encoding/gob 121
  122. Inside 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. Inside 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", "", "...") } } Inside 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) } Inside 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") } ... Inside 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 } Inside sync 127
  128. Inside 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. Inside 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. // Inside 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. // Inside time/tzdata 131
  132. Inside 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. Inside 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 slide notes 134

  135. - Function definition before usage - do you remember that

    first versions of Go compiler was in C ? :) 1 slide notes 135
  136. - Function definition before usage - do you remember that

    first versions of Go compiler was in C ? :) - Many underscores in var and func names - again the C legacy 1 slide notes 136
  137. - Function definition before usage - do you remember that

    first versions of Go compiler was in C ? :) - Many underscores in var and func names - again the C legacy - Windows, NetBSD, Wasm, JS & Plan9 are special - often requires special treatment 1 slide notes 137
  138. - Function definition before usage - do you remember that

    first versions of Go compiler was in C ? :) - Many underscores in var and func names - again the C legacy - Windows, NetBSD, Wasm, JS & Plan9 are special - often requires special treatment - Cgo is not Go, as Rob Pike said - basically explains everything 1 slide notes 138
  139. - Function definition before usage - do you remember that

    first versions of Go compiler was in C ? :) - Many underscores in var and func names - again the C legacy - Windows, NetBSD, Wasm, JS & Plan9 are special - often requires special treatment - Cgo is not Go, as Rob Pike said - basically explains everything - Runtime is all about corner cases - that’s my IMHO 1 slide notes 139
  140. Conclusions 140

  141. - Meeting a machine Conclusions 141

  142. Conclusions 142 - Meeting a machine - Creating goroutine

  143. Conclusions 143 - Meeting a machine - Creating goroutine -

    Creating thread to run it
  144. Conclusions 144 - Meeting a machine - Creating goroutine -

    Creating thread to run it - Running and looking for another to run
  145. Conclusions 145 - Meeting a machine - Creating goroutine -

    Creating thread to run it - Running and looking for another to run - That’s all
  146. - 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 References 146
  147. - Anton Repushko - Bogdan Storozhuk - Iskander Sharipov Thanks

    147
  148. - Anton Repushko - Bogdan Storozhuk - Iskander Sharipov -

    and you Thanks 148
  149. - Anton Repushko - Bogdan Storozhuk - Iskander Sharipov -

    and you GopherCon Poland team 👌 Thanks 149
  150. Thank you Questions? Telegram: @olegkovalov Twitter: @oleg_kovalov That’s all folks