デバッグ用のコードを書いてみる
package main
import "fmt"
func main() {
fmt.Println("Hello world!")
}
以下はfmt.Printlnをステップイン追いかけたメモ
// Println formats using the default formats for its operands and writes to standard output.
// Spaces are always added between operands and a newline is appended.
// It returns the number of bytes written and any write error encountered.
func Println(a ...interface{}) (n int, err error) {
return Fprintln(os.Stdout, a...)
}
...はvariadic (可変長引数)
https://golang.org/ref/spec#Passing_arguments_to_..._parameters
// These routines end in 'ln', do not take a format string,
// always add spaces between operands, and add a newline
// after the last operand.
// Fprintln formats using the default formats for its operands and writes to w.
// Spaces are always added between operands and a newline is appended.
// It returns the number of bytes written and any write error encountered.
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
p := newPrinter()
p.doPrintln(a)
n, err = w.Write(p.buf)
p.free()
return
}
(終わらなくなりそうなのでここにあるppFree.Get()あたりは未調査) newPrinter は新しいpp構造体の領域を確保するか、キャッシュ済みの物をガベージする
// newPrinter allocates a new pp struct or grabs a cached one.
func newPrinter() *pp {
p := ppFree.Get().(*pp)
p.panicking = false
p.erroring = false
p.fmt.init(&p.buf)
return p
}
doPrintlnはdoPrintに似ているが常に引数の間に空白を追加し、最後の引数の後に行を追加する。
// doPrintln is like doPrint but always adds a space between arguments
// and a newline after the last argument.
func (p *pp) doPrintln(a []interface{}) {
for argNum, arg := range a {
if argNum > 0 {
p.buf.WriteByte(' ')
}
p.printArg(arg, 'v')
}
p.buf.WriteByte('\n')
}
Writeはファイルにlen(b)バイト書き込む。 書き込んだバイト数とエラーを戻り値にする。 n != len(b) の場合エラーを返す。
// Write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
// Write returns a non-nil error when n != len(b).
func (f *File) Write(b []byte) (n int, err error) {
if err := f.checkValid("write"); err != nil {
return 0, err
}
n, e := f.write(b)
if n < 0 {
n = 0
}
if n != len(b) {
err = io.ErrShortWrite
}
epipecheck(f, e)
if e != nil {
err = &PathError{"write", f.name, e}
}
return n, err
}
先頭が小文字なので非公開関数
Writeはファイルにlen(b)バイト書き込む。書き込んだバイト数とエラーを戻り値にする。
// write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
func (f *File) write(b []byte) (n int, err error) {
for {
bcap := b
if needsMaxRW && len(bcap) > maxRW {
bcap = bcap[:maxRW]
}
m, err := fixCount(syscall.Write(f.fd, bcap))
n += m
// If the syscall wrote some data but not all (short write)
// or it returned EINTR, then assume it stopped early for
// reasons that are uninteresting to the caller, and try again.
if 0 < m && m < len(bcap) || err == syscall.EINTR {
b = b[m:]
continue
}
if needsMaxRW && len(bcap) != len(b) && err == nil {
b = b[m:]
continue
}
return n, err
}
}
ついにsyscallだ!
func Write(fd int, p []byte) (n int, err error) {
if race.Enabled {
race.ReleaseMerge(unsafe.Pointer(&ioSync))
}
n, err = write(fd, p)
if race.Enabled && n > 0 {
race.ReadRange(unsafe.Pointer(&p[0]), n)
}
if msanenabled && n > 0 {
msanRead(unsafe.Pointer(&p[0]), n)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func write(fd int, p []byte) (n int, err error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
} else {
_p0 = unsafe.Pointer(&_zero)
}
r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)))
n = int(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
syscallの逆アセンブル結果が出てくるようになりました コメントにAMD64と書かれているのでアーキテクチャがx64なのがわかります
goの実装はほぼ全てgoで実装されていますが、OS機能の呼び出しは、C言語で実装されているシステムコールを実行しています。
https://godoc.org/golang.org/x/sys/unix#Syscall
https://golang.org/src/syscall/syscall_darwin.go?#L218
//
// System call support for AMD64, Darwin
//
// Trap # in AX, args in DI SI DX, return in AX DX
// func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno);
TEXT ·Syscall(SB),NOSPLIT,$0-56
CALL runtime·entersyscall(SB)
MOVQ a1+8(FP), DI
MOVQ a2+16(FP), SI
MOVQ a3+24(FP), DX
MOVQ $0, R10
MOVQ $0, R8
MOVQ $0, R9
MOVQ trap+0(FP), AX // syscall entry
ADDQ $0x2000000, AX
SYSCALL
JCC ok
MOVQ $-1, r1+32(FP)
MOVQ $0, r2+40(FP)
MOVQ AX, err+48(FP)
CALL runtime·exitsyscall(SB)
RET
ok:
MOVQ AX, r1+32(FP)
MOVQ DX, r2+40(FP)
MOVQ $0, err+48(FP)
CALL runtime·exitsyscall(SB)
RET