View
@@ -6,6 +6,7 @@ package bytes_test
import (
. "bytes"
"fmt"
"math/rand"
"reflect"
"testing"
@@ -357,167 +358,152 @@ func TestIndexRune(t *testing.T) {
var bmbuf []byte
func BenchmarkIndexByte10(b *testing.B) { bmIndexByte(b, IndexByte, 10) }
func BenchmarkIndexByte32(b *testing.B) { bmIndexByte(b, IndexByte, 32) }
func BenchmarkIndexByte4K(b *testing.B) { bmIndexByte(b, IndexByte, 4<<10) }
func BenchmarkIndexByte4M(b *testing.B) { bmIndexByte(b, IndexByte, 4<<20) }
func BenchmarkIndexByte64M(b *testing.B) { bmIndexByte(b, IndexByte, 64<<20) }
func BenchmarkIndexBytePortable10(b *testing.B) { bmIndexByte(b, IndexBytePortable, 10) }
func BenchmarkIndexBytePortable32(b *testing.B) { bmIndexByte(b, IndexBytePortable, 32) }
func BenchmarkIndexBytePortable4K(b *testing.B) { bmIndexByte(b, IndexBytePortable, 4<<10) }
func BenchmarkIndexBytePortable4M(b *testing.B) { bmIndexByte(b, IndexBytePortable, 4<<20) }
func BenchmarkIndexBytePortable64M(b *testing.B) { bmIndexByte(b, IndexBytePortable, 64<<20) }
func bmIndexByte(b *testing.B, index func([]byte, byte) int, n int) {
if len(bmbuf) < n {
bmbuf = make([]byte, n)
}
b.SetBytes(int64(n))
buf := bmbuf[0:n]
buf[n-1] = 'x'
for i := 0; i < b.N; i++ {
j := index(buf, 'x')
if j != n-1 {
b.Fatal("bad index", j)
}
func valName(x int) string {
if s := x >> 20; s<<20 == x {
return fmt.Sprintf("%dM", s)
}
if s := x >> 10; s<<10 == x {
return fmt.Sprintf("%dK", s)
}
buf[n-1] = '\x00'
return fmt.Sprint(x)
}
func BenchmarkEqual0(b *testing.B) {
var buf [4]byte
buf1 := buf[0:0]
buf2 := buf[1:1]
for i := 0; i < b.N; i++ {
eq := Equal(buf1, buf2)
if !eq {
b.Fatal("bad equal")
}
}
}
func BenchmarkEqual1(b *testing.B) { bmEqual(b, Equal, 1) }
func BenchmarkEqual6(b *testing.B) { bmEqual(b, Equal, 6) }
func BenchmarkEqual9(b *testing.B) { bmEqual(b, Equal, 9) }
func BenchmarkEqual15(b *testing.B) { bmEqual(b, Equal, 15) }
func BenchmarkEqual16(b *testing.B) { bmEqual(b, Equal, 16) }
func BenchmarkEqual20(b *testing.B) { bmEqual(b, Equal, 20) }
func BenchmarkEqual32(b *testing.B) { bmEqual(b, Equal, 32) }
func BenchmarkEqual4K(b *testing.B) { bmEqual(b, Equal, 4<<10) }
func BenchmarkEqual4M(b *testing.B) { bmEqual(b, Equal, 4<<20) }
func BenchmarkEqual64M(b *testing.B) { bmEqual(b, Equal, 64<<20) }
func BenchmarkEqualPort1(b *testing.B) { bmEqual(b, EqualPortable, 1) }
func BenchmarkEqualPort6(b *testing.B) { bmEqual(b, EqualPortable, 6) }
func BenchmarkEqualPort32(b *testing.B) { bmEqual(b, EqualPortable, 32) }
func BenchmarkEqualPort4K(b *testing.B) { bmEqual(b, EqualPortable, 4<<10) }
func BenchmarkEqualPortable4M(b *testing.B) { bmEqual(b, EqualPortable, 4<<20) }
func BenchmarkEqualPortable64M(b *testing.B) { bmEqual(b, EqualPortable, 64<<20) }
func bmEqual(b *testing.B, equal func([]byte, []byte) bool, n int) {
if len(bmbuf) < 2*n {
bmbuf = make([]byte, 2*n)
}
b.SetBytes(int64(n))
buf1 := bmbuf[0:n]
buf2 := bmbuf[n : 2*n]
buf1[n-1] = 'x'
buf2[n-1] = 'x'
for i := 0; i < b.N; i++ {
eq := equal(buf1, buf2)
if !eq {
b.Fatal("bad equal")
}
func benchBytes(b *testing.B, sizes []int, f func(b *testing.B, n int)) {
for _, n := range sizes {
b.Run(valName(n), func(b *testing.B) {
if len(bmbuf) < n {
bmbuf = make([]byte, n)
}
b.SetBytes(int64(n))
f(b, n)
})
}
buf1[n-1] = '\x00'
buf2[n-1] = '\x00'
}
func BenchmarkIndex32(b *testing.B) { bmIndex(b, Index, 32) }
func BenchmarkIndex4K(b *testing.B) { bmIndex(b, Index, 4<<10) }
func BenchmarkIndex4M(b *testing.B) { bmIndex(b, Index, 4<<20) }
func BenchmarkIndex64M(b *testing.B) { bmIndex(b, Index, 64<<20) }
var indexSizes = []int{10, 32, 4 << 10, 4 << 20, 64 << 20}
func bmIndex(b *testing.B, index func([]byte, []byte) int, n int) {
if len(bmbuf) < n {
bmbuf = make([]byte, n)
}
b.SetBytes(int64(n))
buf := bmbuf[0:n]
buf[n-1] = 'x'
for i := 0; i < b.N; i++ {
j := index(buf, buf[n-7:])
if j != n-7 {
b.Fatal("bad index", j)
func BenchmarkIndexByte(b *testing.B) {
benchBytes(b, indexSizes, bmIndexByte(IndexByte))
}
func BenchmarkIndexBytePortable(b *testing.B) {
benchBytes(b, indexSizes, bmIndexByte(IndexBytePortable))
}
func bmIndexByte(index func([]byte, byte) int) func(b *testing.B, n int) {
return func(b *testing.B, n int) {
buf := bmbuf[0:n]
buf[n-1] = 'x'
for i := 0; i < b.N; i++ {
j := index(buf, 'x')
if j != n-1 {
b.Fatal("bad index", j)
}
}
buf[n-1] = '\x00'
}
buf[n-1] = '\x00'
}
func BenchmarkIndexEasy32(b *testing.B) { bmIndexEasy(b, Index, 32) }
func BenchmarkIndexEasy4K(b *testing.B) { bmIndexEasy(b, Index, 4<<10) }
func BenchmarkIndexEasy4M(b *testing.B) { bmIndexEasy(b, Index, 4<<20) }
func BenchmarkIndexEasy64M(b *testing.B) { bmIndexEasy(b, Index, 64<<20) }
func BenchmarkEqual(b *testing.B) {
b.Run("0", func(b *testing.B) {
var buf [4]byte
buf1 := buf[0:0]
buf2 := buf[1:1]
for i := 0; i < b.N; i++ {
eq := Equal(buf1, buf2)
if !eq {
b.Fatal("bad equal")
}
}
})
func bmIndexEasy(b *testing.B, index func([]byte, []byte) int, n int) {
if len(bmbuf) < n {
bmbuf = make([]byte, n)
}
b.SetBytes(int64(n))
buf := bmbuf[0:n]
buf[n-1] = 'x'
buf[n-7] = 'x'
for i := 0; i < b.N; i++ {
j := index(buf, buf[n-7:])
if j != n-7 {
b.Fatal("bad index", j)
sizes := []int{1, 6, 9, 15, 16, 20, 32, 4 << 10, 4 << 20, 64 << 20}
benchBytes(b, sizes, bmEqual(Equal))
}
func BenchmarkEqualPort(b *testing.B) {
sizes := []int{1, 6, 32, 4 << 10, 4 << 20, 64 << 20}
benchBytes(b, sizes, bmEqual(EqualPortable))
}
func bmEqual(equal func([]byte, []byte) bool) func(b *testing.B, n int) {
return func(b *testing.B, n int) {
if len(bmbuf) < 2*n {
bmbuf = make([]byte, 2*n)
}
buf1 := bmbuf[0:n]
buf2 := bmbuf[n : 2*n]
buf1[n-1] = 'x'
buf2[n-1] = 'x'
for i := 0; i < b.N; i++ {
eq := equal(buf1, buf2)
if !eq {
b.Fatal("bad equal")
}
}
buf1[n-1] = '\x00'
buf2[n-1] = '\x00'
}
buf[n-1] = '\x00'
buf[n-7] = '\x00'
}
func BenchmarkCount32(b *testing.B) { bmCount(b, Count, 32) }
func BenchmarkCount4K(b *testing.B) { bmCount(b, Count, 4<<10) }
func BenchmarkCount4M(b *testing.B) { bmCount(b, Count, 4<<20) }
func BenchmarkCount64M(b *testing.B) { bmCount(b, Count, 64<<20) }
func BenchmarkIndex(b *testing.B) {
benchBytes(b, indexSizes, func(b *testing.B, n int) {
buf := bmbuf[0:n]
buf[n-1] = 'x'
for i := 0; i < b.N; i++ {
j := Index(buf, buf[n-7:])
if j != n-7 {
b.Fatal("bad index", j)
}
}
buf[n-1] = '\x00'
})
}
func bmCount(b *testing.B, count func([]byte, []byte) int, n int) {
if len(bmbuf) < n {
bmbuf = make([]byte, n)
}
b.SetBytes(int64(n))
buf := bmbuf[0:n]
buf[n-1] = 'x'
for i := 0; i < b.N; i++ {
j := count(buf, buf[n-7:])
if j != 1 {
b.Fatal("bad count", j)
func BenchmarkIndexEasy(b *testing.B) {
benchBytes(b, indexSizes, func(b *testing.B, n int) {
buf := bmbuf[0:n]
buf[n-1] = 'x'
buf[n-7] = 'x'
for i := 0; i < b.N; i++ {
j := Index(buf, buf[n-7:])
if j != n-7 {
b.Fatal("bad index", j)
}
}
}
buf[n-1] = '\x00'
buf[n-1] = '\x00'
buf[n-7] = '\x00'
})
}
func BenchmarkCountEasy32(b *testing.B) { bmCountEasy(b, Count, 32) }
func BenchmarkCountEasy4K(b *testing.B) { bmCountEasy(b, Count, 4<<10) }
func BenchmarkCountEasy4M(b *testing.B) { bmCountEasy(b, Count, 4<<20) }
func BenchmarkCountEasy64M(b *testing.B) { bmCountEasy(b, Count, 64<<20) }
func BenchmarkCount(b *testing.B) {
benchBytes(b, indexSizes, func(b *testing.B, n int) {
buf := bmbuf[0:n]
buf[n-1] = 'x'
for i := 0; i < b.N; i++ {
j := Count(buf, buf[n-7:])
if j != 1 {
b.Fatal("bad count", j)
}
}
buf[n-1] = '\x00'
})
}
func bmCountEasy(b *testing.B, count func([]byte, []byte) int, n int) {
if len(bmbuf) < n {
bmbuf = make([]byte, n)
}
b.SetBytes(int64(n))
buf := bmbuf[0:n]
buf[n-1] = 'x'
buf[n-7] = 'x'
for i := 0; i < b.N; i++ {
j := count(buf, buf[n-7:])
if j != 1 {
b.Fatal("bad count", j)
func BenchmarkCountEasy(b *testing.B) {
benchBytes(b, indexSizes, func(b *testing.B, n int) {
buf := bmbuf[0:n]
buf[n-1] = 'x'
buf[n-7] = 'x'
for i := 0; i < b.N; i++ {
j := Count(buf, buf[n-7:])
if j != 1 {
b.Fatal("bad count", j)
}
}
}
buf[n-1] = '\x00'
buf[n-7] = '\x00'
buf[n-1] = '\x00'
buf[n-7] = '\x00'
})
}
type ExplodeTest struct {
@@ -1318,33 +1304,24 @@ func BenchmarkRepeat(b *testing.B) {
}
}
func benchmarkBytesCompare(b *testing.B, n int) {
var x = make([]byte, n)
var y = make([]byte, n)
func BenchmarkBytesCompare(b *testing.B) {
for n := 1; n <= 2048; n <<= 1 {
b.Run(fmt.Sprint(n), func(b *testing.B) {
var x = make([]byte, n)
var y = make([]byte, n)
for i := 0; i < n; i++ {
x[i] = 'a'
}
for i := 0; i < n; i++ {
x[i] = 'a'
}
for i := 0; i < n; i++ {
y[i] = 'a'
}
for i := 0; i < n; i++ {
y[i] = 'a'
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
Compare(x, y)
}
}
func BenchmarkBytesCompare1(b *testing.B) { benchmarkBytesCompare(b, 1) }
func BenchmarkBytesCompare2(b *testing.B) { benchmarkBytesCompare(b, 2) }
func BenchmarkBytesCompare4(b *testing.B) { benchmarkBytesCompare(b, 4) }
func BenchmarkBytesCompare8(b *testing.B) { benchmarkBytesCompare(b, 8) }
func BenchmarkBytesCompare16(b *testing.B) { benchmarkBytesCompare(b, 16) }
func BenchmarkBytesCompare32(b *testing.B) { benchmarkBytesCompare(b, 32) }
func BenchmarkBytesCompare64(b *testing.B) { benchmarkBytesCompare(b, 64) }
func BenchmarkBytesCompare128(b *testing.B) { benchmarkBytesCompare(b, 128) }
func BenchmarkBytesCompare256(b *testing.B) { benchmarkBytesCompare(b, 256) }
func BenchmarkBytesCompare512(b *testing.B) { benchmarkBytesCompare(b, 512) }
func BenchmarkBytesCompare1024(b *testing.B) { benchmarkBytesCompare(b, 1024) }
func BenchmarkBytesCompare2048(b *testing.B) { benchmarkBytesCompare(b, 2048) }
b.ResetTimer()
for i := 0; i < b.N; i++ {
Compare(x, y)
}
})
}
}
View
@@ -172,7 +172,7 @@ func (f *File) saveExprs(x interface{}, context string) {
f.saveRef(x, context)
}
case *ast.CallExpr:
f.saveCall(x)
f.saveCall(x, context)
}
}
@@ -220,15 +220,16 @@ func (f *File) saveRef(n *ast.Expr, context string) {
}
// Save calls to C.xxx for later processing.
func (f *File) saveCall(call *ast.CallExpr) {
func (f *File) saveCall(call *ast.CallExpr, context string) {
sel, ok := call.Fun.(*ast.SelectorExpr)
if !ok {
return
}
if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" {
return
}
f.Calls = append(f.Calls, call)
c := &Call{Call: call, Deferred: context == "defer"}
f.Calls = append(f.Calls, c)
}
// If a function should be exported add it to ExpFunc.
@@ -401,7 +402,7 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{}
case *ast.GoStmt:
f.walk(n.Call, "expr", visit)
case *ast.DeferStmt:
f.walk(n.Call, "expr", visit)
f.walk(n.Call, "defer", visit)
case *ast.ReturnStmt:
f.walk(n.Results, "expr", visit)
case *ast.BranchStmt:
View
@@ -581,7 +581,7 @@ func (p *Package) mangleName(n *Name) {
func (p *Package) rewriteCalls(f *File) {
for _, call := range f.Calls {
// This is a call to C.xxx; set goname to "xxx".
goname := call.Fun.(*ast.SelectorExpr).Sel.Name
goname := call.Call.Fun.(*ast.SelectorExpr).Sel.Name
if goname == "malloc" {
continue
}
@@ -596,37 +596,58 @@ func (p *Package) rewriteCalls(f *File) {
// rewriteCall rewrites one call to add pointer checks. We replace
// each pointer argument x with _cgoCheckPointer(x).(T).
func (p *Package) rewriteCall(f *File, call *ast.CallExpr, name *Name) {
func (p *Package) rewriteCall(f *File, call *Call, name *Name) {
any := false
for i, param := range name.FuncType.Params {
if len(call.Args) <= i {
if len(call.Call.Args) <= i {
// Avoid a crash; this will be caught when the
// generated file is compiled.
return
}
if p.needsPointerCheck(f, param.Go, call.Call.Args[i]) {
any = true
break
}
}
if !any {
return
}
// An untyped nil does not need a pointer check, and
// when _cgoCheckPointer returns the untyped nil the
// type assertion we are going to insert will fail.
// Easier to just skip nil arguments.
// TODO: Note that this fails if nil is shadowed.
if id, ok := call.Args[i].(*ast.Ident); ok && id.Name == "nil" {
continue
// We need to rewrite this call.
//
// We are going to rewrite C.f(p) to C.f(_cgoCheckPointer(p)).
// If the call to C.f is deferred, that will check p at the
// point of the defer statement, not when the function is called, so
// rewrite to func(_cgo0 ptype) { C.f(_cgoCheckPointer(_cgo0)) }(p)
var dargs []ast.Expr
if call.Deferred {
dargs = make([]ast.Expr, len(name.FuncType.Params))
}
for i, param := range name.FuncType.Params {
origArg := call.Call.Args[i]
darg := origArg
if call.Deferred {
dargs[i] = darg
darg = ast.NewIdent(fmt.Sprintf("_cgo%d", i))
call.Call.Args[i] = darg
}
if !p.needsPointerCheck(f, param.Go) {
if !p.needsPointerCheck(f, param.Go, origArg) {
continue
}
c := &ast.CallExpr{
Fun: ast.NewIdent("_cgoCheckPointer"),
Args: []ast.Expr{
call.Args[i],
darg,
},
}
// Add optional additional arguments for an address
// expression.
c.Args = p.checkAddrArgs(f, c.Args, call.Args[i])
c.Args = p.checkAddrArgs(f, c.Args, origArg)
// _cgoCheckPointer returns interface{}.
// We need to type assert that to the type we want.
@@ -636,7 +657,7 @@ func (p *Package) rewriteCall(f *File, call *ast.CallExpr, name *Name) {
// Instead we use a local variant of _cgoCheckPointer.
var arg ast.Expr
if n := p.unsafeCheckPointerName(param.Go); n != "" {
if n := p.unsafeCheckPointerName(param.Go, call.Deferred); n != "" {
c.Fun = ast.NewIdent(n)
arg = c
} else {
@@ -664,14 +685,73 @@ func (p *Package) rewriteCall(f *File, call *ast.CallExpr, name *Name) {
}
}
call.Args[i] = arg
call.Call.Args[i] = arg
}
if call.Deferred {
params := make([]*ast.Field, len(name.FuncType.Params))
for i, param := range name.FuncType.Params {
ptype := param.Go
if p.hasUnsafePointer(ptype) {
// Avoid generating unsafe.Pointer by using
// interface{}. This works because we are
// going to call a _cgoCheckPointer function
// anyhow.
ptype = &ast.InterfaceType{
Methods: &ast.FieldList{},
}
}
params[i] = &ast.Field{
Names: []*ast.Ident{
ast.NewIdent(fmt.Sprintf("_cgo%d", i)),
},
Type: ptype,
}
}
dbody := &ast.CallExpr{
Fun: call.Call.Fun,
Args: call.Call.Args,
}
call.Call.Fun = &ast.FuncLit{
Type: &ast.FuncType{
Params: &ast.FieldList{
List: params,
},
},
Body: &ast.BlockStmt{
List: []ast.Stmt{
&ast.ExprStmt{
X: dbody,
},
},
},
}
call.Call.Args = dargs
call.Call.Lparen = token.NoPos
call.Call.Rparen = token.NoPos
// There is a Ref pointing to the old call.Call.Fun.
for _, ref := range f.Ref {
if ref.Expr == &call.Call.Fun {
ref.Expr = &dbody.Fun
}
}
}
}
// needsPointerCheck returns whether the type t needs a pointer check.
// This is true if t is a pointer and if the value to which it points
// might contain a pointer.
func (p *Package) needsPointerCheck(f *File, t ast.Expr) bool {
func (p *Package) needsPointerCheck(f *File, t ast.Expr, arg ast.Expr) bool {
// An untyped nil does not need a pointer check, and when
// _cgoCheckPointer returns the untyped nil the type assertion we
// are going to insert will fail. Easier to just skip nil arguments.
// TODO: Note that this fails if nil is shadowed.
if id, ok := arg.(*ast.Ident); ok && id.Name == "nil" {
return false
}
return p.hasPointer(f, t, true)
}
@@ -859,20 +939,31 @@ func (p *Package) isType(t ast.Expr) bool {
// assertion to unsafe.Pointer in our copy of user code. We return
// the name of the _cgoCheckPointer function we are going to build, or
// the empty string if the type does not use unsafe.Pointer.
func (p *Package) unsafeCheckPointerName(t ast.Expr) string {
//
// The deferred parameter is true if this check is for the argument of
// a deferred function. In that case we need to use an empty interface
// as the argument type, because the deferred function we introduce in
// rewriteCall will use an empty interface type, and we can't add a
// type assertion. This is handled by keeping a separate list, and
// writing out the lists separately in writeDefs.
func (p *Package) unsafeCheckPointerName(t ast.Expr, deferred bool) string {
if !p.hasUnsafePointer(t) {
return ""
}
var buf bytes.Buffer
conf.Fprint(&buf, fset, t)
s := buf.String()
for i, t := range p.CgoChecks {
checks := &p.CgoChecks
if deferred {
checks = &p.DeferredCgoChecks
}
for i, t := range *checks {
if s == t {
return p.unsafeCheckPointerNameIndex(i)
return p.unsafeCheckPointerNameIndex(i, deferred)
}
}
p.CgoChecks = append(p.CgoChecks, s)
return p.unsafeCheckPointerNameIndex(len(p.CgoChecks) - 1)
*checks = append(*checks, s)
return p.unsafeCheckPointerNameIndex(len(*checks)-1, deferred)
}
// hasUnsafePointer returns whether the Go type t uses unsafe.Pointer.
@@ -900,7 +991,10 @@ func (p *Package) hasUnsafePointer(t ast.Expr) bool {
// unsafeCheckPointerNameIndex returns the name to use for a
// _cgoCheckPointer variant based on the index in the CgoChecks slice.
func (p *Package) unsafeCheckPointerNameIndex(i int) string {
func (p *Package) unsafeCheckPointerNameIndex(i int, deferred bool) string {
if deferred {
return fmt.Sprintf("_cgoCheckPointerInDefer%d", i)
}
return fmt.Sprintf("_cgoCheckPointer%d", i)
}
View
@@ -42,7 +42,10 @@ type Package struct {
GoFiles []string // list of Go files
GccFiles []string // list of gcc output files
Preamble string // collected preamble for _cgo_export.h
CgoChecks []string // see unsafeCheckPointerName
// See unsafeCheckPointerName.
CgoChecks []string
DeferredCgoChecks []string
}
// A File collects information about a single Go input file.
@@ -52,7 +55,7 @@ type File struct {
Package string // Package name
Preamble string // C preamble (doc comment on import "C")
Ref []*Ref // all references to C.xxx in AST
Calls []*ast.CallExpr // all calls to C.xxx in AST
Calls []*Call // all calls to C.xxx in AST
ExpFunc []*ExpFunc // exported functions for this file
Name map[string]*Name // map from Go name to Name
}
@@ -66,6 +69,12 @@ func nameKeys(m map[string]*Name) []string {
return ks
}
// A Call refers to a call of a C.xxx function in the AST.
type Call struct {
Call *ast.CallExpr
Deferred bool
}
// A Ref refers to an expression of the form C.xxx in the AST.
type Ref struct {
Name *Name
View
@@ -112,7 +112,13 @@ func (p *Package) writeDefs() {
}
for i, t := range p.CgoChecks {
n := p.unsafeCheckPointerNameIndex(i)
n := p.unsafeCheckPointerNameIndex(i, false)
fmt.Fprintf(fgo2, "\nfunc %s(p %s, args ...interface{}) %s {\n", n, t, t)
fmt.Fprintf(fgo2, "\treturn _cgoCheckPointer(p, args...).(%s)\n", t)
fmt.Fprintf(fgo2, "}\n")
}
for i, t := range p.DeferredCgoChecks {
n := p.unsafeCheckPointerNameIndex(i, true)
fmt.Fprintf(fgo2, "\nfunc %s(p interface{}, args ...interface{}) %s {\n", n, t)
fmt.Fprintf(fgo2, "\treturn _cgoCheckPointer(p, args...).(%s)\n", t)
fmt.Fprintf(fgo2, "}\n")
View
@@ -2855,11 +2855,6 @@ func cgen_append(n, res *Node) {
Dump("cgen_append-n", n)
Dump("cgen_append-res", res)
}
if res.Op != ONAME && !samesafeexpr(res, n.List.First()) {
Dump("cgen_append-n", n)
Dump("cgen_append-res", res)
Fatalf("append not lowered")
}
for _, n1 := range n.List.Slice() {
if n1.Ullman >= UINF {
Fatalf("append with function call arguments")
View
@@ -156,6 +156,8 @@ var Debug_typeassert int
var localpkg *Pkg // package being compiled
var autopkg *Pkg // fake package for allocating auto variables
var importpkg *Pkg // package being imported
var itabpkg *Pkg // fake pkg for itab entries
View
@@ -108,6 +108,8 @@ func Main() {
localpkg = mkpkg("")
localpkg.Prefix = "\"\""
autopkg = mkpkg("")
autopkg.Prefix = "\"\""
// pseudo-package, for scoping
builtinpkg = mkpkg("go.builtin")
View
@@ -577,6 +577,15 @@ func progeffects(prog *obj.Prog, vars []*Node, uevar bvec, varkill bvec, avarini
return
}
if prog.As == obj.AJMP && prog.To.Type == obj.TYPE_MEM && prog.To.Name == obj.NAME_EXTERN {
// This is a tail call. Ensure the arguments are still alive.
// See issue 16016.
for i, node := range vars {
if node.Class == PPARAM {
bvset(uevar, int32(i))
}
}
}
if prog.As == obj.ATEXT {
// A text instruction marks the entry point to a function and
View
@@ -164,7 +164,7 @@ func instrumentnode(np **Node, init *Nodes, wr int, skip int) {
var outn Nodes
outn.Set(out)
instrumentnode(&ls[i], &outn, 0, 0)
if ls[i].Op != OAS || ls[i].Ninit.Len() == 0 {
if ls[i].Op != OAS && ls[i].Op != OASWB && ls[i].Op != OAS2FUNC || ls[i].Ninit.Len() == 0 {
out = append(outn.Slice(), ls[i])
} else {
// Splice outn onto end of ls[i].Ninit
View
@@ -75,7 +75,7 @@ func uncommonSize(t *Type) int { // Sizeof(runtime.uncommontype{})
if t.Sym == nil && len(methods(t)) == 0 {
return 0
}
return 4 + 2 + 2
return 4 + 2 + 2 + 4 + 4
}
func makefield(name string, t *Type) *Field {
@@ -604,17 +604,19 @@ func dextratype(s *Sym, ot int, t *Type, dataAdd int) int {
ot = dgopkgpathOffLSym(Linksym(s), ot, typePkg(t))
dataAdd += 4 + 2 + 2
dataAdd += uncommonSize(t)
mcount := len(m)
if mcount != int(uint16(mcount)) {
Fatalf("too many methods on %s: %d", t, mcount)
}
if dataAdd != int(uint16(dataAdd)) {
if dataAdd != int(uint32(dataAdd)) {
Fatalf("methods are too far away on %s: %d", t, dataAdd)
}
ot = duint16(s, ot, uint16(mcount))
ot = duint16(s, ot, uint16(dataAdd))
ot = duint16(s, ot, 0)
ot = duint32(s, ot, uint32(dataAdd))
ot = duint32(s, ot, 0)
return ot
}
View
@@ -2573,6 +2573,9 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
}
i := s.expr(fn.Left)
itab := s.newValue1(ssa.OpITab, Types[TUINTPTR], i)
if k != callNormal {
s.nilCheck(itab)
}
itabidx := fn.Xoffset + 3*int64(Widthptr) + 8 // offset of fun field in runtime.itab
itab = s.newValue1I(ssa.OpOffPtr, Types[TUINTPTR], itabidx, itab)
if k == callNormal {
@@ -4378,7 +4381,7 @@ func (e *ssaExport) SplitStruct(name ssa.LocalSlot, i int) ssa.LocalSlot {
// namedAuto returns a new AUTO variable with the given name and type.
func (e *ssaExport) namedAuto(name string, typ ssa.Type) ssa.GCNode {
t := typ.(*Type)
s := Lookup(name)
s := &Sym{Name: name, Pkg: autopkg}
n := Nod(ONAME, nil, nil)
s.Def = n
s.Def.Used = true
View
@@ -1860,7 +1860,13 @@ func genwrapper(rcvr *Type, method *Field, newnam *Sym, iface int) {
dot := adddot(NodSym(OXDOT, this.Left, method.Sym))
// generate call
if !instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !isifacemethod(method.Type) {
// It's not possible to use a tail call when dynamic linking on ppc64le. The
// bad scenario is when a local call is made to the wrapper: the wrapper will
// call the implementation, which might be in a different module and so set
// the TOC to the appropriate value for that module. But if it returns
// directly to the wrapper's caller, nothing will reset it to the correct
// value for that function.
if !instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !isifacemethod(method.Type) && !(Thearch.LinkArch.Name == "ppc64le" && Ctxt.Flag_dynlink) {
// generate tail call: adjust pointer receiver and jump to embedded method.
dot = dot.Left // skip final .M
// TODO(mdempsky): Remove dependency on dotlist.
View
@@ -1143,8 +1143,8 @@ func checkCC() {
}
fatal("cannot invoke C compiler %q: %v\n\n"+
"Go needs a system C compiler for use with cgo.\n"+
"To set a C compiler, export CC=the-compiler.\n"+
"To disable cgo, export CGO_ENABLED=0.\n%s%s", defaultcc, err, outputHdr, output)
"To set a C compiler, set CC=the-compiler.\n"+
"To disable cgo, set CGO_ENABLED=0.\n%s%s", defaultcc, err, outputHdr, output)
}
}
View
@@ -42,10 +42,21 @@ func mkzdefaultcc(dir, file string) {
// mkzcgo writes zosarch.go for cmd/go.
func mkzosarch(dir, file string) {
// sort for deterministic zosarch.go file
var list []string
for plat := range cgoEnabled {
list = append(list, plat)
}
sort.Strings(list)
var buf bytes.Buffer
buf.WriteString("// auto generated by go tool dist\n\n")
buf.WriteString("package main\n\n")
fmt.Fprintf(&buf, "var osArchSupportsCgo = %#v", cgoEnabled)
fmt.Fprintf(&buf, "var osArchSupportsCgo = map[string]bool{\n")
for _, plat := range list {
fmt.Fprintf(&buf, "\t%q: %v,\n", plat, cgoEnabled[plat])
}
fmt.Fprintf(&buf, "}\n")
writefile(buf.String(), file, writeSkipSame)
}
View
@@ -465,11 +465,7 @@ func (t *tester) registerTests() {
})
}
}
if t.cgoEnabled && !t.iOS() {
// TODO(crawshaw): reenable on iOS
// golang.org/issue/15919
//
// These tests are not designed to run off the host.
if t.cgoEnabled {
t.tests = append(t.tests, distTest{
name: "cgo_test",
heading: "../misc/cgo/test",
@@ -729,16 +725,10 @@ func (t *tester) runHostTest(dirBanner, pkg string) error {
func (t *tester) cgoTest(dt *distTest) error {
env := mergeEnvLists([]string{"GOTRACEBACK=2"}, os.Environ())
if t.iOS() {
cmd := t.dirCmd("misc/cgo/test", "go", "test", t.tags())
cmd.Env = env
return cmd.Run()
}
cmd := t.addCmd(dt, "misc/cgo/test", "go", "test", t.tags(), "-ldflags", "-linkmode=auto", t.runFlag(""))
cmd.Env = env
if t.gohostos != "dragonfly" && t.gohostarch != "ppc64le" && t.goos != "android" {
if t.gohostos != "dragonfly" && t.gohostarch != "ppc64le" && t.goos != "android" && (t.goos != "darwin" || t.goarch != "arm") {
// linkmode=internal fails on dragonfly since errno is a TLS relocation.
// linkmode=internal fails on ppc64le because cmd/link doesn't
// handle the TOC correctly (issue 15409).
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -2182,7 +2182,6 @@ func mkAbs(dir, f string) string {
type toolchain interface {
// gc runs the compiler in a specific directory on a set of files
// and returns the name of the generated output file.
// The compiler runs in the directory dir.
gc(b *builder, p *Package, archive, obj string, asmhdr bool, importArgs []string, gofiles []string) (ofile string, out []byte, err error)
// cc runs the toolchain's C compiler in a directory on a C file
// to produce an output file.
@@ -2328,7 +2327,15 @@ func (gcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error {
// Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files.
inc := filepath.Join(goroot, "pkg", "include")
sfile = mkAbs(p.Dir, sfile)
args := []interface{}{buildToolExec, tool("asm"), "-o", ofile, "-trimpath", b.work, "-I", obj, "-I", inc, "-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch, buildAsmflags, sfile}
args := []interface{}{buildToolExec, tool("asm"), "-o", ofile, "-trimpath", b.work, "-I", obj, "-I", inc, "-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch, buildAsmflags}
if p.ImportPath == "runtime" && goarch == "386" {
for _, arg := range buildAsmflags {
if arg == "-dynlink" {
args = append(args, "-D=GOBUILDMODE_shared=1")
}
}
}
args = append(args, sfile)
if err := b.run(p.Dir, p.ImportPath, nil, args...); err != nil {
return err
}
View
@@ -19,8 +19,8 @@ var cmdGet = &Command{
UsageLine: "get [-d] [-f] [-fix] [-insecure] [-t] [-u] [build flags] [packages]",
Short: "download and install packages and dependencies",
Long: `
Get downloads and installs the packages named by the import paths,
along with their dependencies.
Get downloads the packages named by the import paths, along with their
dependencies. It then installs the named packages, like 'go install'.
The -d flag instructs get to stop after downloading the packages; that is,
it instructs get not to install the packages.
View
@@ -1332,15 +1332,31 @@ func TestPackageMainTestImportsArchiveNotBinary(t *testing.T) {
tg.run("test", "main_test")
}
// The runtime version string takes one of two forms:
// "go1.X[.Y]" for Go releases, and "devel +hash" at tip.
// Determine whether we are in a released copy by
// inspecting the version.
var isGoRelease = strings.HasPrefix(runtime.Version(), "go1")
// Issue 12690
func TestPackageNotStaleWithTrailingSlash(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
// Make sure the packages below are not stale.
tg.run("install", "runtime", "os", "io")
goroot := runtime.GOROOT()
tg.setenv("GOROOT", goroot+"/")
tg.wantNotStale("runtime", "", "with trailing slash in GOROOT, runtime listed as stale")
tg.wantNotStale("os", "", "with trailing slash in GOROOT, os listed as stale")
tg.wantNotStale("io", "", "with trailing slash in GOROOT, io listed as stale")
want := ""
if isGoRelease {
want = "standard package in Go release distribution"
}
tg.wantNotStale("runtime", want, "with trailing slash in GOROOT, runtime listed as stale")
tg.wantNotStale("os", want, "with trailing slash in GOROOT, os listed as stale")
tg.wantNotStale("io", want, "with trailing slash in GOROOT, io listed as stale")
}
// With $GOBIN set, binaries get installed to $GOBIN.
@@ -2342,6 +2358,11 @@ func TestGoGetRscIoToolstash(t *testing.T) {
// Issue 13037: Was not parsing <meta> tags in 404 served over HTTPS
func TestGoGetHTTPS404(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
switch runtime.GOOS {
case "darwin", "linux", "freebsd":
default:
t.Skipf("test case does not work on %s", runtime.GOOS)
}
tg := testgo(t)
defer tg.cleanup()
@@ -2877,3 +2898,25 @@ func TestBinaryOnlyPackages(t *testing.T) {
tg.run("run", tg.path("src/p3/p3.go"))
tg.grepStdout("hello from p1", "did not see message from p1")
}
// Issue 16050.
func TestAlwaysLinkSysoFiles(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.tempDir("src/syso")
tg.tempFile("src/syso/a.syso", ``)
tg.tempFile("src/syso/b.go", `package syso`)
tg.setenv("GOPATH", tg.path("."))
// We should see the .syso file regardless of the setting of
// CGO_ENABLED.
tg.setenv("CGO_ENABLED", "1")
tg.run("list", "-f", "{{.SysoFiles}}", "syso")
tg.grepStdout("a.syso", "missing syso file with CGO_ENABLED=1")
tg.setenv("CGO_ENABLED", "0")
tg.run("list", "-f", "{{.SysoFiles}}", "syso")
tg.grepStdout("a.syso", "missing syso file with CGO_ENABLED=0")
}
View
@@ -214,15 +214,7 @@ var helpTemplate = `{{if .Runnable}}usage: go {{.UsageLine}}
{{end}}{{.Long | trim}}
`
var documentationTemplate = `// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// DO NOT EDIT THIS FILE. GENERATED BY mkalldocs.sh.
// Edit the documentation in other files and rerun mkalldocs.sh to generate this one.
/*
{{range .}}{{if .Short}}{{.Short | capitalize}}
var documentationTemplate = `{{range .}}{{if .Short}}{{.Short | capitalize}}
{{end}}{{if .Runnable}}Usage:
@@ -231,9 +223,39 @@ var documentationTemplate = `// Copyright 2011 The Go Authors. All rights reserv
{{end}}{{.Long | trim}}
{{end}}*/
package main
`
{{end}}`
// commentWriter writes a Go comment to the underlying io.Writer,
// using line comment form (//).
type commentWriter struct {
W io.Writer
wroteSlashes bool // Wrote "//" at the beginning of the current line.
}
func (c *commentWriter) Write(p []byte) (int, error) {
var n int
for i, b := range p {
if !c.wroteSlashes {
s := "//"
if b != '\n' {
s = "// "
}
if _, err := io.WriteString(c.W, s); err != nil {
return n, err
}
c.wroteSlashes = true
}
n0, err := c.W.Write(p[i : i+1])
n += n0
if err != nil {
return n, err
}
if b == '\n' {
c.wroteSlashes = false
}
}
return len(p), nil
}
// An errWriter wraps a writer, recording whether a write error occurred.
type errWriter struct {
@@ -310,10 +332,18 @@ func help(args []string) {
// 'go help documentation' generates doc.go.
if arg == "documentation" {
fmt.Println("// Copyright 2011 The Go Authors. All rights reserved.")
fmt.Println("// Use of this source code is governed by a BSD-style")
fmt.Println("// license that can be found in the LICENSE file.")
fmt.Println()
fmt.Println("// DO NOT EDIT THIS FILE. GENERATED BY mkalldocs.sh.")
fmt.Println("// Edit the documentation in other files and rerun mkalldocs.sh to generate this one.")
fmt.Println()
buf := new(bytes.Buffer)
printUsage(buf)
usage := &Command{Long: buf.String()}
tmpl(os.Stdout, documentationTemplate, append([]*Command{usage}, commands...))
tmpl(&commentWriter{W: os.Stdout}, documentationTemplate, append([]*Command{usage}, commands...))
fmt.Println("package main")
return
}
View
@@ -6,7 +6,6 @@
set -e
go build -o go.latest
./go.latest help documentation | sed 's; \*/; * /;' >alldocs.go
./go.latest help documentation >alldocs.go
gofmt -w alldocs.go
rm go.latest
View
@@ -1022,9 +1022,10 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
p.MFiles = nil
p.SwigFiles = nil
p.SwigCXXFiles = nil
p.SysoFiles = nil
// Note that SFiles are okay (they go to the Go assembler)
// and HFiles are okay (they might be used by the SFiles).
// Also Sysofiles are okay (they might not contain object
// code; see issue #16050).
}
// The gc toolchain only permits C source files with cgo.
View
@@ -129,7 +129,7 @@ control the execution of any test:
const testFlag2 = `
-bench regexp
Run (sub)benchmarks matching a regular expression.
The given regular expression is split into smaller ones by
The given regular expression is split into smaller ones by
top-level '/', where each must match the corresponding part of a
benchmark's identifier.
By default, no benchmarks run. To run all benchmarks,
@@ -221,7 +221,7 @@ const testFlag2 = `
-run regexp
Run only those tests and examples matching the regular expression.
For tests the regular expression is split into smaller ones by
top-level '/', where each must match the corresponding part of a
top-level '/', where each must match the corresponding part of a
test's identifier.
-short
@@ -263,7 +263,7 @@ execution, not to the test itself.)
The test flags that generate profiles (other than for coverage) also
leave the test binary in pkg.test for use when analyzing the profiles.
When 'go test' runs a test binary, it does so from within the
When 'go test' runs a test binary, it does so from within the
corresponding package's source code directory. Depending on the test,
it may be necessary to do the same when invoking a generated test
binary directly.
View
@@ -779,15 +779,31 @@ type metaImport struct {
// errNoMatch is returned from matchGoImport when there's no applicable match.
var errNoMatch = errors.New("no import match")
func splitPathHasPrefix(path, prefix []string) bool {
if len(path) < len(prefix) {
return false
}
for i, p := range prefix {
if path[i] != p {
return false
}
}
return true
}
// matchGoImport returns the metaImport from imports matching importPath.
// An error is returned if there are multiple matches.
// errNoMatch is returned if none match.
func matchGoImport(imports []metaImport, importPath string) (_ metaImport, err error) {
match := -1
imp := strings.Split(importPath, "/")
for i, im := range imports {
if !strings.HasPrefix(importPath, im.Prefix) {
pre := strings.Split(im.Prefix, "/")
if !splitPathHasPrefix(imp, pre) {
continue
}
if match != -1 {
err = fmt.Errorf("multiple meta tags match import path %q", importPath)
return
View
@@ -5,6 +5,7 @@
package main
import (
"errors"
"internal/testenv"
"io/ioutil"
"os"
@@ -227,3 +228,96 @@ func TestIsSecure(t *testing.T) {
}
}
}
func TestMatchGoImport(t *testing.T) {
tests := []struct {
imports []metaImport
path string
mi metaImport
err error
}{
{
imports: []metaImport{
{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
},
path: "example.com/user/foo",
mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
},
{
imports: []metaImport{
{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
},
path: "example.com/user/foo/",
mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
},
{
imports: []metaImport{
{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
{Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"},
},
path: "example.com/user/foo",
mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
},
{
imports: []metaImport{
{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
{Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"},
},
path: "example.com/user/fooa",
mi: metaImport{Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"},
},
{
imports: []metaImport{
{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
{Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
},
path: "example.com/user/foo/bar",
err: errors.New("should not be allowed to create nested repo"),
},
{
imports: []metaImport{
{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
{Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
},
path: "example.com/user/foo/bar/baz",
err: errors.New("should not be allowed to create nested repo"),
},
{
imports: []metaImport{
{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
{Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
},
path: "example.com/user/foo/bar/baz/qux",
err: errors.New("should not be allowed to create nested repo"),
},
{
imports: []metaImport{
{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
{Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
},
path: "example.com/user/foo/bar/baz/",
err: errors.New("should not be allowed to create nested repo"),
},
{
imports: []metaImport{
{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
{Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
},
path: "example.com",
err: errors.New("pathologically short path"),
},
}
for _, test := range tests {
mi, err := matchGoImport(test.imports, test.path)
if mi != test.mi {
t.Errorf("unexpected metaImport; got %v, want %v", mi, test.mi)
}
got := err
want := test.err
if (got == nil) != (want == nil) {
t.Errorf("unexpected error; got %v, want %v", got, want)
}
}
}
View
@@ -1986,16 +1986,6 @@ func span6(ctxt *obj.Link, s *obj.LSym) {
c = naclpad(ctxt, s, c, -c&31)
}
// Pad functions with trap instruction, to catch invalid jumps
if c&(FuncAlign-1) != 0 {
v := -c & (FuncAlign - 1)
s.Grow(int64(c) + int64(v))
for i := c; i < c+v; i++ {
// 0xCC is INT $3 - breakpoint instruction
s.P[i] = uint8(0xCC)
}
c += v
}
s.Size = int64(c)
if false { /* debug['a'] > 1 */
View
@@ -1092,6 +1092,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *ob
call.Mode = ctxt.Cursym.Text.Mode
call.As = obj.ACALL
call.To.Type = obj.TYPE_BRANCH
call.To.Name = obj.NAME_EXTERN
morestack := "runtime.morestack"
switch {
case ctxt.Cursym.Cfunc:
@@ -1100,8 +1101,17 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *ob
morestack = "runtime.morestack_noctxt"
}
call.To.Sym = obj.Linklookup(ctxt, morestack, 0)
// When compiling 386 code for dynamic linking, the call needs to be adjusted
// to follow PIC rules. This in turn can insert more instructions, so we need
// to keep track of the start of the call (where the jump will be to) and the
// end (which following instructions are appended to).
callend := call
progedit(ctxt, callend)
for ; callend.Link != nil; callend = callend.Link {
progedit(ctxt, callend.Link)
}
jmp := obj.Appendp(ctxt, call)
jmp := obj.Appendp(ctxt, callend)
jmp.As = obj.AJMP
jmp.To.Type = obj.TYPE_BRANCH
jmp.Pcond = ctxt.Cursym.Text.Link
View
@@ -615,7 +615,8 @@ func asmb() {
sect := ld.Segtext.Sect
ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
ld.Codeblk(int64(sect.Vaddr), int64(sect.Length))
// 0xCC is INT $3 - breakpoint instruction
ld.CodeblkPad(int64(sect.Vaddr), int64(sect.Length), []byte{0xCC})
for sect = sect.Next; sect != nil; sect = sect.Next {
ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
ld.Datblk(int64(sect.Vaddr), int64(sect.Length))
View
@@ -790,11 +790,14 @@ func blk(start *LSym, addr int64, size int64) {
}
func Codeblk(addr int64, size int64) {
CodeblkPad(addr, size, zeros[:])
}
func CodeblkPad(addr int64, size int64, pad []byte) {
if Debug['a'] != 0 {
fmt.Fprintf(Bso, "codeblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos())
}
blkSlice(Ctxt.Textp, addr, size)
blkSlice(Ctxt.Textp, addr, size, pad)
/* again for printing */
if Debug['a'] == 0 {
@@ -858,7 +861,7 @@ func Codeblk(addr int64, size int64) {
// blkSlice is a variant of blk that processes slices.
// After text symbols are converted from a linked list to a slice,
// delete blk and give this function its name.
func blkSlice(syms []*LSym, addr, size int64) {
func blkSlice(syms []*LSym, addr, size int64, pad []byte) {
for i, s := range syms {
if s.Type&obj.SSUB == 0 && s.Value >= addr {
syms = syms[i:]
@@ -880,13 +883,13 @@ func blkSlice(syms []*LSym, addr, size int64) {
errorexit()
}
if addr < s.Value {
strnput("", int(s.Value-addr))
strnputPad("", int(s.Value-addr), pad)
addr = s.Value
}
Cwrite(s.P)
addr += int64(len(s.P))
if addr < s.Value+s.Size {
strnput("", int(s.Value+s.Size-addr))
strnputPad("", int(s.Value+s.Size-addr), pad)
addr = s.Value + s.Size
}
if addr != s.Value+s.Size {
@@ -899,7 +902,7 @@ func blkSlice(syms []*LSym, addr, size int64) {
}
if addr < eaddr {
strnput("", int(eaddr-addr))
strnputPad("", int(eaddr-addr), pad)
}
Cflush()
}
@@ -909,7 +912,7 @@ func Datblk(addr int64, size int64) {
fmt.Fprintf(Bso, "datblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos())
}
blkSlice(datap, addr, size)
blkSlice(datap, addr, size, zeros[:])
/* again for printing */
if Debug['a'] == 0 {
@@ -986,23 +989,27 @@ func Dwarfblk(addr int64, size int64) {
var zeros [512]byte
// strnput writes the first n bytes of s.
// If n is larger then len(s),
// If n is larger than len(s),
// it is padded with NUL bytes.
func strnput(s string, n int) {
strnputPad(s, n, zeros[:])
}
// strnput writes the first n bytes of s.
// If n is larger than len(s),
// it is padded with the bytes in pad (repeated as needed).
func strnputPad(s string, n int, pad []byte) {
if len(s) >= n {
Cwritestring(s[:n])
} else {
Cwritestring(s)
n -= len(s)
for n > 0 {
if len(zeros) >= n {
Cwrite(zeros[:n])
return
} else {
Cwrite(zeros[:])
n -= len(zeros)
}
for n > len(pad) {
Cwrite(pad)
n -= len(pad)
}
Cwrite(pad[:n])
}
}
View
@@ -61,7 +61,7 @@ func decode_inuxi(p []byte, sz int) uint64 {
func commonsize() int { return 4*SysArch.PtrSize + 8 + 8 } // runtime._type
func structfieldSize() int { return 3 * SysArch.PtrSize } // runtime.structfield
func uncommonSize() int { return 4 + 2 + 2 } // runtime.uncommontype
func uncommonSize() int { return 4 + 2 + 2 + 4 + 4 } // runtime.uncommontype
// Type.commonType.kind
func decodetype_kind(s *LSym) uint8 {
@@ -362,7 +362,7 @@ func decodetype_methods(s *LSym) []methodsig {
}
mcount := int(decode_inuxi(s.P[off+4:], 2))
moff := int(decode_inuxi(s.P[off+4+2:], 2))
moff := int(decode_inuxi(s.P[off+4+2+2:], 4))
off += moff // offset to array of reflect.method values
const sizeofMethod = 4 * 4 // sizeof reflect.method in program
return decode_methodsig(s, off, sizeofMethod, mcount)
View
@@ -529,7 +529,7 @@ func walktypedef(die *DWDie) *DWDie {
}
func walksymtypedef(s *LSym) *LSym {
if t := Linkrlookup(Ctxt, s.Name+".def", int(s.Version)); t != nil {
if t := Linkrlookup(Ctxt, s.Name+"..def", int(s.Version)); t != nil {
return t
}
return s
@@ -819,7 +819,7 @@ func dotypedef(parent *DWDie, name string, def *DWDie) {
Diag("dwarf: bad def in dotypedef")
}
def.sym = Linklookup(Ctxt, def.sym.Name+".def", 0)
def.sym = Linklookup(Ctxt, def.sym.Name+"..def", 0)
def.sym.Attr |= AttrHidden
def.sym.Type = obj.SDWARFINFO
@@ -1021,7 +1021,7 @@ func newtype(gotype *LSym) *DWDie {
}
func nameFromDIESym(dwtype *LSym) string {
return strings.TrimSuffix(dwtype.Name[len(infoprefix):], ".def")
return strings.TrimSuffix(dwtype.Name[len(infoprefix):], "..def")
}
// Find or construct *T given T.
View
@@ -609,7 +609,8 @@ func asmb() {
sect := ld.Segtext.Sect
ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
ld.Codeblk(int64(sect.Vaddr), int64(sect.Length))
// 0xCC is INT $3 - breakpoint instruction
ld.CodeblkPad(int64(sect.Vaddr), int64(sect.Length), []byte{0xCC})
for sect = sect.Next; sect != nil; sect = sect.Next {
ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
ld.Datblk(int64(sect.Vaddr), int64(sect.Length))
View
@@ -7,9 +7,17 @@
// and between processes.
//
// Incoming requests to a server should create a Context, and outgoing calls to
// servers should accept a Context. The chain of function calls between must
// propagate the Context, optionally replacing it with a modified copy created
// using WithDeadline, WithTimeout, WithCancel, or WithValue.
// servers should accept a Context. The chain of function calls between them
// must propagate the Context, optionally replacing it with a derived Context
// created using WithCancel, WithDeadline, WithTimeout, or WithValue. These
// Context values form a tree: when a Context is canceled, all Contexts derived
// from it are also canceled.
//
// The WithCancel, WithDeadline, and WithTimeout functions return a derived
// Context and a CancelFunc. Calling the CancelFunc cancels the new Context and
// any Contexts derived from it, removes the Context from the parent's tree, and
// stops any associated timers. Failing to call the CancelFunc leaks the
// associated resources until the parent Context is canceled or the timer fires.
//
// Programs that use Contexts should follow these rules to keep interfaces
// consistent across packages and enable static analysis tools to check context
View
@@ -6,7 +6,7 @@
// code but require careful thought to use correctly.
package subtle
// ConstantTimeCompare returns 1 iff the two slices, x
// ConstantTimeCompare returns 1 if and only if the two slices, x
// and y, have equal contents. The time taken is a function of the length of
// the slices and is independent of the contents.
func ConstantTimeCompare(x, y []byte) int {
View
@@ -1796,6 +1796,9 @@ var oidExtensionRequest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 14}
func newRawAttributes(attributes []pkix.AttributeTypeAndValueSET) ([]asn1.RawValue, error) {
var rawAttributes []asn1.RawValue
b, err := asn1.Marshal(attributes)
if err != nil {
return nil, err
}
rest, err := asn1.Unmarshal(b, &rawAttributes)
if err != nil {
return nil, err
View
@@ -86,34 +86,35 @@ var (
// The exported fields can be changed to customize the details before the
// first call to Read or ReadAll.
//
// Comma is the field delimiter. It defaults to ','.
//
// Comment, if not 0, is the comment character. Lines beginning with the
// Comment character are ignored.
//
// If FieldsPerRecord is positive, Read requires each record to
// have the given number of fields. If FieldsPerRecord is 0, Read sets it to
// the number of fields in the first record, so that future records must
// have the same field count. If FieldsPerRecord is negative, no check is
// made and records may have a variable number of fields.
//
// If LazyQuotes is true, a quote may appear in an unquoted field and a
// non-doubled quote may appear in a quoted field.
//
// If TrimLeadingSpace is true, leading white space in a field is ignored.
// If the field delimiter is white space, TrimLeadingSpace will trim the
// delimiter.
type Reader struct {
Comma rune // field delimiter (set to ',' by NewReader)
Comment rune // comment character for start of line
FieldsPerRecord int // number of expected fields per record
LazyQuotes bool // allow lazy quotes
TrailingComma bool // ignored; here for backwards compatibility
TrimLeadingSpace bool // trim leading space
line int
column int
r *bufio.Reader
field bytes.Buffer
// Comma is the field delimiter.
// It is set to comma (',') by NewReader.
Comma rune
// Comment, if not 0, is the comment character. Lines beginning with the
// Comment character without preceding whitespace are ignored.
// With leading whitespace the Comment character becomes part of the
// field, even if TrimLeadingSpace is true.
Comment rune
// FieldsPerRecord is the number of expected fields per record.
// If FieldsPerRecord is positive, Read requires each record to
// have the given number of fields. If FieldsPerRecord is 0, Read sets it to
// the number of fields in the first record, so that future records must
// have the same field count. If FieldsPerRecord is negative, no check is
// made and records may have a variable number of fields.
FieldsPerRecord int
// If LazyQuotes is true, a quote may appear in an unquoted field and a
// non-doubled quote may appear in a quoted field.
LazyQuotes bool
TrailingComma bool // ignored; here for backwards compatibility
// If TrimLeadingSpace is true, leading white space in a field is ignored.
// This is done even if the field delimiter, Comma, is white space.
TrimLeadingSpace bool
line int
column int
r *bufio.Reader
field bytes.Buffer
}
// NewReader returns a new Reader that reads from r.
View
@@ -90,8 +90,8 @@ import (
// Int64String int64 `json:",string"`
//
// The key name will be used if it's a non-empty string consisting of
// only Unicode letters, digits, dollar signs, percent signs, hyphens,
// underscores and slashes.
// only Unicode letters, digits, and ASCII punctuation except quotation
// marks, backslash, and comma.
//
// Anonymous struct fields are usually marshaled as if their inner exported fields
// were fields in the outer struct, subject to the usual Go visibility rules amended
View
@@ -418,7 +418,7 @@ type (
)
// Pos and End implementations for expression/type nodes.
//
func (x *BadExpr) Pos() token.Pos { return x.From }
func (x *Ident) Pos() token.Pos { return x.NamePos }
func (x *Ellipsis) Pos() token.Pos { return x.Ellipsis }
@@ -709,7 +709,7 @@ type (
)
// Pos and End implementations for statement nodes.
//
func (s *BadStmt) Pos() token.Pos { return s.From }
func (s *DeclStmt) Pos() token.Pos { return s.Decl.Pos() }
func (s *EmptyStmt) Pos() token.Pos { return s.Semicolon }
@@ -854,7 +854,7 @@ type (
)
// Pos and End implementations for spec nodes.
//
func (s *ImportSpec) Pos() token.Pos {
if s.Name != nil {
return s.Name.Pos()
@@ -931,7 +931,7 @@ type (
)
// Pos and End implementations for declaration nodes.
//
func (d *BadDecl) Pos() token.Pos { return d.From }
func (d *GenDecl) Pos() token.Pos { return d.TokPos }
func (d *FuncDecl) Pos() token.Pos { return d.Type.Pos() }
View
@@ -73,7 +73,7 @@ const (
//
// The mode parameter controls the amount of source text parsed and other
// optional parser functionality. Position information is recorded in the
// file set fset.
// file set fset, which must not be nil.
//
// If the source couldn't be read, the returned AST is nil and the error
// indicates the specific failure. If the source was read but syntax
@@ -82,6 +82,10 @@ const (
// are returned via a scanner.ErrorList which is sorted by file position.
//
func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode) (f *ast.File, err error) {
if fset == nil {
panic("parser.ParseFile: no token.FileSet provided (fset == nil)")
}
// get source
text, err := readSource(filename, src)
if err != nil {
@@ -125,7 +129,8 @@ func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode)
//
// If filter != nil, only the files with os.FileInfo entries passing through
// the filter (and ending in ".go") are considered. The mode bits are passed
// to ParseFile unchanged. Position information is recorded in fset.
// to ParseFile unchanged. Position information is recorded in fset, which
// must not be nil.
//
// If the directory couldn't be read, a nil map and the respective error are
// returned. If a parse error occurred, a non-nil but incomplete map and the
@@ -169,9 +174,14 @@ func ParseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool, m
// ParseExprFrom is a convenience function for parsing an expression.
// The arguments have the same meaning as for Parse, but the source must
// be a valid Go (type or value) expression.
// be a valid Go (type or value) expression. Specifically, fset must not
// be nil.
//
func ParseExprFrom(fset *token.FileSet, filename string, src interface{}, mode Mode) (ast.Expr, error) {
if fset == nil {
panic("parser.ParseExprFrom: no token.FileSet provided (fset == nil)")
}
// get source
text, err := readSource(filename, src)
if err != nil {
View
@@ -60,21 +60,34 @@ const (
// Parse parses, post-processes and verifies the trace.
func Parse(r io.Reader, bin string) ([]*Event, error) {
ver, rawEvents, strings, err := readTrace(r)
ver, events, err := parse(r, bin)
if err != nil {
return nil, err
}
if ver < 1007 && bin == "" {
return nil, fmt.Errorf("for traces produced by go 1.6 or below, the binary argument must be provided")
}
return events, nil
}
// parse parses, post-processes and verifies the trace. It returns the
// trace version and the list of events.
func parse(r io.Reader, bin string) (int, []*Event, error) {
ver, rawEvents, strings, err := readTrace(r)
if err != nil {
return 0, nil, err
}
events, stacks, err := parseEvents(ver, rawEvents, strings)
if err != nil {
return nil, err
return 0, nil, err
}
events, err = removeFutile(events)
if err != nil {
return nil, err
return 0, nil, err
}
err = postProcessTrace(ver, events)
if err != nil {
return nil, err
return 0, nil, err
}
// Attach stack traces.
for _, ev := range events {
@@ -84,10 +97,10 @@ func Parse(r io.Reader, bin string) ([]*Event, error) {
}
if ver < 1007 && bin != "" {
if err := symbolize(events, bin); err != nil {
return nil, err
return 0, nil, err
}
}
return events, nil
return ver, events, nil
}
// rawEvent is a helper type used during parsing.
View
@@ -42,7 +42,9 @@ func TestParseCanned(t *testing.T) {
if err != nil {
t.Fatalf("failed to read input file: %v", err)
}
_, err = Parse(bytes.NewReader(data), "")
// Instead of Parse that requires a proper binary name for old traces,
// we use 'parse' that omits symbol lookup if an empty string is given.
_, _, err = parse(bytes.NewReader(data), "")
switch {
case strings.HasSuffix(f.Name(), "_good"):
if err != nil {
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View
@@ -275,9 +275,9 @@ type Request struct {
//
// For outgoing client requests, the context controls cancelation.
//
// For incoming server requests, the context is canceled when either
// the client's connection closes, or when the ServeHTTP method
// returns.
// For incoming server requests, the context is canceled when the
// ServeHTTP method returns. For its associated values, see
// ServerContextKey and LocalAddrContextKey.
func (r *Request) Context() context.Context {
if r.ctx != nil {
return r.ctx
View
@@ -9,6 +9,7 @@ package http_test
import (
"bufio"
"bytes"
"compress/gzip"
"context"
"crypto/tls"
"encoding/json"
@@ -1996,6 +1997,26 @@ func TestTimeoutHandlerStartTimerWhenServing(t *testing.T) {
}
}
// https://golang.org/issue/15948
func TestTimeoutHandlerEmptyResponse(t *testing.T) {
defer afterTest(t)
var handler HandlerFunc = func(w ResponseWriter, _ *Request) {
// No response.
}
timeout := 300 * time.Millisecond
ts := httptest.NewServer(TimeoutHandler(handler, timeout, ""))
defer ts.Close()
res, err := Get(ts.URL)
if err != nil {
t.Fatal(err)
}
defer res.Body.Close()
if res.StatusCode != StatusOK {
t.Errorf("got res.StatusCode %d, want %v", res.StatusCode, StatusOK)
}
}
// Verifies we don't path.Clean() on the wrong parts in redirects.
func TestRedirectMunging(t *testing.T) {
req, _ := NewRequest("GET", "http://example.com/", nil)
@@ -4166,6 +4187,38 @@ func testServerContext_ServerContextKey(t *testing.T, h2 bool) {
res.Body.Close()
}
// https://golang.org/issue/15960
func TestHandlerSetTransferEncodingChunked(t *testing.T) {
defer afterTest(t)
ht := newHandlerTest(HandlerFunc(func(w ResponseWriter, r *Request) {
w.Header().Set("Transfer-Encoding", "chunked")
w.Write([]byte("hello"))
}))
resp := ht.rawResponse("GET / HTTP/1.1\nHost: foo")
const hdr = "Transfer-Encoding: chunked"
if n := strings.Count(resp, hdr); n != 1 {
t.Errorf("want 1 occurrence of %q in response, got %v\nresponse: %v", hdr, n, resp)
}
}
// https://golang.org/issue/16063
func TestHandlerSetTransferEncodingGzip(t *testing.T) {
defer afterTest(t)
ht := newHandlerTest(HandlerFunc(func(w ResponseWriter, r *Request) {
w.Header().Set("Transfer-Encoding", "gzip")
gz := gzip.NewWriter(w)
gz.Write([]byte("hello"))
gz.Close()
}))
resp := ht.rawResponse("GET / HTTP/1.1\nHost: foo")
for _, v := range []string{"gzip", "chunked"} {
hdr := "Transfer-Encoding: " + v
if n := strings.Count(resp, hdr); n != 1 {
t.Errorf("want 1 occurrence of %q in response, got %v\nresponse: %v", hdr, n, resp)
}
}
}
func BenchmarkClientServer(b *testing.B) {
b.ReportAllocs()
b.StopTimer()
View
@@ -1147,6 +1147,10 @@ func (cw *chunkWriter) writeHeader(p []byte) {
// to avoid closing the connection at EOF.
cw.chunking = true
setHeader.transferEncoding = "chunked"
if hasTE && te == "chunked" {
// We will send the chunked Transfer-Encoding header later.
delHeader("Transfer-Encoding")
}
}
} else {
// HTTP version < 1.1: cannot do chunked transfer
@@ -2463,6 +2467,9 @@ func (h *timeoutHandler) ServeHTTP(w ResponseWriter, r *Request) {
for k, vv := range tw.h {
dst[k] = vv
}
if !tw.wroteHeader {
tw.code = StatusOK
}
w.WriteHeader(tw.code)
w.Write(tw.wbuf.Bytes())
if t != nil {
View
@@ -453,6 +453,10 @@ func TestLookupDotsWithLocalSource(t *testing.T) {
t.Skip("IPv4 is required")
}
if testenv.Builder() == "" {
testenv.MustHaveExternalNetwork(t)
}
for i, fn := range []func() func(){forceGoDNS, forceCgoDNS} {
fixup := fn()
if fixup == nil {
View
@@ -5,6 +5,7 @@
package net
import (
"context"
"fmt"
"internal/testenv"
"io"
@@ -164,10 +165,13 @@ func TestAcceptTimeout(t *testing.T) {
}
defer ln.Close()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for i, tt := range acceptTimeoutTests {
if tt.timeout < 0 {
go func() {
c, err := Dial(ln.Addr().Network(), ln.Addr().String())
var d Dialer
c, err := d.DialContext(ctx, ln.Addr().Network(), ln.Addr().String())
if err != nil {
t.Error(err)
return
View
@@ -6,15 +6,17 @@ package os
import (
"runtime"
"sync"
"sync/atomic"
"syscall"
)
// Process stores the information about a process created by StartProcess.
type Process struct {
Pid int
handle uintptr // handle is accessed atomically on Windows
isdone uint32 // process has been successfully waited on, non zero if true
handle uintptr // handle is accessed atomically on Windows
isdone uint32 // process has been successfully waited on, non zero if true
sigMu sync.RWMutex // avoid race between wait and signal
}
func newProcess(pid int, handle uintptr) *Process {
View
@@ -17,6 +17,22 @@ func (p *Process) wait() (ps *ProcessState, err error) {
if p.Pid == -1 {
return nil, syscall.EINVAL
}
// If we can block until Wait4 will succeed immediately, do so.
ready, err := p.blockUntilWaitable()
if err != nil {
return nil, err
}
if ready {
// Mark the process done now, before the call to Wait4,
// so that Process.signal will not send a signal.
p.setDone()
// Acquire a write lock on sigMu to wait for any
// active call to the signal method to complete.
p.sigMu.Lock()
p.sigMu.Unlock()
}
var status syscall.WaitStatus
var rusage syscall.Rusage
pid1, e := syscall.Wait4(p.Pid, &status, 0, &rusage)
@@ -43,6 +59,8 @@ func (p *Process) signal(sig Signal) error {
if p.Pid == 0 {
return errors.New("os: process not initialized")
}
p.sigMu.RLock()
defer p.sigMu.RUnlock()
if p.done() {
return errFinished
}
View
@@ -46,6 +46,10 @@ func (f *File) Name() string { return f.name }
// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
// standard output, and standard error file descriptors.
//
// Note that the Go runtime writes to standard error for panics and crashes;
// closing Stderr may cause those messages to go elsewhere, perhaps
// to a file opened later.
var (
Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
View
@@ -0,0 +1,16 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build dragonfly nacl netbsd openbsd solaris
package os
// blockUntilWaitable attempts to block until a call to p.Wait will
// succeed immediately, and returns whether it has done so.
// It does not actually call p.Wait.
// This version is used on systems that do not implement waitid,
// or where we have not implemented it yet.
func (p *Process) blockUntilWaitable() (bool, error) {
return false, nil
}
View
@@ -0,0 +1,40 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build freebsd
package os
import (
"runtime"
"syscall"
)
const _P_PID = 0
// blockUntilWaitable attempts to block until a call to p.Wait will
// succeed immediately, and returns whether it has done so.
// It does not actually call p.Wait.
func (p *Process) blockUntilWaitable() (bool, error) {
var errno syscall.Errno
// The arguments on 32-bit FreeBSD look like the following:
// - freebsd32_wait6_args{ idtype, id1, id2, status, options, wrusage, info } or
// - freebsd32_wait6_args{ idtype, pad, id1, id2, status, options, wrusage, info } when PAD64_REQUIRED=1 on ARM, MIPS or PowerPC
if runtime.GOARCH == "386" {
_, _, errno = syscall.Syscall9(syscall.SYS_WAIT6, _P_PID, uintptr(p.Pid), 0, 0, syscall.WEXITED|syscall.WNOWAIT, 0, 0, 0, 0)
} else if runtime.GOARCH == "arm" {
_, _, errno = syscall.Syscall9(syscall.SYS_WAIT6, _P_PID, 0, uintptr(p.Pid), 0, 0, syscall.WEXITED|syscall.WNOWAIT, 0, 0, 0)
} else {
_, _, errno = syscall.Syscall6(syscall.SYS_WAIT6, _P_PID, uintptr(p.Pid), 0, syscall.WEXITED|syscall.WNOWAIT, 0, 0)
}
if errno != 0 {
// The wait6 system call is supported only on FreeBSD
// 9.3 and above, so it may return an ENOSYS error.
if errno == syscall.ENOSYS {
return false, nil
}
return false, NewSyscallError("wait6", errno)
}
return true, nil
}
View
@@ -0,0 +1,34 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin linux
package os
import (
"runtime"
"syscall"
"unsafe"
)
const _P_PID = 1
// blockUntilWaitable attempts to block until a call to p.Wait will
// succeed immediately, and returns whether it has done so.
// It does not actually call p.Wait.
func (p *Process) blockUntilWaitable() (bool, error) {
// The waitid system call expects a pointer to a siginfo_t,
// which is 128 bytes on all GNU/Linux systems.
// On Darwin, it requires greater than or equal to 64 bytes
// for darwin/{386,arm} and 104 bytes for darwin/amd64.
// We don't care about the values it returns.
var siginfo [128]byte
psig := &siginfo[0]
_, _, e := syscall.Syscall6(syscall.SYS_WAITID, _P_PID, uintptr(p.Pid), uintptr(unsafe.Pointer(psig)), syscall.WEXITED|syscall.WNOWAIT, 0, 0)
runtime.KeepAlive(psig)
if e != 0 {
return false, NewSyscallError("waitid", e)
}
return true, nil
}
View
@@ -3934,6 +3934,10 @@ func TestStructOf(t *testing.T) {
if s != want {
t.Errorf("constructed struct = %s, want %s", s, want)
}
const stStr = `struct { S string "s"; X uint8 "x"; Y uint64; Z [3]uint16 }`
if got, want := st.String(), stStr; got != want {
t.Errorf("StructOf(fields).String()=%q, want %q", got, want)
}
// check the size, alignment and field offsets
stt := TypeOf(struct {
View
@@ -313,7 +313,9 @@ type method struct {
type uncommonType struct {
pkgPath nameOff // import path; empty for built-in types like int, string
mcount uint16 // number of methods
moff uint16 // offset from this uncommontype to [mcount]method
_ uint16 // unused
moff uint32 // offset from this uncommontype to [mcount]method
_ uint32 // unused
}
// ChanDir represents a channel type's direction.
@@ -2351,6 +2353,9 @@ type structTypeFixed32 struct {
// StructOf returns the struct type containing fields.
// The Offset and Index fields are ignored and computed as they would be
// by the compiler.
//
// StructOf currently does not generate wrapper methods for embedded fields.
// This limitation may be lifted in a future version.
func StructOf(fields []StructField) Type {
var (
hash = fnv1(0, []byte("struct {")...)
@@ -2581,7 +2586,7 @@ func StructOf(fields []StructField) Type {
panic("reflect.StructOf: too many methods")
}
ut.mcount = uint16(len(methods))
ut.moff = uint16(unsafe.Sizeof(uncommonType{}))
ut.moff = uint32(unsafe.Sizeof(uncommonType{}))
if len(fs) > 0 {
repr = append(repr, ' ')
@@ -2637,6 +2642,7 @@ func StructOf(fields []StructField) Type {
}
typ.str = resolveReflectName(newName(str, "", "", false))
typ.tflag = 0
typ.hash = hash
typ.size = size
typ.align = typalign
View
@@ -174,7 +174,7 @@ DATA bad_proc_msg<>+0x00(SB)/8, $"This pro"
DATA bad_proc_msg<>+0x08(SB)/8, $"gram can"
DATA bad_proc_msg<>+0x10(SB)/8, $" only be"
DATA bad_proc_msg<>+0x18(SB)/8, $" run on "
DATA bad_proc_msg<>+0x20(SB)/8, $"processe"
DATA bad_proc_msg<>+0x20(SB)/8, $"processo"
DATA bad_proc_msg<>+0x28(SB)/8, $"rs with "
DATA bad_proc_msg<>+0x30(SB)/8, $"MMX supp"
DATA bad_proc_msg<>+0x38(SB)/4, $"ort."
@@ -539,13 +539,20 @@ TEXT ·publicationBarrier(SB),NOSPLIT,$0-0
// void jmpdefer(fn, sp);
// called from deferreturn.
// 1. pop the caller
// 2. sub 5 bytes from the callers return
// 2. sub 5 bytes (the length of CALL & a 32 bit displacement) from the callers
// return (when building for shared libraries, subtract 16 bytes -- 5 bytes
// for CALL & displacement to call __x86.get_pc_thunk.cx, 6 bytes for the
// LEAL to load the offset into BX, and finally 5 for the call & displacement)
// 3. jmp to the argument
TEXT runtime·jmpdefer(SB), NOSPLIT, $0-8
MOVL fv+0(FP), DX // fn
MOVL argp+4(FP), BX // caller sp
LEAL -4(BX), SP // caller sp after CALL
#ifdef GOBUILDMODE_shared
SUBL $16, (SP) // return to CALL again
#else
SUBL $5, (SP) // return to CALL again
#endif
MOVL 0(DX), BX
JMP BX // but first run the deferred function
View
@@ -914,8 +914,9 @@ aes0to15:
MOVQ $masks<>(SB), AX
PAND (AX)(CX*8), X1
final1:
AESENC X0, X1 // scramble input, xor in seed
AESENC X1, X1 // scramble combo 2 times
PXOR X0, X1 // xor data with seed
AESENC X1, X1 // scramble combo 3 times
AESENC X1, X1
AESENC X1, X1
MOVQ X1, (DX)
RET
@@ -949,9 +950,13 @@ aes17to32:
MOVOU (AX), X2
MOVOU -16(AX)(CX*1), X3
// xor with seed
PXOR X0, X2
PXOR X1, X3
// scramble 3 times
AESENC X0, X2
AESENC X1, X3
AESENC X2, X2
AESENC X3, X3
AESENC X2, X2
AESENC X3, X3
AESENC X2, X2
@@ -977,11 +982,16 @@ aes33to64:
MOVOU 16(AX), X5
MOVOU -32(AX)(CX*1), X6
MOVOU -16(AX)(CX*1), X7
PXOR X0, X4
PXOR X1, X5
PXOR X2, X6
PXOR X3, X7
AESENC X0, X4
AESENC X1, X5
AESENC X2, X6
AESENC X3, X7
AESENC X4, X4
AESENC X5, X5
AESENC X6, X6
AESENC X7, X7
AESENC X4, X4
AESENC X5, X5
@@ -1032,17 +1042,17 @@ aes65to128:
MOVOU -32(AX)(CX*1), X14
MOVOU -16(AX)(CX*1), X15
// scramble data, xor in seed
AESENC X0, X8
AESENC X1, X9
AESENC X2, X10
AESENC X3, X11
AESENC X4, X12
AESENC X5, X13
AESENC X6, X14
AESENC X7, X15
// xor with seed
PXOR X0, X8
PXOR X1, X9
PXOR X2, X10
PXOR X3, X11
PXOR X4, X12
PXOR X5, X13
PXOR X6, X14
PXOR X7, X15
// scramble twice
// scramble 3 times
AESENC X8, X8
AESENC X9, X9
AESENC X10, X10
@@ -1051,7 +1061,16 @@ aes65to128:
AESENC X13, X13
AESENC X14, X14
AESENC X15, X15
AESENC X8, X8
AESENC X9, X9
AESENC X10, X10
AESENC X11, X11
AESENC X12, X12
AESENC X13, X13
AESENC X14, X14
AESENC X15, X15
AESENC X8, X8
AESENC X9, X9
AESENC X10, X10
@@ -1105,21 +1124,31 @@ aes129plus:
MOVOU -32(AX)(CX*1), X14
MOVOU -16(AX)(CX*1), X15
// scramble input once, xor in seed
AESENC X0, X8
AESENC X1, X9
AESENC X2, X10
AESENC X3, X11
AESENC X4, X12
AESENC X5, X13
AESENC X6, X14
AESENC X7, X15
// xor in seed
PXOR X0, X8
PXOR X1, X9
PXOR X2, X10
PXOR X3, X11
PXOR X4, X12
PXOR X5, X13
PXOR X6, X14
PXOR X7, X15
// compute number of remaining 128-byte blocks
DECQ CX
SHRQ $7, CX
aesloop:
// scramble state
AESENC X8, X8
AESENC X9, X9
AESENC X10, X10
AESENC X11, X11
AESENC X12, X12
AESENC X13, X13
AESENC X14, X14
AESENC X15, X15
// scramble state, xor in a block
MOVOU (AX), X0
MOVOU 16(AX), X1
@@ -1138,7 +1167,11 @@ aesloop:
AESENC X6, X14
AESENC X7, X15
// scramble state
ADDQ $128, AX
DECQ CX
JNE aesloop
// 3 more scrambles to finish
AESENC X8, X8
AESENC X9, X9
AESENC X10, X10
@@ -1147,12 +1180,6 @@ aesloop:
AESENC X13, X13
AESENC X14, X14
AESENC X15, X15
ADDQ $128, AX
DECQ CX
JNE aesloop
// 2 more scrambles to finish
AESENC X8, X8
AESENC X9, X9
AESENC X10, X10
View
@@ -7,21 +7,15 @@
#include "libcgo.h"
// The context function, used when tracing back C calls into Go.
void (*x_cgo_context_function)(struct context_arg*);
// Sets the context function to call to record the traceback context
// when calling a Go function from C code. Called from runtime.SetCgoTraceback.
void x_cgo_set_context_function(void (*context)(struct context_arg*)) {
x_cgo_context_function = context;
}
// Releases the cgo traceback context.
void _cgo_release_context(uintptr_t ctxt) {
if (ctxt != 0 && x_cgo_context_function != nil) {
void (*pfn)(struct context_arg*);
pfn = _cgo_get_context_function();
if (ctxt != 0 && pfn != nil) {
struct context_arg arg;
arg.Context = ctxt;
(*x_cgo_context_function)(&arg);
(*pfn)(&arg);
}
}
View
@@ -15,6 +15,9 @@ static pthread_cond_t runtime_init_cond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t runtime_init_mu = PTHREAD_MUTEX_INITIALIZER;
static int runtime_init_done;
// The context function, used when tracing back C calls into Go.
static void (*cgo_context_function)(struct context_arg*);
void
x_cgo_sys_thread_create(void* (*func)(void*), void* arg) {
pthread_t p;
@@ -27,16 +30,30 @@ x_cgo_sys_thread_create(void* (*func)(void*), void* arg) {
uintptr_t
_cgo_wait_runtime_init_done() {
void (*pfn)(struct context_arg*);
pthread_mutex_lock(&runtime_init_mu);
while (runtime_init_done == 0) {
pthread_cond_wait(&runtime_init_cond, &runtime_init_mu);
}
// TODO(iant): For the case of a new C thread calling into Go, such
// as when using -buildmode=c-archive, we know that Go runtime
// initialization is complete but we do not know that all Go init
// functions have been run. We should not fetch cgo_context_function
// until they have been, because that is where a call to
// SetCgoTraceback is likely to occur. We are going to wait for Go
// initialization to be complete anyhow, later, by waiting for
// main_init_done to be closed in cgocallbackg1. We should wait here
// instead. See also issue #15943.
pfn = cgo_context_function;
pthread_mutex_unlock(&runtime_init_mu);
if (x_cgo_context_function != nil) {
if (pfn != nil) {
struct context_arg arg;
arg.Context = 0;
(*x_cgo_context_function)(&arg);
(*pfn)(&arg);
return arg.Context;
}
return 0;
@@ -49,3 +66,21 @@ x_cgo_notify_runtime_init_done(void* dummy) {
pthread_cond_broadcast(&runtime_init_cond);
pthread_mutex_unlock(&runtime_init_mu);
}
// Sets the context function to call to record the traceback context
// when calling a Go function from C code. Called from runtime.SetCgoTraceback.
void x_cgo_set_context_function(void (*context)(struct context_arg*)) {
pthread_mutex_lock(&runtime_init_mu);
cgo_context_function = context;
pthread_mutex_unlock(&runtime_init_mu);
}
// Gets the context function.
void (*(_cgo_get_context_function(void)))(struct context_arg*) {
void (*ret)(struct context_arg*);
pthread_mutex_lock(&runtime_init_mu);
ret = cgo_context_function;
pthread_mutex_unlock(&runtime_init_mu);
return ret;
}
View
@@ -6,6 +6,9 @@
#include <stdlib.h>
#include "libcgo.h"
// The context function, used when tracing back C calls into Go.
static void (*cgo_context_function)(struct context_arg*);
void
x_cgo_sys_thread_create(void* (*func)(void*), void* arg) {
fprintf(stderr, "x_cgo_sys_thread_create not implemented");
@@ -14,12 +17,16 @@ x_cgo_sys_thread_create(void* (*func)(void*), void* arg) {
uintptr_t
_cgo_wait_runtime_init_done() {
void (*pfn)(struct context_arg*);
// TODO(spetrovic): implement this method.
if (x_cgo_context_function != nil) {
pfn = _cgo_get_context_function();
if (pfn != nil) {
struct context_arg arg;
arg.Context = 0;
(*x_cgo_context_function)(&arg);
(*pfn)(&arg);
return arg.Context;
}
return 0;
@@ -29,3 +36,15 @@ void
x_cgo_notify_runtime_init_done(void* dummy) {
// TODO(spetrovic): implement this method.
}
// Sets the context function to call to record the traceback context
// when calling a Go function from C code. Called from runtime.SetCgoTraceback.
void x_cgo_set_context_function(void (*context)(struct context_arg*)) {
// TODO(iant): Needs synchronization.
cgo_context_function = context;
}
// Gets the context function.
void (*(_cgo_get_context_function(void)))(struct context_arg*) {
return cgo_context_function;
}
View
@@ -70,15 +70,18 @@ _cgo_is_runtime_initialized() {
uintptr_t
_cgo_wait_runtime_init_done() {
void (*pfn)(struct context_arg*);
_cgo_maybe_run_preinit();
while (!_cgo_is_runtime_initialized()) {
WaitForSingleObject(runtime_init_wait, INFINITE);
}
if (x_cgo_context_function != nil) {
pfn = _cgo_get_context_function();
if (pfn != nil) {
struct context_arg arg;
arg.Context = 0;
(*x_cgo_context_function)(&arg);
(*pfn)(&arg);
return arg.Context;
}
return 0;
@@ -98,3 +101,23 @@ x_cgo_notify_runtime_init_done(void* dummy) {
}
}
// The context function, used when tracing back C calls into Go.
static void (*cgo_context_function)(struct context_arg*);
// Sets the context function to call to record the traceback context
// when calling a Go function from C code. Called from runtime.SetCgoTraceback.
void x_cgo_set_context_function(void (*context)(struct context_arg*)) {
EnterCriticalSection(&runtime_init_cs);
cgo_context_function = context;
LeaveCriticalSection(&runtime_init_cs);
}
// Gets the context function.
void (*(_cgo_get_context_function(void)))(struct context_arg*) {
void (*ret)(struct context_arg*);
EnterCriticalSection(&runtime_init_cs);
ret = cgo_context_function;
LeaveCriticalSection(&runtime_init_cs);
return ret;
}
View
@@ -8,11 +8,15 @@
#include <stdint.h>
#include <sys/mman.h>
#include "libcgo.h"
void *
x_cgo_mmap(void *addr, uintptr_t length, int32_t prot, int32_t flags, int32_t fd, uint32_t offset) {
void *p;
_cgo_tsan_acquire();
p = mmap(addr, length, prot, flags, fd, offset);
_cgo_tsan_release();
if (p == MAP_FAILED) {
/* This is what the Go code expects on failure. */
p = (void *) (uintptr_t) errno;
View
@@ -21,9 +21,12 @@ crosscall_s390x:
stdy %f4, 144(%r15)
stdy %f6, 152(%r15)
/* assumes this call does not clobber r2 or r15 */
/* set r0 to 0 */
xgr %r0, %r0
/* restore g pointer */
lgr %r13, %r3
/* grow stack 8 bytes and call fn */
agfi %r15, -8
basr %r14, %r2
View
@@ -93,7 +93,7 @@ void darwin_arm_init_mach_exception_handler(void);
struct context_arg {
uintptr_t Context;
};
extern void (*x_cgo_context_function)(struct context_arg*);
extern void (*(_cgo_get_context_function(void)))(struct context_arg*);
/*
* TSAN support. This is only useful when building with
View
@@ -193,7 +193,20 @@ func SetCPUProfileRate(hz int) {
// and cannot allocate memory or acquire locks that might be
// held at the time of the signal, nor can it use substantial amounts
// of stack. It is allowed to call evict.
//go:nowritebarrierrec
func (p *cpuProfile) add(pc []uintptr) {
p.addWithFlushlog(pc, p.flushlog)
}
// addWithFlushlog implements add and addNonGo.
// It is called from signal handlers and other limited environments
// and cannot allocate memory or acquire locks that might be
// held at the time of the signal, nor can it use substantial amounts
// of stack. It may be called by a signal handler with no g or m.
// It is allowed to call evict, passing the flushlog parameter.
//go:nosplit
//go:nowritebarrierrec
func (p *cpuProfile) addWithFlushlog(pc []uintptr, flushlog func() bool) {
if len(pc) > maxCPUProfStack {
pc = pc[:maxCPUProfStack]
}
@@ -231,7 +244,7 @@ Assoc:
}
}
if e.count > 0 {
if !p.evict(e) {
if !p.evict(e, flushlog) {
// Could not evict entry. Record lost stack.
p.lost++
return
@@ -248,15 +261,17 @@ Assoc:
// evict copies the given entry's data into the log, so that
// the entry can be reused. evict is called from add, which
// is called from the profiling signal handler, so it must not
// allocate memory or block. It is safe to call flushlog.
// evict returns true if the entry was copied to the log,
// false if there was no room available.
func (p *cpuProfile) evict(e *cpuprofEntry) bool {
// allocate memory or block, and it may be called with no g or m.
// It is safe to call flushlog. evict returns true if the entry was
// copied to the log, false if there was no room available.
//go:nosplit
//go:nowritebarrierrec
func (p *cpuProfile) evict(e *cpuprofEntry, flushlog func() bool) bool {
d := e.depth
nslot := d + 2
log := &p.log[p.toggle]
if p.nlog+nslot > len(log) {
if !p.flushlog() {
if !flushlog() {
return false
}
log = &p.log[p.toggle]
@@ -278,6 +293,7 @@ func (p *cpuProfile) evict(e *cpuprofEntry) bool {
// flushlog is called from evict, called from add, called from the signal handler,
// so it cannot allocate memory or block. It can try to swap logs with
// the writing goroutine, as explained in the comment at the top of this file.
//go:nowritebarrierrec
func (p *cpuProfile) flushlog() bool {
if !atomic.Cas(&p.handoff, 0, uint32(p.nlog)) {
return false
@@ -299,6 +315,16 @@ func (p *cpuProfile) flushlog() bool {
return true
}
// addNonGo is like add, but runs on a non-Go thread.
// It can't do anything that might need a g or an m.
// With this entry point, we don't try to flush the log when evicting an
// old entry. Instead, we just drop the stack trace if we're out of space.
//go:nosplit
//go:nowritebarrierrec
func (p *cpuProfile) addNonGo(pc []uintptr) {
p.addWithFlushlog(pc, func() bool { return false })
}
// getprofile blocks until the next block of profiling data is available
// and returns it as a []byte. It is called from the writing goroutine.
func (p *cpuProfile) getprofile() []byte {
@@ -366,7 +392,7 @@ Flush:
b := &p.hash[i]
for j := range b.entry {
e := &b.entry[j]
if e.count > 0 && !p.evict(e) {
if e.count > 0 && !p.evict(e, p.flushlog) {
// Filled the log. Stop the loop and return what we've got.
break Flush
}
View
@@ -234,62 +234,43 @@ func TestCgoTracebackContext(t *testing.T) {
}
}
func TestCgoPprof(t *testing.T) {
func testCgoPprof(t *testing.T, buildArg, runArg string) {
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
}
testenv.MustHaveGoRun(t)
exe, err := buildTestProg(t, "testprogcgo")
exe, err := buildTestProg(t, "testprogcgo", buildArg)
if err != nil {
t.Fatal(err)
}
got, err := testEnv(exec.Command(exe, "CgoPprof")).CombinedOutput()
got, err := testEnv(exec.Command(exe, runArg)).CombinedOutput()
if err != nil {
t.Fatal(err)
}
fn := strings.TrimSpace(string(got))
defer os.Remove(fn)
top, err := exec.Command("go", "tool", "pprof", "-top", "-nodecount=1", exe, fn).CombinedOutput()
t.Logf("%s", top)
if err != nil {
t.Fatal(err)
}
t.Logf("%s", top)
if !bytes.Contains(top, []byte("cpuHog")) {
t.Error("missing cpuHog in pprof output")
}
}
func TestCgoPprofPIE(t *testing.T) {
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
}
testenv.MustHaveGoRun(t)
exe, err := buildTestProg(t, "testprogcgo", "-ldflags=-extldflags=-pie")
if err != nil {
t.Fatal(err)
}
got, err := testEnv(exec.Command(exe, "CgoPprof")).CombinedOutput()
if err != nil {
t.Fatal(err)
}
fn := strings.TrimSpace(string(got))
defer os.Remove(fn)
top, err := exec.Command("go", "tool", "pprof", "-top", "-nodecount=1", exe, fn).CombinedOutput()
if err != nil {
t.Fatal(err)
}
func TestCgoPprof(t *testing.T) {
testCgoPprof(t, "", "CgoPprof")
}
t.Logf("%s", top)
func TestCgoPprofPIE(t *testing.T) {
testCgoPprof(t, "-ldflags=-extldflags=-pie", "CgoPprof")
}
if !bytes.Contains(top, []byte("cpuHog")) {
t.Error("missing cpuHog in pprof output")
}
func TestCgoPprofThread(t *testing.T) {
testCgoPprof(t, "", "CgoPprofThread")
}
View
@@ -0,0 +1,13 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build windows plan9 nacl
package runtime_test
import "os"
// sigquit is the signal to send to kill a hanging testdata program.
// On Unix we send SIGQUIT, but on non-Unix we only have os.Kill.
var sigquit = os.Kill
View
@@ -5,6 +5,7 @@
package runtime_test
import (
"bytes"
"fmt"
"internal/testenv"
"io/ioutil"
@@ -13,9 +14,11 @@ import (
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"sync"
"testing"
"time"
)
var toRemove []string
@@ -65,8 +68,45 @@ func runTestProg(t *testing.T, binary, name string) string {
if err != nil {
t.Fatal(err)
}
got, _ := testEnv(exec.Command(exe, name)).CombinedOutput()
return string(got)
cmd := testEnv(exec.Command(exe, name))
var b bytes.Buffer
cmd.Stdout = &b
cmd.Stderr = &b
if err := cmd.Start(); err != nil {
t.Fatalf("starting %s %s: %v", binary, name, err)
}
// If the process doesn't complete within 1 minute,
// assume it is hanging and kill it to get a stack trace.
p := cmd.Process
done := make(chan bool)
go func() {
scale := 1
// This GOARCH/GOOS test is copied from cmd/dist/test.go.
// TODO(iant): Have cmd/dist update the environment variable.
if runtime.GOARCH == "arm" || runtime.GOOS == "windows" {
scale = 2
}
if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
if sc, err := strconv.Atoi(s); err == nil {
scale = sc
}
}
select {
case <-done:
case <-time.After(time.Duration(scale) * time.Minute):
p.Signal(sigquit)
}
}()
if err := cmd.Wait(); err != nil {
t.Logf("%s %s exit status: %v", binary, name, err)
}
close(done)
return b.String()
}
func buildTestProg(t *testing.T, binary string, flags ...string) (string, error) {
View
@@ -19,6 +19,10 @@ import (
"testing"
)
// sigquit is the signal to send to kill a hanging testdata program.
// Send SIGQUIT to get a stack trace.
var sigquit = syscall.SIGQUIT
func TestCrashDumpsAllThreads(t *testing.T) {
switch runtime.GOOS {
case "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris":
View
@@ -38,7 +38,7 @@ type Obj struct {
}
func objfin(x *Obj) {
println("finalized", x)
//println("finalized", x)
}
func TestWriteHeapDumpFinalizers(t *testing.T) {
View
@@ -681,3 +681,23 @@ func BenchmarkUnalignedLoad(b *testing.B) {
}
sink = s
}
func TestCollisions(t *testing.T) {
for i := 0; i < 16; i++ {
for j := 0; j < 16; j++ {
if j == i {
continue
}
var a [16]byte
m := make(map[uint16]struct{}, 1<<16)
for n := 0; n < 1<<16; n++ {
a[i] = byte(n)
a[j] = byte(n >> 8)
m[uint16(BytesHash(a[:], 0))] = struct{}{}
}
if len(m) <= 1<<15 {
t.Errorf("too many collisions i=%d j=%d outputs=%d out of 65536\n", i, j, len(m))
}
}
}
}
View
@@ -6,7 +6,7 @@
// Initialize head to 0, compare with 0 to test for emptiness.
// The stack does not keep pointers to nodes,
// so they can be garbage collected if there are no other pointers to nodes.
// The following code runs only on g0 stack.
// The following code runs only in non-preemptible contexts.
package runtime
View
@@ -3000,6 +3000,8 @@ func _ExternalCode() { _ExternalCode() }
func _GC() { _GC() }
// Called if we receive a SIGPROF signal.
// Called by the signal handler, may run during STW.
//go:nowritebarrierrec
func sigprof(pc, sp, lr uintptr, gp *g, mp *m) {
if prof.hz == 0 {
return
@@ -3159,6 +3161,36 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) {
mp.mallocing--
}
// If the signal handler receives a SIGPROF signal on a non-Go thread,
// it tries to collect a traceback into sigprofCallers.
// sigprofCallersUse is set to non-zero while sigprofCallers holds a traceback.
var sigprofCallers cgoCallers
var sigprofCallersUse uint32
// Called if we receive a SIGPROF signal on a non-Go thread.
// When this is called, sigprofCallersUse will be non-zero.
// g is nil, and what we can do is very limited.
//go:nosplit
//go:nowritebarrierrec
func sigprofNonGo() {
if prof.hz != 0 {
n := 0
for n < len(sigprofCallers) && sigprofCallers[n] != 0 {
n++
}
// Simple cas-lock to coordinate with setcpuprofilerate.
if atomic.Cas(&prof.lock, 0, 1) {
if prof.hz != 0 {
cpuprof.addNonGo(sigprofCallers[:n])
}
atomic.Store(&prof.lock, 0)
}
}
atomic.Store(&sigprofCallersUse, 0)
}
// Reports whether a function will set the SP
// to an absolute value. Important that
// we don't traceback when these are at the bottom
View
@@ -448,15 +448,15 @@ def invoke(self, arg, _from_tty):
except gdb.error:
pc = int(str(pc).split(None, 1)[0], 16)
save_frame = gdb.selected_frame()
gdb.parse_and_eval('$save_pc = $pc')
gdb.parse_and_eval('$save_sp = $sp')
gdb.parse_and_eval('$pc = {0}'.format(str(pc)))
gdb.parse_and_eval('$save_pc = $pc')
gdb.parse_and_eval('$sp = {0}'.format(str(sp)))
gdb.parse_and_eval('$pc = {0}'.format(str(pc)))
try:
gdb.execute(cmd)
finally:
gdb.parse_and_eval('$pc = $save_pc')
gdb.parse_and_eval('$sp = $save_sp')
gdb.parse_and_eval('$pc = $save_pc')
save_frame.select()
View
@@ -18,6 +18,14 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
}
g := getg()
if g == nil {
if sig == _SIGPROF {
// Ignore profiling signals that arrive on
// non-Go threads. On some systems they will
// be handled directly by the signal handler,
// by calling sigprofNonGo, in which case we won't
// get here anyhow.
return
}
badsignal(uintptr(sig), &sigctxt{info, ctx})
return
}
View
@@ -253,7 +253,7 @@ TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
get_tls(CX)
MOVQ g(CX),AX
TESTQ AX, AX
JZ sigtramp // g == nil
JZ sigtrampnog // g == nil
MOVQ g_m(AX), AX
TESTQ AX, AX
JZ sigtramp // g.m == nil
@@ -276,8 +276,8 @@ TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
// Jump to a function in runtime/cgo.
// That function, written in C, will call the user's traceback
// function with proper unwind info, and will then call back here.
// The first three arguments are already in registers.
// Set the last three arguments now.
// The first three arguments, and the fifth, are already in registers.
// Set the two remaining arguments now.
MOVQ runtime·cgoTraceback(SB), CX
MOVQ $runtime·sigtramp(SB), R9
MOVQ _cgo_callers(SB), AX
@@ -286,6 +286,30 @@ TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
sigtramp:
JMP runtime·sigtramp(SB)
sigtrampnog:
// Signal arrived on a non-Go thread. If this is SIGPROF, get a
// stack trace.
CMPL DI, $27 // 27 == SIGPROF
JNZ sigtramp
// Lock sigprofCallersUse.
MOVL $0, AX
MOVL $1, CX
MOVQ $runtime·sigprofCallersUse(SB), BX
LOCK
CMPXCHGL CX, 0(BX)
JNZ sigtramp // Skip stack trace if already locked.
// Jump to the traceback function in runtime/cgo.
// It will call back to sigprofNonGo, which will ignore the
// arguments passed in registers.
// First three arguments to traceback function are in registers already.
MOVQ runtime·cgoTraceback(SB), CX
MOVQ $runtime·sigprofCallers(SB), R8
MOVQ $runtime·sigprofNonGo(SB), R9
MOVQ _cgo_callers(SB), AX
JMP AX
// For cgo unwinding to work, this function must look precisely like
// the one in glibc. The glibc source code is:
// https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/x86_64/sigaction.c
View
@@ -30,19 +30,17 @@ func GoNop() {}
func CgoCCodeSIGPROF() {
c := make(chan bool)
go func() {
for {
<-c
start := time.Now()
for i := 0; i < 1e7; i++ {
if i%1000 == 0 {
if time.Since(start) > time.Second {
break
}
<-c
start := time.Now()
for i := 0; i < 1e7; i++ {
if i%1000 == 0 {
if time.Since(start) > time.Second {
break
}
C.GoNop()
}
c <- true
C.GoNop()
}
c <- true
}()
var buf bytes.Buffer
Oops, something went wrong.