Skip to content

Instantly share code, notes, and snippets.

@dorako321
Last active March 11, 2018 08:12
Show Gist options
  • Save dorako321/1cfac2e41dfb4987df2a4fe065d7ff77 to your computer and use it in GitHub Desktop.
Save dorako321/1cfac2e41dfb4987df2a4fe065d7ff77 to your computer and use it in GitHub Desktop.

デバッグ用のコードを書いてみる

helloworld.go

package main
import "fmt"

func main() {
  fmt.Println("Hello world!")
}

fmt.Println

以下は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

Fprintln

// 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
}

newPrinter()

(終わらなくなりそうなのでここにある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
}

p.doPrintln()

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')
}

w.Write()

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()

先頭が小文字なので非公開関数

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.Write

ついに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
}

syscall.write

// 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

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	
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment