diff options
author | Ian Lance Taylor <iant@golang.org> | 2018-09-24 21:46:21 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2018-09-24 21:46:21 +0000 |
commit | dd931d9b48647e898dc80927c532ae93cc09e192 (patch) | |
tree | 71be2295cd79b8a182f6130611658db8628772d5 /libgo/go/internal | |
parent | 779d8a5ad09b01428726ea5a0e6c87bd9ac3c0e4 (diff) | |
download | gcc-dd931d9b48647e898dc80927c532ae93cc09e192.zip gcc-dd931d9b48647e898dc80927c532ae93cc09e192.tar.gz gcc-dd931d9b48647e898dc80927c532ae93cc09e192.tar.bz2 |
libgo: update to Go 1.11
Reviewed-on: https://go-review.googlesource.com/136435
gotools/:
* Makefile.am (mostlyclean-local): Run chmod on check-go-dir to
make sure it is writable.
(check-go-tools): Likewise.
(check-vet): Copy internal/objabi to check-vet-dir.
* Makefile.in: Rebuild.
From-SVN: r264546
Diffstat (limited to 'libgo/go/internal')
62 files changed, 2306 insertions, 259 deletions
diff --git a/libgo/go/internal/bytealg/bytealg.c b/libgo/go/internal/bytealg/bytealg.c new file mode 100644 index 0000000..39c060f --- /dev/null +++ b/libgo/go/internal/bytealg/bytealg.c @@ -0,0 +1,116 @@ +/* bytealg.c -- gccgo implementations of bytealg functions + + Copyright 2018 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. */ + +#include <stddef.h> +#include <string.h> + +#include "runtime.h" +#include "array.h" + +intgo Compare(struct __go_open_array, struct __go_open_array) + __asm__(GOSYM_PREFIX "internal_bytealg.Compare") + __attribute__((no_split_stack)); + +intgo Compare(struct __go_open_array a, struct __go_open_array b) +{ + intgo l; + + l = a.__count; + if (b.__count < l) { + l = b.__count; + } + if (l > 0 && a.__values != b.__values) { + int i; + + i = __builtin_memcmp(a.__values, b.__values, (size_t)(l)); + if (i != 0) { + return i; + } + } + if (a.__count < b.__count) { + return -1; + } + if (a.__count > b.__count) { + return +1; + } + return 0; +} + +_Bool Equal(struct __go_open_array, struct __go_open_array) + __asm__(GOSYM_PREFIX "internal_bytealg.Equal") + __attribute__((no_split_stack)); + +_Bool Equal(struct __go_open_array a, struct __go_open_array b) +{ + intgo l; + + l = a.__count; + if (l != b.__count) { + return 0; + } + return __builtin_memcmp(a.__values, b.__values, l) == 0; +} + +intgo IndexByte(struct __go_open_array, byte) + __asm__(GOSYM_PREFIX "internal_bytealg.IndexByte") + __attribute__((no_split_stack)); + +intgo IndexByte(struct __go_open_array b, byte c) +{ + const byte *p; + + p = __builtin_memchr(b.__values, c, (size_t)(b.__count)); + if (p == nil) { + return -1; + } + return p - (byte *)(b.__values); +} + + +intgo IndexByteString(String, byte) + __asm__(GOSYM_PREFIX "internal_bytealg.IndexByteString") + __attribute__((no_split_stack)); + +intgo IndexByteString(String s, byte c) +{ + const byte *p; + + p = __builtin_memchr(s.str, c, (size_t)(s.len)); + if (p == nil) { + return -1; + } + return p - s.str; +} + +intgo Index(struct __go_open_array, struct __go_open_array) + __asm__(GOSYM_PREFIX "internal_bytealg.Index") + __attribute__((no_split_stack)); + +intgo Index(struct __go_open_array a, struct __go_open_array b) +{ + const byte* p; + + p = memmem(a.__values, a.__count, b.__values, b.__count); + if (p == nil) { + return -1; + } + return p - (const byte*)(a.__values); +} + +intgo IndexString(String, String) + __asm__(GOSYM_PREFIX "internal_bytealg.IndexString") + __attribute__((no_split_stack)); + +intgo IndexString(String a, String b) +{ + const byte* p; + + p = memmem(a.str, a.len, b.str, b.len); + if (p == nil) { + return -1; + } + return p - a.str; +} diff --git a/libgo/go/internal/bytealg/bytealg.go b/libgo/go/internal/bytealg/bytealg.go new file mode 100644 index 0000000..b91eb00 --- /dev/null +++ b/libgo/go/internal/bytealg/bytealg.go @@ -0,0 +1,24 @@ +// Copyright 2018 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 ignore_for_gccgo + +package bytealg + +import ( + "internal/cpu" + "unsafe" +) + +// Offsets into internal/cpu records for use in assembly. +const ( + x86_HasSSE2 = unsafe.Offsetof(cpu.X86.HasSSE2) + x86_HasSSE42 = unsafe.Offsetof(cpu.X86.HasSSE42) + x86_HasAVX2 = unsafe.Offsetof(cpu.X86.HasAVX2) + x86_HasPOPCNT = unsafe.Offsetof(cpu.X86.HasPOPCNT) + s390x_HasVX = unsafe.Offsetof(cpu.S390X.HasVX) +) + +// MaxLen is the maximum length of the string to be searched for (argument b) in Index. +var MaxLen int = 32 diff --git a/libgo/go/internal/bytealg/compare_generic.go b/libgo/go/internal/bytealg/compare_generic.go new file mode 100644 index 0000000..bb48833 --- /dev/null +++ b/libgo/go/internal/bytealg/compare_generic.go @@ -0,0 +1,89 @@ +// Copyright 2018 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 ignore_for_gccgo +// +build !386,!amd64,!amd64p32,!s390x,!arm,!arm64,!ppc64,!ppc64le,!mips,!mipsle,!wasm + +package bytealg + +import _ "unsafe" // for go:linkname + +func Compare(a, b []byte) int { + l := len(a) + if len(b) < l { + l = len(b) + } + if l == 0 || &a[0] == &b[0] { + goto samebytes + } + for i := 0; i < l; i++ { + c1, c2 := a[i], b[i] + if c1 < c2 { + return -1 + } + if c1 > c2 { + return +1 + } + } +samebytes: + if len(a) < len(b) { + return -1 + } + if len(a) > len(b) { + return +1 + } + return 0 +} + +//go:linkname bytes_Compare bytes.Compare +func bytes_Compare(a, b []byte) int { + l := len(a) + if len(b) < l { + l = len(b) + } + if l == 0 || &a[0] == &b[0] { + goto samebytes + } + for i := 0; i < l; i++ { + c1, c2 := a[i], b[i] + if c1 < c2 { + return -1 + } + if c1 > c2 { + return +1 + } + } +samebytes: + if len(a) < len(b) { + return -1 + } + if len(a) > len(b) { + return +1 + } + return 0 +} + +//go:linkname runtime_cmpstring runtime.cmpstring +func runtime_cmpstring(a, b string) int { + l := len(a) + if len(b) < l { + l = len(b) + } + for i := 0; i < l; i++ { + c1, c2 := a[i], b[i] + if c1 < c2 { + return -1 + } + if c1 > c2 { + return +1 + } + } + if len(a) < len(b) { + return -1 + } + if len(a) > len(b) { + return +1 + } + return 0 +} diff --git a/libgo/go/internal/bytealg/compare_native.go b/libgo/go/internal/bytealg/compare_native.go new file mode 100644 index 0000000..2c317e2 --- /dev/null +++ b/libgo/go/internal/bytealg/compare_native.go @@ -0,0 +1,10 @@ +// Copyright 2018 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 386 amd64 amd64p32 s390x arm arm64 ppc64 ppc64le mips mipsle wasm + +package bytealg + +//go:noescape +func Compare(a, b []byte) int diff --git a/libgo/go/internal/bytealg/count_generic.go b/libgo/go/internal/bytealg/count_generic.go new file mode 100644 index 0000000..5a06430 --- /dev/null +++ b/libgo/go/internal/bytealg/count_generic.go @@ -0,0 +1,27 @@ +// Copyright 2018 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 !amd64,!arm64 + +package bytealg + +func Count(b []byte, c byte) int { + n := 0 + for _, x := range b { + if x == c { + n++ + } + } + return n +} + +func CountString(s string, c byte) int { + n := 0 + for i := 0; i < len(s); i++ { + if s[i] == c { + n++ + } + } + return n +} diff --git a/libgo/go/internal/bytealg/count_native.go b/libgo/go/internal/bytealg/count_native.go new file mode 100644 index 0000000..5ea5c28 --- /dev/null +++ b/libgo/go/internal/bytealg/count_native.go @@ -0,0 +1,34 @@ +// Copyright 2018 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 ignore_for_gccgo +// +build amd64 arm64 + +package bytealg + +//go:noescape +func Count(b []byte, c byte) int + +//go:noescape +func CountString(s string, c byte) int + +// A backup implementation to use by assembly. +func countGeneric(b []byte, c byte) int { + n := 0 + for _, x := range b { + if x == c { + n++ + } + } + return n +} +func countGenericString(s string, c byte) int { + n := 0 + for i := 0; i < len(s); i++ { + if s[i] == c { + n++ + } + } + return n +} diff --git a/libgo/go/internal/bytealg/equal_native.go b/libgo/go/internal/bytealg/equal_native.go new file mode 100644 index 0000000..b5c4530 --- /dev/null +++ b/libgo/go/internal/bytealg/equal_native.go @@ -0,0 +1,14 @@ +// Copyright 2018 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. + +package bytealg + +// Note: there's no equal_generic.go because every platform must implement at least memequal_varlen in assembly. + +//go:noescape +func Equal(a, b []byte) bool + +// The compiler generates calls to runtime.memequal and runtime.memequal_varlen. +// In addition, the runtime calls runtime.memequal explicitly. +// Those functions are implemented in this package. diff --git a/libgo/go/internal/bytealg/gccgo.go b/libgo/go/internal/bytealg/gccgo.go new file mode 100644 index 0000000..8c78b63 --- /dev/null +++ b/libgo/go/internal/bytealg/gccgo.go @@ -0,0 +1,12 @@ +// Copyright 2018 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 gccgo + +package bytealg + +// MaxLen is the maximum length of the string to be searched for (argument b) in Index. +var MaxLen int = 32 + +const MaxBruteForce = 64 diff --git a/libgo/go/internal/bytealg/index_amd64.go b/libgo/go/internal/bytealg/index_amd64.go new file mode 100644 index 0000000..dd6a6cf --- /dev/null +++ b/libgo/go/internal/bytealg/index_amd64.go @@ -0,0 +1,28 @@ +// Copyright 2018 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 ignore_for_gccgo + +package bytealg + +import "internal/cpu" + +const MaxBruteForce = 64 + +func init() { + if cpu.X86.HasAVX2 { + MaxLen = 63 + } else { + MaxLen = 31 + } +} + +// Cutover reports the number of failures of IndexByte we should tolerate +// before switching over to Index. +// n is the number of bytes processed so far. +// See the bytes.Index implementation for details. +func Cutover(n int) int { + // 1 error per 8 characters, plus a few slop to start. + return (n + 16) / 8 +} diff --git a/libgo/go/internal/bytealg/index_arm64.go b/libgo/go/internal/bytealg/index_arm64.go new file mode 100644 index 0000000..51bdf16 --- /dev/null +++ b/libgo/go/internal/bytealg/index_arm64.go @@ -0,0 +1,25 @@ +// Copyright 2018 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 ignore_for_gccgo + +package bytealg + +// Empirical data shows that using IndexShortStr can get better +// performance when len(s) <= 16. +const MaxBruteForce = 16 + +func init() { + // Optimize cases where the length of the substring is less than 32 bytes + MaxLen = 32 +} + +// Cutover reports the number of failures of IndexByte we should tolerate +// before switching over to IndexShortStr. +// n is the number of bytes processed so far. +// See the bytes.Index implementation for details. +func Cutover(n int) int { + // 1 error per 16 characters, plus a few slop to start. + return 4 + n>>4 +} diff --git a/libgo/go/internal/bytealg/index_generic.go b/libgo/go/internal/bytealg/index_generic.go new file mode 100644 index 0000000..c595c23 --- /dev/null +++ b/libgo/go/internal/bytealg/index_generic.go @@ -0,0 +1,30 @@ +// Copyright 2018 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 ignore_for_gccgo +// +build !amd64,!arm64,!s390x + +package bytealg + +const MaxBruteForce = 0 + +// Index returns the index of the first instance of b in a, or -1 if b is not present in a. +// Requires 2 <= len(b) <= MaxLen. +func Index(a, b []byte) int { + panic("unimplemented") +} + +// IndexString returns the index of the first instance of b in a, or -1 if b is not present in a. +// Requires 2 <= len(b) <= MaxLen. +func IndexString(a, b string) int { + panic("unimplemented") +} + +// Cutover reports the number of failures of IndexByte we should tolerate +// before switching over to Index. +// n is the number of bytes processed so far. +// See the bytes.Index implementation for details. +func Cutover(n int) int { + panic("unimplemented") +} diff --git a/libgo/go/internal/bytealg/index_native.go b/libgo/go/internal/bytealg/index_native.go new file mode 100644 index 0000000..26f4769 --- /dev/null +++ b/libgo/go/internal/bytealg/index_native.go @@ -0,0 +1,24 @@ +// Copyright 2018 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 amd64 arm64 s390x + +package bytealg + +//go:noescape + +// Index returns the index of the first instance of b in a, or -1 if b is not present in a. +// Requires 2 <= len(b) <= MaxLen. +func Index(a, b []byte) int + +//go:noescape + +// IndexString returns the index of the first instance of b in a, or -1 if b is not present in a. +// Requires 2 <= len(b) <= MaxLen. +func IndexString(a, b string) int + +func Cutover(n int) int { + // 1 error per 8 characters, plus a few slop to start. + return (n + 16) / 8 +} diff --git a/libgo/go/internal/bytealg/index_s390x.go b/libgo/go/internal/bytealg/index_s390x.go new file mode 100644 index 0000000..ee076a0 --- /dev/null +++ b/libgo/go/internal/bytealg/index_s390x.go @@ -0,0 +1,33 @@ +// Copyright 2018 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 ignore_for_gcco + +package bytealg + +import "internal/cpu" + +const MaxBruteForce = 64 + +func init() { + // Note: we're kind of lucky that this flag is available at this point. + // The runtime sets HasVX when processing auxv records, and that happens + // to happen *before* running the init functions of packages that + // the runtime depends on. + // TODO: it would really be nicer for internal/cpu to figure out this + // flag by itself. Then we wouldn't need to depend on quirks of + // early startup initialization order. + if cpu.S390X.HasVX { + MaxLen = 64 + } +} + +// Cutover reports the number of failures of IndexByte we should tolerate +// before switching over to Index. +// n is the number of bytes processed so far. +// See the bytes.Index implementation for details. +func Cutover(n int) int { + // 1 error per 8 characters, plus a few slop to start. + return (n + 16) / 8 +} diff --git a/libgo/go/internal/bytealg/indexbyte_generic.go b/libgo/go/internal/bytealg/indexbyte_generic.go new file mode 100644 index 0000000..890cd42 --- /dev/null +++ b/libgo/go/internal/bytealg/indexbyte_generic.go @@ -0,0 +1,48 @@ +// Copyright 2018 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. + +// +ignore_for_gccgo +// +build !386,!amd64,!amd64p32,!s390x,!arm,!arm64,!ppc64,!ppc64le,!mips,!mipsle,!mips64,!mips64le,!wasm + +package bytealg + +import _ "unsafe" // for go:linkname + +func IndexByte(b []byte, c byte) int { + for i, x := range b { + if x == c { + return i + } + } + return -1 +} + +func IndexByteString(s string, c byte) int { + for i := 0; i < len(s); i++ { + if s[i] == c { + return i + } + } + return -1 +} + +//go:linkname bytes_IndexByte bytes.IndexByte +func bytes_IndexByte(b []byte, c byte) int { + for i, x := range b { + if x == c { + return i + } + } + return -1 +} + +//go:linkname strings_IndexByte strings.IndexByte +func strings_IndexByte(s string, c byte) int { + for i := 0; i < len(s); i++ { + if s[i] == c { + return i + } + } + return -1 +} diff --git a/libgo/go/internal/bytealg/indexbyte_native.go b/libgo/go/internal/bytealg/indexbyte_native.go new file mode 100644 index 0000000..5cf6fed --- /dev/null +++ b/libgo/go/internal/bytealg/indexbyte_native.go @@ -0,0 +1,13 @@ +// Copyright 2018 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 386 amd64 amd64p32 s390x arm arm64 ppc64 ppc64le mips mipsle mips64 mips64le wasm + +package bytealg + +//go:noescape +func IndexByte(b []byte, c byte) int + +//go:noescape +func IndexByteString(s string, c byte) int diff --git a/libgo/go/internal/cpu/cpu.go b/libgo/go/internal/cpu/cpu.go index 22fc561..9567a4f 100644 --- a/libgo/go/internal/cpu/cpu.go +++ b/libgo/go/internal/cpu/cpu.go @@ -6,6 +6,11 @@ // used by the Go standard library. package cpu +// DebugOptions is set to true by the runtime if go was compiled with GOEXPERIMENT=debugcpu +// and GOOS is Linux or Darwin. +// This should not be changed after it is initialized. +var DebugOptions bool + var X86 x86 // The booleans in x86 contain the correspondingly named cpuid feature bit. @@ -37,9 +42,9 @@ var PPC64 ppc64 // For ppc64x, it is safe to check only for ISA level starting on ISA v3.00, // since there are no optional categories. There are some exceptions that also -// require kernel support to work (darn, scv), so there are capability bits for +// require kernel support to work (darn, scv), so there are feature bits for // those as well. The minimum processor requirement is POWER8 (ISA 2.07), so we -// maintain some of the old capability checks for optional categories for +// maintain some of the old feature checks for optional categories for // safety. // The struct is padded to avoid false sharing. type ppc64 struct { @@ -63,15 +68,126 @@ var ARM64 arm64 // The booleans in arm64 contain the correspondingly named cpu feature bit. // The struct is padded to avoid false sharing. type arm64 struct { - _ [CacheLineSize]byte - HasFP bool - HasASIMD bool - HasEVTSTRM bool - HasAES bool - HasPMULL bool - HasSHA1 bool - HasSHA2 bool - HasCRC32 bool - HasATOMICS bool - _ [CacheLineSize]byte + _ [CacheLineSize]byte + HasFP bool + HasASIMD bool + HasEVTSTRM bool + HasAES bool + HasPMULL bool + HasSHA1 bool + HasSHA2 bool + HasCRC32 bool + HasATOMICS bool + HasFPHP bool + HasASIMDHP bool + HasCPUID bool + HasASIMDRDM bool + HasJSCVT bool + HasFCMA bool + HasLRCPC bool + HasDCPOP bool + HasSHA3 bool + HasSM3 bool + HasSM4 bool + HasASIMDDP bool + HasSHA512 bool + HasSVE bool + HasASIMDFHM bool + _ [CacheLineSize]byte +} + +var S390X s390x + +type s390x struct { + _ [CacheLineSize]byte + HasZArch bool // z architecture mode is active [mandatory] + HasSTFLE bool // store facility list extended [mandatory] + HasLDisp bool // long (20-bit) displacements [mandatory] + HasEImm bool // 32-bit immediates [mandatory] + HasDFP bool // decimal floating point + HasETF3Enhanced bool // ETF-3 enhanced + HasMSA bool // message security assist (CPACF) + HasAES bool // KM-AES{128,192,256} functions + HasAESCBC bool // KMC-AES{128,192,256} functions + HasAESCTR bool // KMCTR-AES{128,192,256} functions + HasAESGCM bool // KMA-GCM-AES{128,192,256} functions + HasGHASH bool // KIMD-GHASH function + HasSHA1 bool // K{I,L}MD-SHA-1 functions + HasSHA256 bool // K{I,L}MD-SHA-256 functions + HasSHA512 bool // K{I,L}MD-SHA-512 functions + HasVX bool // vector facility. Note: the runtime sets this when it processes auxv records. + _ [CacheLineSize]byte +} + +// Initialize examines the processor and sets the relevant variables above. +// This is called by the runtime package early in program initialization, +// before normal init functions are run. env is set by runtime on Linux and Darwin +// if go was compiled with GOEXPERIMENT=debugcpu. +func Initialize(env string) { + doinit() + processOptions(env) +} + +// options contains the cpu debug options that can be used in GODEBUGCPU. +// Options are arch dependent and are added by the arch specific doinit functions. +// Features that are mandatory for the specific GOARCH should not be added to options +// (e.g. SSE2 on amd64). +var options []option + +// Option names should be lower case. e.g. avx instead of AVX. +type option struct { + Name string + Feature *bool +} + +// processOptions disables CPU feature values based on the parsed env string. +// The env string is expected to be of the form feature1=0,feature2=0... +// where feature names is one of the architecture specifc list stored in the +// cpu packages options variable. If env contains all=0 then all capabilities +// referenced through the options variable are disabled. Other feature +// names and values other than 0 are silently ignored. +func processOptions(env string) { +field: + for env != "" { + field := "" + i := indexByte(env, ',') + if i < 0 { + field, env = env, "" + } else { + field, env = env[:i], env[i+1:] + } + i = indexByte(field, '=') + if i < 0 { + continue + } + key, value := field[:i], field[i+1:] + + // Only allow turning off CPU features by specifying '0'. + if value == "0" { + if key == "all" { + for _, v := range options { + *v.Feature = false + } + return + } else { + for _, v := range options { + if v.Name == key { + *v.Feature = false + continue field + } + } + } + } + } +} + +// indexByte returns the index of the first instance of c in s, +// or -1 if c is not present in s. +func indexByte(s string, c byte) int { + for i := 0; i < len(s); i++ { + if s[i] == c { + return i + } + } + return -1 } diff --git a/libgo/go/internal/cpu/cpu_386.go b/libgo/go/internal/cpu/cpu_386.go new file mode 100644 index 0000000..561c81f --- /dev/null +++ b/libgo/go/internal/cpu/cpu_386.go @@ -0,0 +1,7 @@ +// Copyright 2018 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. + +package cpu + +const GOARCH = "386" diff --git a/libgo/go/internal/cpu/cpu_amd64.go b/libgo/go/internal/cpu/cpu_amd64.go new file mode 100644 index 0000000..9b00153 --- /dev/null +++ b/libgo/go/internal/cpu/cpu_amd64.go @@ -0,0 +1,7 @@ +// Copyright 2018 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. + +package cpu + +const GOARCH = "amd64" diff --git a/libgo/go/internal/cpu/cpu_amd64p32.go b/libgo/go/internal/cpu/cpu_amd64p32.go new file mode 100644 index 0000000..177b14e --- /dev/null +++ b/libgo/go/internal/cpu/cpu_amd64p32.go @@ -0,0 +1,7 @@ +// Copyright 2018 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. + +package cpu + +const GOARCH = "amd64p32" diff --git a/libgo/go/internal/cpu/cpu_arm64.go b/libgo/go/internal/cpu/cpu_arm64.go index e1278a1..009f2a2 100644 --- a/libgo/go/internal/cpu/cpu_arm64.go +++ b/libgo/go/internal/cpu/cpu_arm64.go @@ -2,42 +2,99 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build arm64 - package cpu const CacheLineSize = 64 // arm64 doesn't have a 'cpuid' equivalent, so we rely on HWCAP/HWCAP2. -// These are linknamed in runtime/os_linux_arm64.go and are initialized by -// archauxv(). -var arm64_hwcap uint -var arm64_hwcap2 uint +// These are initialized by archauxv in runtime/os_linux_arm64.go. +// These should not be changed after they are initialized. +var HWCap uint +var HWCap2 uint // HWCAP/HWCAP2 bits. These are exposed by Linux. const ( - _ARM64_FEATURE_HAS_FP = (1 << 0) - _ARM64_FEATURE_HAS_ASIMD = (1 << 1) - _ARM64_FEATURE_HAS_EVTSTRM = (1 << 2) - _ARM64_FEATURE_HAS_AES = (1 << 3) - _ARM64_FEATURE_HAS_PMULL = (1 << 4) - _ARM64_FEATURE_HAS_SHA1 = (1 << 5) - _ARM64_FEATURE_HAS_SHA2 = (1 << 6) - _ARM64_FEATURE_HAS_CRC32 = (1 << 7) - _ARM64_FEATURE_HAS_ATOMICS = (1 << 8) + hwcap_FP = (1 << 0) + hwcap_ASIMD = (1 << 1) + hwcap_EVTSTRM = (1 << 2) + hwcap_AES = (1 << 3) + hwcap_PMULL = (1 << 4) + hwcap_SHA1 = (1 << 5) + hwcap_SHA2 = (1 << 6) + hwcap_CRC32 = (1 << 7) + hwcap_ATOMICS = (1 << 8) + hwcap_FPHP = (1 << 9) + hwcap_ASIMDHP = (1 << 10) + hwcap_CPUID = (1 << 11) + hwcap_ASIMDRDM = (1 << 12) + hwcap_JSCVT = (1 << 13) + hwcap_FCMA = (1 << 14) + hwcap_LRCPC = (1 << 15) + hwcap_DCPOP = (1 << 16) + hwcap_SHA3 = (1 << 17) + hwcap_SM3 = (1 << 18) + hwcap_SM4 = (1 << 19) + hwcap_ASIMDDP = (1 << 20) + hwcap_SHA512 = (1 << 21) + hwcap_SVE = (1 << 22) + hwcap_ASIMDFHM = (1 << 23) ) -func init() { +func doinit() { + options = []option{ + {"evtstrm", &ARM64.HasEVTSTRM}, + {"aes", &ARM64.HasAES}, + {"pmull", &ARM64.HasPMULL}, + {"sha1", &ARM64.HasSHA1}, + {"sha2", &ARM64.HasSHA2}, + {"crc32", &ARM64.HasCRC32}, + {"atomics", &ARM64.HasATOMICS}, + {"fphp", &ARM64.HasFPHP}, + {"asimdhp", &ARM64.HasASIMDHP}, + {"cpuid", &ARM64.HasCPUID}, + {"asimdrdm", &ARM64.HasASIMDRDM}, + {"jscvt", &ARM64.HasJSCVT}, + {"fcma", &ARM64.HasFCMA}, + {"lrcpc", &ARM64.HasLRCPC}, + {"dcpop", &ARM64.HasDCPOP}, + {"sha3", &ARM64.HasSHA3}, + {"sm3", &ARM64.HasSM3}, + {"sm4", &ARM64.HasSM4}, + {"asimddp", &ARM64.HasASIMDDP}, + {"sha512", &ARM64.HasSHA512}, + {"sve", &ARM64.HasSVE}, + {"asimdfhm", &ARM64.HasASIMDFHM}, + + // These capabilities should always be enabled on arm64: + // {"fp", &ARM64.HasFP}, + // {"asimd", &ARM64.HasASIMD}, + } + // HWCAP feature bits - ARM64.HasFP = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_FP) - ARM64.HasASIMD = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_ASIMD) - ARM64.HasEVTSTRM = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_EVTSTRM) - ARM64.HasAES = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_AES) - ARM64.HasPMULL = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_PMULL) - ARM64.HasSHA1 = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_SHA1) - ARM64.HasSHA2 = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_SHA2) - ARM64.HasCRC32 = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_CRC32) - ARM64.HasATOMICS = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_ATOMICS) + ARM64.HasFP = isSet(HWCap, hwcap_FP) + ARM64.HasASIMD = isSet(HWCap, hwcap_ASIMD) + ARM64.HasEVTSTRM = isSet(HWCap, hwcap_EVTSTRM) + ARM64.HasAES = isSet(HWCap, hwcap_AES) + ARM64.HasPMULL = isSet(HWCap, hwcap_PMULL) + ARM64.HasSHA1 = isSet(HWCap, hwcap_SHA1) + ARM64.HasSHA2 = isSet(HWCap, hwcap_SHA2) + ARM64.HasCRC32 = isSet(HWCap, hwcap_CRC32) + ARM64.HasATOMICS = isSet(HWCap, hwcap_ATOMICS) + ARM64.HasFPHP = isSet(HWCap, hwcap_FPHP) + ARM64.HasASIMDHP = isSet(HWCap, hwcap_ASIMDHP) + ARM64.HasCPUID = isSet(HWCap, hwcap_CPUID) + ARM64.HasASIMDRDM = isSet(HWCap, hwcap_ASIMDRDM) + ARM64.HasJSCVT = isSet(HWCap, hwcap_JSCVT) + ARM64.HasFCMA = isSet(HWCap, hwcap_FCMA) + ARM64.HasLRCPC = isSet(HWCap, hwcap_LRCPC) + ARM64.HasDCPOP = isSet(HWCap, hwcap_DCPOP) + ARM64.HasSHA3 = isSet(HWCap, hwcap_SHA3) + ARM64.HasSM3 = isSet(HWCap, hwcap_SM3) + ARM64.HasSM4 = isSet(HWCap, hwcap_SM4) + ARM64.HasASIMDDP = isSet(HWCap, hwcap_ASIMDDP) + ARM64.HasSHA512 = isSet(HWCap, hwcap_SHA512) + ARM64.HasSVE = isSet(HWCap, hwcap_SVE) + ARM64.HasASIMDFHM = isSet(HWCap, hwcap_ASIMDFHM) } func isSet(hwc uint, value uint) bool { diff --git a/libgo/go/internal/cpu/cpu_arm64_test.go b/libgo/go/internal/cpu/cpu_arm64_test.go new file mode 100644 index 0000000..f4c419a --- /dev/null +++ b/libgo/go/internal/cpu/cpu_arm64_test.go @@ -0,0 +1,26 @@ +// Copyright 2018 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. + +package cpu_test + +import ( + . "internal/cpu" + "runtime" + "testing" +) + +func TestARM64minimalFeatures(t *testing.T) { + switch runtime.GOOS { + case "linux", "android": + default: + t.Skipf("%s/arm64 is not supported", runtime.GOOS) + } + + if !ARM64.HasASIMD { + t.Fatalf("HasASIMD expected true, got false") + } + if !ARM64.HasFP { + t.Fatalf("HasFP expected true, got false") + } +} diff --git a/libgo/go/internal/cpu/cpu_gccgo.c b/libgo/go/internal/cpu/cpu_gccgo.c new file mode 100644 index 0000000..6625ddc --- /dev/null +++ b/libgo/go/internal/cpu/cpu_gccgo.c @@ -0,0 +1,66 @@ +// Copyright 2018 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. + +#include <stdint.h> + +#if defined(__i386__) || defined(__x86_64__) +#include <cpuid.h> +#include <x86intrin.h> +#endif + +#include "runtime.h" + +#if defined(__i386__) || defined(__x86_64__) + +struct cpuid_ret { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; +}; + +struct cpuid_ret cpuid(uint32_t, uint32_t) + __asm__(GOSYM_PREFIX "internal_cpu.cpuid") + __attribute__((no_split_stack)); + +struct cpuid_ret cpuid(uint32_t eaxArg, uint32_t ecxArg) { + unsigned int eax = 0; + unsigned int ebx = 0; + unsigned int ecx = 0; + unsigned int edx = 0; + struct cpuid_ret ret; + + __get_cpuid_count(eaxArg, ecxArg, &eax, &ebx, &ecx, &edx); + ret.eax = (uint32_t)(eax); + ret.ebx = (uint32_t)(ebx); + ret.ecx = (uint32_t)(ecx); + ret.edx = (uint32_t)(edx); + return ret; +} + +struct xgetbv_ret { + uint32_t eax; + uint32_t edx; +}; + +struct xgetbv_ret xgetbv(void) + __asm__(GOSYM_PREFIX "internal_cpu.xgetbv") + __attribute__((no_split_stack)); + +#pragma GCC push_options +#pragma GCC target("xsave") + +struct xgetbv_ret xgetbv(void) { + long long r; + struct xgetbv_ret ret; + + r = _xgetbv(0); + ret.eax = r & 0xffffffff; + ret.edx = r >> 32; + return ret; +} + +#pragma GCC pop_options + +#endif /* defined(__i386__) || defined(__x86_64__) */ diff --git a/libgo/go/internal/cpu/cpu_no_init.go b/libgo/go/internal/cpu/cpu_no_init.go new file mode 100644 index 0000000..1be4f29 --- /dev/null +++ b/libgo/go/internal/cpu/cpu_no_init.go @@ -0,0 +1,16 @@ +// Copyright 2018 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 !386 +// +build !amd64 +// +build !amd64p32 +// +build !arm64 +// +build !ppc64 +// +build !ppc64le +// +build !s390x + +package cpu + +func doinit() { +} diff --git a/libgo/go/internal/cpu/cpu_ppc64x.go b/libgo/go/internal/cpu/cpu_ppc64x.go index 7f09372..d3f02ef 100644 --- a/libgo/go/internal/cpu/cpu_ppc64x.go +++ b/libgo/go/internal/cpu/cpu_ppc64x.go @@ -9,10 +9,10 @@ package cpu const CacheLineSize = 128 // ppc64x doesn't have a 'cpuid' equivalent, so we rely on HWCAP/HWCAP2. -// These are linknamed in runtime/os_linux_ppc64x.go and are initialized by -// archauxv(). -var ppc64x_hwcap uint -var ppc64x_hwcap2 uint +// These are initialized by archauxv in runtime/os_linux_ppc64x.go. +// These should not be changed after they are initialized. +var HWCap uint +var HWCap2 uint // HWCAP/HWCAP2 bits. These are exposed by the kernel. const ( @@ -32,21 +32,35 @@ const ( _PPC_FEATURE2_SCV = 0x00100000 ) -func init() { +func doinit() { + options = []option{ + {"htm", &PPC64.HasHTM}, + {"htmnosc", &PPC64.HasHTMNOSC}, + {"darn", &PPC64.HasDARN}, + {"scv", &PPC64.HasSCV}, + + // These capabilities should always be enabled on ppc64 and ppc64le: + // {"vmx", &PPC64.HasVMX}, + // {"dfp", &PPC64.HasDFP}, + // {"vsx", &PPC64.HasVSX}, + // {"isel", &PPC64.HasISEL}, + // {"vcrypto", &PPC64.HasVCRYPTO}, + } + // HWCAP feature bits - PPC64.HasVMX = isSet(ppc64x_hwcap, _PPC_FEATURE_HAS_ALTIVEC) - PPC64.HasDFP = isSet(ppc64x_hwcap, _PPC_FEATURE_HAS_DFP) - PPC64.HasVSX = isSet(ppc64x_hwcap, _PPC_FEATURE_HAS_VSX) + PPC64.HasVMX = isSet(HWCap, _PPC_FEATURE_HAS_ALTIVEC) + PPC64.HasDFP = isSet(HWCap, _PPC_FEATURE_HAS_DFP) + PPC64.HasVSX = isSet(HWCap, _PPC_FEATURE_HAS_VSX) // HWCAP2 feature bits - PPC64.IsPOWER8 = isSet(ppc64x_hwcap2, _PPC_FEATURE2_ARCH_2_07) - PPC64.HasHTM = isSet(ppc64x_hwcap2, _PPC_FEATURE2_HAS_HTM) - PPC64.HasISEL = isSet(ppc64x_hwcap2, _PPC_FEATURE2_HAS_ISEL) - PPC64.HasVCRYPTO = isSet(ppc64x_hwcap2, _PPC_FEATURE2_HAS_VEC_CRYPTO) - PPC64.HasHTMNOSC = isSet(ppc64x_hwcap2, _PPC_FEATURE2_HTM_NOSC) - PPC64.IsPOWER9 = isSet(ppc64x_hwcap2, _PPC_FEATURE2_ARCH_3_00) - PPC64.HasDARN = isSet(ppc64x_hwcap2, _PPC_FEATURE2_DARN) - PPC64.HasSCV = isSet(ppc64x_hwcap2, _PPC_FEATURE2_SCV) + PPC64.IsPOWER8 = isSet(HWCap2, _PPC_FEATURE2_ARCH_2_07) + PPC64.HasHTM = isSet(HWCap2, _PPC_FEATURE2_HAS_HTM) + PPC64.HasISEL = isSet(HWCap2, _PPC_FEATURE2_HAS_ISEL) + PPC64.HasVCRYPTO = isSet(HWCap2, _PPC_FEATURE2_HAS_VEC_CRYPTO) + PPC64.HasHTMNOSC = isSet(HWCap2, _PPC_FEATURE2_HTM_NOSC) + PPC64.IsPOWER9 = isSet(HWCap2, _PPC_FEATURE2_ARCH_3_00) + PPC64.HasDARN = isSet(HWCap2, _PPC_FEATURE2_DARN) + PPC64.HasSCV = isSet(HWCap2, _PPC_FEATURE2_SCV) } func isSet(hwc uint, value uint) bool { diff --git a/libgo/go/internal/cpu/cpu_ppc64x_test.go b/libgo/go/internal/cpu/cpu_ppc64x_test.go new file mode 100644 index 0000000..9c43d1e --- /dev/null +++ b/libgo/go/internal/cpu/cpu_ppc64x_test.go @@ -0,0 +1,33 @@ +// Copyright 2018 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 ppc64 ppc64le + +package cpu_test + +import ( + . "internal/cpu" + "testing" +) + +func TestPPC64minimalFeatures(t *testing.T) { + if !PPC64.IsPOWER8 { + t.Fatalf("IsPOWER8 expected true, got false") + } + if !PPC64.HasVMX { + t.Fatalf("HasVMX expected true, got false") + } + if !PPC64.HasDFP { + t.Fatalf("HasDFP expected true, got false") + } + if !PPC64.HasVSX { + t.Fatalf("HasVSX expected true, got false") + } + if !PPC64.HasISEL { + t.Fatalf("HasISEL expected true, got false") + } + if !PPC64.HasVCRYPTO { + t.Fatalf("HasVCRYPTO expected true, got false") + } +} diff --git a/libgo/go/internal/cpu/cpu_s390x.go b/libgo/go/internal/cpu/cpu_s390x.go index 4455809..9dedb4c 100644 --- a/libgo/go/internal/cpu/cpu_s390x.go +++ b/libgo/go/internal/cpu/cpu_s390x.go @@ -5,3 +5,149 @@ package cpu const CacheLineSize = 256 + +// bitIsSet reports whether the bit at index is set. The bit index +// is in big endian order, so bit index 0 is the leftmost bit. +func bitIsSet(bits []uint64, index uint) bool { + return bits[index/64]&((1<<63)>>(index%64)) != 0 +} + +// function is the function code for the named function. +type function uint8 + +const ( + // KM{,A,C,CTR} function codes + aes128 function = 18 // AES-128 + aes192 = 19 // AES-192 + aes256 = 20 // AES-256 + + // K{I,L}MD function codes + sha1 = 1 // SHA-1 + sha256 = 2 // SHA-256 + sha512 = 3 // SHA-512 + + // KLMD function codes + ghash = 65 // GHASH +) + +// queryResult contains the result of a Query function +// call. Bits are numbered in big endian order so the +// leftmost bit (the MSB) is at index 0. +type queryResult struct { + bits [2]uint64 +} + +// Has reports whether the given functions are present. +func (q *queryResult) Has(fns ...function) bool { + if len(fns) == 0 { + panic("no function codes provided") + } + for _, f := range fns { + if !bitIsSet(q.bits[:], uint(f)) { + return false + } + } + return true +} + +// facility is a bit index for the named facility. +type facility uint8 + +const ( + // mandatory facilities + zarch facility = 1 // z architecture mode is active + stflef = 7 // store-facility-list-extended + ldisp = 18 // long-displacement + eimm = 21 // extended-immediate + + // miscellaneous facilities + dfp = 42 // decimal-floating-point + etf3eh = 30 // extended-translation 3 enhancement + + // cryptography facilities + msa = 17 // message-security-assist + msa3 = 76 // message-security-assist extension 3 + msa4 = 77 // message-security-assist extension 4 + msa5 = 57 // message-security-assist extension 5 + msa8 = 146 // message-security-assist extension 8 + + // Note: vx and highgprs are excluded because they require + // kernel support and so must be fetched from HWCAP. +) + +// facilityList contains the result of an STFLE call. +// Bits are numbered in big endian order so the +// leftmost bit (the MSB) is at index 0. +type facilityList struct { + bits [4]uint64 +} + +// Has reports whether the given facilities are present. +func (s *facilityList) Has(fs ...facility) bool { + if len(fs) == 0 { + panic("no facility bits provided") + } + for _, f := range fs { + if !bitIsSet(s.bits[:], uint(f)) { + return false + } + } + return true +} + +// The following feature detection functions are defined in cpu_s390x.s. +// They are likely to be expensive to call so the results should be cached. +func stfle() facilityList { panic("not implemented for gccgo") } +func kmQuery() queryResult { panic("not implemented for gccgo") } +func kmcQuery() queryResult { panic("not implemented for gccgo") } +func kmctrQuery() queryResult { panic("not implemented for gccgo") } +func kmaQuery() queryResult { panic("not implemented for gccgo") } +func kimdQuery() queryResult { panic("not implemented for gccgo") } +func klmdQuery() queryResult { panic("not implemented for gccgo") } + +func doinit() { + options = []option{ + {"zarch", &S390X.HasZArch}, + {"stfle", &S390X.HasSTFLE}, + {"ldisp", &S390X.HasLDisp}, + {"msa", &S390X.HasMSA}, + {"eimm", &S390X.HasEImm}, + {"dfp", &S390X.HasDFP}, + {"etf3eh", &S390X.HasETF3Enhanced}, + {"vx", &S390X.HasVX}, + } + + aes := []function{aes128, aes192, aes256} + facilities := stfle() + + S390X.HasZArch = facilities.Has(zarch) + S390X.HasSTFLE = facilities.Has(stflef) + S390X.HasLDisp = facilities.Has(ldisp) + S390X.HasEImm = facilities.Has(eimm) + S390X.HasDFP = facilities.Has(dfp) + S390X.HasETF3Enhanced = facilities.Has(etf3eh) + S390X.HasMSA = facilities.Has(msa) + + if S390X.HasMSA { + // cipher message + km, kmc := kmQuery(), kmcQuery() + S390X.HasAES = km.Has(aes...) + S390X.HasAESCBC = kmc.Has(aes...) + if facilities.Has(msa4) { + kmctr := kmctrQuery() + S390X.HasAESCTR = kmctr.Has(aes...) + } + if facilities.Has(msa8) { + kma := kmaQuery() + S390X.HasAESGCM = kma.Has(aes...) + } + + // compute message digest + kimd := kimdQuery() // intermediate (no padding) + klmd := klmdQuery() // last (padding) + S390X.HasSHA1 = kimd.Has(sha1) && klmd.Has(sha1) + S390X.HasSHA256 = kimd.Has(sha256) && klmd.Has(sha256) + S390X.HasSHA512 = kimd.Has(sha512) && klmd.Has(sha512) + S390X.HasGHASH = kimd.Has(ghash) // KLMD-GHASH does not exist + } +} diff --git a/libgo/go/internal/cpu/cpu_s390x_test.go b/libgo/go/internal/cpu/cpu_s390x_test.go new file mode 100644 index 0000000..d910bbe --- /dev/null +++ b/libgo/go/internal/cpu/cpu_s390x_test.go @@ -0,0 +1,63 @@ +// Copyright 2018 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. + +package cpu_test + +import ( + "errors" + . "internal/cpu" + "io/ioutil" + "regexp" + "testing" +) + +func getFeatureList() ([]string, error) { + cpuinfo, err := ioutil.ReadFile("/proc/cpuinfo") + if err != nil { + return nil, err + } + r := regexp.MustCompile("features\\s*:\\s*(.*)") + b := r.FindSubmatch(cpuinfo) + if len(b) < 2 { + return nil, errors.New("no feature list in /proc/cpuinfo") + } + return regexp.MustCompile("\\s+").Split(string(b[1]), -1), nil +} + +func TestS390XAgainstCPUInfo(t *testing.T) { + // mapping of linux feature strings to S390X fields + mapping := make(map[string]*bool) + for _, option := range Options { + mapping[option.Name] = option.Feature + } + + // these must be true on the machines Go supports + mandatory := make(map[string]bool) + mandatory["zarch"] = false + mandatory["eimm"] = false + mandatory["ldisp"] = false + mandatory["stfle"] = false + + features, err := getFeatureList() + if err != nil { + t.Error(err) + } + for _, feature := range features { + if _, ok := mandatory[feature]; ok { + mandatory[feature] = true + } + if flag, ok := mapping[feature]; ok { + if !*flag { + t.Errorf("feature '%v' not detected", feature) + } + } else { + t.Logf("no entry for '%v'", feature) + } + } + for k, v := range mandatory { + if !v { + t.Errorf("mandatory feature '%v' not detected", k) + } + } +} diff --git a/libgo/go/internal/cpu/cpu_test.go b/libgo/go/internal/cpu/cpu_test.go index 07b0243..d4115a1 100644 --- a/libgo/go/internal/cpu/cpu_test.go +++ b/libgo/go/internal/cpu/cpu_test.go @@ -5,46 +5,52 @@ package cpu_test import ( - "internal/cpu" - "runtime" + . "internal/cpu" + "internal/testenv" + "os" + "os/exec" + "strings" "testing" ) -func TestAMD64minimalFeatures(t *testing.T) { - if runtime.GOARCH == "amd64" { - if !cpu.X86.HasSSE2 { - t.Fatalf("HasSSE2 expected true, got false") - } +func MustHaveDebugOptionsEnabled(t *testing.T) { + if !DebugOptions { + t.Skipf("skipping test: cpu feature options not enabled") } } -func TestAVX2hasAVX(t *testing.T) { - if runtime.GOARCH == "amd64" { - if cpu.X86.HasAVX2 && !cpu.X86.HasAVX { - t.Fatalf("HasAVX expected true, got false") - } +func runDebugOptionsTest(t *testing.T, test string, options string) { + MustHaveDebugOptionsEnabled(t) + + testenv.MustHaveExec(t) + + env := "GODEBUGCPU=" + options + + cmd := exec.Command(os.Args[0], "-test.run="+test) + cmd.Env = append(cmd.Env, env) + + output, err := cmd.CombinedOutput() + got := strings.TrimSpace(string(output)) + want := "PASS" + if err != nil || got != want { + t.Fatalf("%s with %s: want %s, got %v", test, env, want, got) } } -func TestPPC64minimalFeatures(t *testing.T) { - if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" { - if !cpu.PPC64.IsPOWER8 { - t.Fatalf("IsPOWER8 expected true, got false") - } - if !cpu.PPC64.HasVMX { - t.Fatalf("HasVMX expected true, got false") - } - if !cpu.PPC64.HasDFP { - t.Fatalf("HasDFP expected true, got false") - } - if !cpu.PPC64.HasVSX { - t.Fatalf("HasVSX expected true, got false") - } - if !cpu.PPC64.HasISEL { - t.Fatalf("HasISEL expected true, got false") - } - if !cpu.PPC64.HasVCRYPTO { - t.Fatalf("HasVCRYPTO expected true, got false") +func TestDisableAllCapabilities(t *testing.T) { + runDebugOptionsTest(t, "TestAllCapabilitiesDisabled", "all=0") +} + +func TestAllCapabilitiesDisabled(t *testing.T) { + MustHaveDebugOptionsEnabled(t) + + if os.Getenv("GODEBUGCPU") != "all=0" { + t.Skipf("skipping test: GODEBUGCPU=all=0 not set") + } + + for _, o := range Options { + if got := *o.Feature; got != false { + t.Errorf("%v: expected false, got %v", o.Name, got) } } } diff --git a/libgo/go/internal/cpu/cpu_wasm.go b/libgo/go/internal/cpu/cpu_wasm.go new file mode 100644 index 0000000..1107a7a --- /dev/null +++ b/libgo/go/internal/cpu/cpu_wasm.go @@ -0,0 +1,7 @@ +// Copyright 2018 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. + +package cpu + +const CacheLineSize = 64 diff --git a/libgo/go/internal/cpu/cpu_x86.go b/libgo/go/internal/cpu/cpu_x86.go index 34c632f..7d9d3aa 100644 --- a/libgo/go/internal/cpu/cpu_x86.go +++ b/libgo/go/internal/cpu/cpu_x86.go @@ -14,7 +14,56 @@ func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) // xgetbv with ecx = 0 is implemented in cpu_x86.s. func xgetbv() (eax, edx uint32) -func init() { +const ( + // edx bits + cpuid_SSE2 = 1 << 26 + + // ecx bits + cpuid_SSE3 = 1 << 0 + cpuid_PCLMULQDQ = 1 << 1 + cpuid_SSSE3 = 1 << 9 + cpuid_FMA = 1 << 12 + cpuid_SSE41 = 1 << 19 + cpuid_SSE42 = 1 << 20 + cpuid_POPCNT = 1 << 23 + cpuid_AES = 1 << 25 + cpuid_OSXSAVE = 1 << 27 + cpuid_AVX = 1 << 28 + + // ebx bits + cpuid_BMI1 = 1 << 3 + cpuid_AVX2 = 1 << 5 + cpuid_BMI2 = 1 << 8 + cpuid_ERMS = 1 << 9 + cpuid_ADX = 1 << 19 +) + +func doinit() { + options = []option{ + {"adx", &X86.HasADX}, + {"aes", &X86.HasAES}, + {"avx", &X86.HasAVX}, + {"avx2", &X86.HasAVX2}, + {"bmi1", &X86.HasBMI1}, + {"bmi2", &X86.HasBMI2}, + {"erms", &X86.HasERMS}, + {"fma", &X86.HasFMA}, + {"pclmulqdq", &X86.HasPCLMULQDQ}, + {"popcnt", &X86.HasPOPCNT}, + {"sse3", &X86.HasSSE3}, + {"sse41", &X86.HasSSE41}, + {"sse42", &X86.HasSSE42}, + {"ssse3", &X86.HasSSSE3}, + + // sse2 set as last element so it can easily be removed again. See code below. + {"sse2", &X86.HasSSE2}, + } + + // Remove sse2 from options on amd64(p32) because SSE2 is a mandatory feature for these GOARCHs. + if GOARCH == "amd64" || GOARCH == "amd64p32" { + options = options[:len(options)-1] + } + maxID, _, _, _ := cpuid(0, 0) if maxID < 1 { @@ -22,40 +71,40 @@ func init() { } _, _, ecx1, edx1 := cpuid(1, 0) - X86.HasSSE2 = isSet(26, edx1) - - X86.HasSSE3 = isSet(0, ecx1) - X86.HasPCLMULQDQ = isSet(1, ecx1) - X86.HasSSSE3 = isSet(9, ecx1) - X86.HasFMA = isSet(12, ecx1) - X86.HasSSE41 = isSet(19, ecx1) - X86.HasSSE42 = isSet(20, ecx1) - X86.HasPOPCNT = isSet(23, ecx1) - X86.HasAES = isSet(25, ecx1) - X86.HasOSXSAVE = isSet(27, ecx1) + X86.HasSSE2 = isSet(edx1, cpuid_SSE2) + + X86.HasSSE3 = isSet(ecx1, cpuid_SSE3) + X86.HasPCLMULQDQ = isSet(ecx1, cpuid_PCLMULQDQ) + X86.HasSSSE3 = isSet(ecx1, cpuid_SSSE3) + X86.HasFMA = isSet(ecx1, cpuid_FMA) + X86.HasSSE41 = isSet(ecx1, cpuid_SSE41) + X86.HasSSE42 = isSet(ecx1, cpuid_SSE42) + X86.HasPOPCNT = isSet(ecx1, cpuid_POPCNT) + X86.HasAES = isSet(ecx1, cpuid_AES) + X86.HasOSXSAVE = isSet(ecx1, cpuid_OSXSAVE) osSupportsAVX := false // For XGETBV, OSXSAVE bit is required and sufficient. if X86.HasOSXSAVE { eax, _ := xgetbv() // Check if XMM and YMM registers have OS support. - osSupportsAVX = isSet(1, eax) && isSet(2, eax) + osSupportsAVX = isSet(eax, 1<<1) && isSet(eax, 1<<2) } - X86.HasAVX = isSet(28, ecx1) && osSupportsAVX + X86.HasAVX = isSet(ecx1, cpuid_AVX) && osSupportsAVX if maxID < 7 { return } _, ebx7, _, _ := cpuid(7, 0) - X86.HasBMI1 = isSet(3, ebx7) - X86.HasAVX2 = isSet(5, ebx7) && osSupportsAVX - X86.HasBMI2 = isSet(8, ebx7) - X86.HasERMS = isSet(9, ebx7) - X86.HasADX = isSet(19, ebx7) + X86.HasBMI1 = isSet(ebx7, cpuid_BMI1) + X86.HasAVX2 = isSet(ebx7, cpuid_AVX2) && osSupportsAVX + X86.HasBMI2 = isSet(ebx7, cpuid_BMI2) + X86.HasERMS = isSet(ebx7, cpuid_ERMS) + X86.HasADX = isSet(ebx7, cpuid_ADX) } -func isSet(bitpos uint, value uint32) bool { - return value&(1<<bitpos) != 0 +func isSet(hwc uint32, value uint32) bool { + return hwc&value != 0 } diff --git a/libgo/go/internal/cpu/cpu_x86_test.go b/libgo/go/internal/cpu/cpu_x86_test.go new file mode 100644 index 0000000..d03306c --- /dev/null +++ b/libgo/go/internal/cpu/cpu_x86_test.go @@ -0,0 +1,47 @@ +// Copyright 2018 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 386 amd64 amd64p32 + +package cpu_test + +import ( + . "internal/cpu" + "os" + "runtime" + "testing" +) + +func TestAMD64minimalFeatures(t *testing.T) { + if runtime.GOARCH != "amd64" { + return + } + + if !X86.HasSSE2 { + t.Fatalf("HasSSE2 expected true, got false") + } +} + +func TestX86ifAVX2hasAVX(t *testing.T) { + if X86.HasAVX2 && !X86.HasAVX { + t.Fatalf("HasAVX expected true when HasAVX2 is true, got false") + } +} + +func TestDisableSSE2(t *testing.T) { + runDebugOptionsTest(t, "TestSSE2DebugOption", "sse2=0") +} + +func TestSSE2DebugOption(t *testing.T) { + MustHaveDebugOptionsEnabled(t) + + if os.Getenv("GODEBUGCPU") != "sse2=0" { + t.Skipf("skipping test: GODEBUGCPU=sse2=0 not set") + } + + want := runtime.GOARCH != "386" // SSE2 can only be disabled on 386. + if got := X86.HasSSE2; got != want { + t.Errorf("X86.HasSSE2 on %s expected %v, got %v", runtime.GOARCH, want, got) + } +} diff --git a/libgo/go/internal/cpu/export_test.go b/libgo/go/internal/cpu/export_test.go new file mode 100644 index 0000000..91bfc1b --- /dev/null +++ b/libgo/go/internal/cpu/export_test.go @@ -0,0 +1,9 @@ +// Copyright 2018 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. + +package cpu + +var ( + Options = options +) diff --git a/libgo/go/internal/goroot/gc.go b/libgo/go/internal/goroot/gc.go new file mode 100644 index 0000000..ce4c1e5 --- /dev/null +++ b/libgo/go/internal/goroot/gc.go @@ -0,0 +1,141 @@ +// Copyright 2018 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 gc + +package goroot + +import ( + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "sync" +) + +// IsStandardPackage returns whether path is a standard package, +// given goroot and compiler. +func IsStandardPackage(goroot, compiler, path string) bool { + switch compiler { + case "gc": + dir := filepath.Join(goroot, "src", path) + _, err := os.Stat(dir) + return err == nil + case "gccgo": + return gccgoSearch.isStandard(path) + default: + panic("unknown compiler " + compiler) + } +} + +// gccgoSearch holds the gccgo search directories. +type gccgoDirs struct { + once sync.Once + dirs []string +} + +// gccgoSearch is used to check whether a gccgo package exists in the +// standard library. +var gccgoSearch gccgoDirs + +// init finds the gccgo search directories. If this fails it leaves dirs == nil. +func (gd *gccgoDirs) init() { + gccgo := os.Getenv("GCCGO") + if gccgo == "" { + gccgo = "gccgo" + } + bin, err := exec.LookPath(gccgo) + if err != nil { + return + } + + allDirs, err := exec.Command(bin, "-print-search-dirs").Output() + if err != nil { + return + } + versionB, err := exec.Command(bin, "-dumpversion").Output() + if err != nil { + return + } + version := strings.TrimSpace(string(versionB)) + machineB, err := exec.Command(bin, "-dumpmachine").Output() + if err != nil { + return + } + machine := strings.TrimSpace(string(machineB)) + + dirsEntries := strings.Split(string(allDirs), "\n") + const prefix = "libraries: =" + var dirs []string + for _, dirEntry := range dirsEntries { + if strings.HasPrefix(dirEntry, prefix) { + dirs = filepath.SplitList(strings.TrimPrefix(dirEntry, prefix)) + break + } + } + if len(dirs) == 0 { + return + } + + var lastDirs []string + for _, dir := range dirs { + goDir := filepath.Join(dir, "go", version) + if fi, err := os.Stat(goDir); err == nil && fi.IsDir() { + gd.dirs = append(gd.dirs, goDir) + goDir = filepath.Join(goDir, machine) + if fi, err = os.Stat(goDir); err == nil && fi.IsDir() { + gd.dirs = append(gd.dirs, goDir) + } + } + if fi, err := os.Stat(dir); err == nil && fi.IsDir() { + lastDirs = append(lastDirs, dir) + } + } + gd.dirs = append(gd.dirs, lastDirs...) +} + +// isStandard returns whether path is a standard library for gccgo. +func (gd *gccgoDirs) isStandard(path string) bool { + // Quick check: if the first path component has a '.', it's not + // in the standard library. This skips most GOPATH directories. + i := strings.Index(path, "/") + if i < 0 { + i = len(path) + } + if strings.Contains(path[:i], ".") { + return false + } + + if path == "unsafe" { + // Special case. + return true + } + + gd.once.Do(gd.init) + if gd.dirs == nil { + // We couldn't find the gccgo search directories. + // Best guess, since the first component did not contain + // '.', is that this is a standard library package. + return true + } + + for _, dir := range gd.dirs { + full := filepath.Join(dir, path) + pkgdir, pkg := filepath.Split(full) + for _, p := range [...]string{ + full, + full + ".gox", + pkgdir + "lib" + pkg + ".so", + pkgdir + "lib" + pkg + ".a", + full + ".o", + } { + if fi, err := os.Stat(p); err == nil && !fi.IsDir() { + return true + } + } + } + + return false +} diff --git a/libgo/go/internal/goroot/gccgo.go b/libgo/go/internal/goroot/gccgo.go new file mode 100644 index 0000000..098e77d --- /dev/null +++ b/libgo/go/internal/goroot/gccgo.go @@ -0,0 +1,27 @@ +// Copyright 2018 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 gccgo + +package goroot + +import ( + "os" + "path/filepath" +) + +// IsStandardPackage returns whether path is a standard package, +// given goroot and compiler. +func IsStandardPackage(goroot, compiler, path string) bool { + switch compiler { + case "gc": + dir := filepath.Join(goroot, "src", path) + _, err := os.Stat(dir) + return err == nil + case "gccgo": + return stdpkg[path] + default: + panic("unknown compiler " + compiler) + } +} diff --git a/libgo/go/internal/poll/fd_mutex.go b/libgo/go/internal/poll/fd_mutex.go index 2ba7de7..0a8ee6f 100644 --- a/libgo/go/internal/poll/fd_mutex.go +++ b/libgo/go/internal/poll/fd_mutex.go @@ -34,6 +34,8 @@ const ( mutexWMask = (1<<20 - 1) << 43 ) +const overflowMsg = "too many concurrent operations on a single file or socket (max 1048575)" + // Read operations must do rwlock(true)/rwunlock(true). // // Write operations must do rwlock(false)/rwunlock(false). @@ -56,7 +58,7 @@ func (mu *fdMutex) incref() bool { } new := old + mutexRef if new&mutexRefMask == 0 { - panic("inconsistent poll.fdMutex") + panic(overflowMsg) } if atomic.CompareAndSwapUint64(&mu.state, old, new) { return true @@ -75,7 +77,7 @@ func (mu *fdMutex) increfAndClose() bool { // Mark as closed and acquire a reference. new := (old | mutexClosed) + mutexRef if new&mutexRefMask == 0 { - panic("inconsistent poll.fdMutex") + panic(overflowMsg) } // Remove all read and write waiters. new &^= mutexRMask | mutexWMask @@ -136,13 +138,13 @@ func (mu *fdMutex) rwlock(read bool) bool { // Lock is free, acquire it. new = (old | mutexBit) + mutexRef if new&mutexRefMask == 0 { - panic("inconsistent poll.fdMutex") + panic(overflowMsg) } } else { // Wait for lock. new = old + mutexWait if new&mutexMask == 0 { - panic("inconsistent poll.fdMutex") + panic(overflowMsg) } } if atomic.CompareAndSwapUint64(&mu.state, old, new) { diff --git a/libgo/go/internal/poll/fd_mutex_test.go b/libgo/go/internal/poll/fd_mutex_test.go index bab81c6..2c53c45 100644 --- a/libgo/go/internal/poll/fd_mutex_test.go +++ b/libgo/go/internal/poll/fd_mutex_test.go @@ -8,6 +8,7 @@ import ( . "internal/poll" "math/rand" "runtime" + "strings" "testing" "time" ) @@ -121,6 +122,27 @@ func TestMutexPanic(t *testing.T) { mu.RWUnlock(false) } +func TestMutexOverflowPanic(t *testing.T) { + defer func() { + r := recover() + if r == nil { + t.Fatal("did not panic") + } + msg, ok := r.(string) + if !ok { + t.Fatalf("unexpected panic type %T", r) + } + if !strings.Contains(msg, "too many") || strings.Contains(msg, "inconsistent") { + t.Fatalf("wrong panic message %q", msg) + } + }() + + var mu1 FDMutex + for i := 0; i < 1<<21; i++ { + mu1.Incref() + } +} + func TestMutexStress(t *testing.T) { P := 8 N := int(1e6) diff --git a/libgo/go/internal/poll/fd_poll_nacl.go b/libgo/go/internal/poll/fd_poll_nacljs.go index 2df3003..832dddb 100644 --- a/libgo/go/internal/poll/fd_poll_nacl.go +++ b/libgo/go/internal/poll/fd_poll_nacljs.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build nacl js,wasm + package poll import ( @@ -40,6 +42,9 @@ func (pd *pollDesc) wait(mode int, isFile bool) error { if pd.closing { return errClosing(isFile) } + if isFile { // TODO(neelance): wasm: Use callbacks from JS to block until the read/write finished. + return nil + } return ErrTimeout } diff --git a/libgo/go/internal/poll/fd_posix.go b/libgo/go/internal/poll/fd_posix.go index 4e6e355..23ad7a0 100644 --- a/libgo/go/internal/poll/fd_posix.go +++ b/libgo/go/internal/poll/fd_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows package poll diff --git a/libgo/go/internal/poll/fd_unix.go b/libgo/go/internal/poll/fd_unix.go index cfd441e..28bc32f 100644 --- a/libgo/go/internal/poll/fd_unix.go +++ b/libgo/go/internal/poll/fd_unix.go @@ -2,13 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package poll import ( "io" "runtime" + "sync/atomic" "syscall" ) @@ -30,6 +31,9 @@ type FD struct { // Semaphore signaled when file is closed. csema uint32 + // Non-zero if this file has been set to blocking mode. + isBlocking uint32 + // Whether this is a streaming descriptor, as opposed to a // packet-based descriptor like a UDP socket. Immutable. IsStream bool @@ -40,9 +44,6 @@ type FD struct { // Whether this is a file rather than a network socket. isFile bool - - // Whether this file has been set to blocking mode. - isBlocking bool } // Init initializes the FD. The Sysfd field should already be set. @@ -56,10 +57,16 @@ func (fd *FD) Init(net string, pollable bool) error { fd.isFile = true } if !pollable { - fd.isBlocking = true + fd.isBlocking = 1 return nil } - return fd.pd.init(fd) + err := fd.pd.init(fd) + if err != nil { + // If we could not initialize the runtime poller, + // assume we are using blocking mode. + fd.isBlocking = 1 + } + return err } // Destroy closes the file descriptor. This is called when there are @@ -96,7 +103,9 @@ func (fd *FD) Close() error { // reference, it is already closed. Only wait if the file has // not been set to blocking mode, as otherwise any current I/O // may be blocking, and that would block the Close. - if !fd.isBlocking { + // No need for an atomic read of isBlocking, increfAndClose means + // we have exclusive access to fd. + if fd.isBlocking == 0 { runtime_Semacquire(&fd.csema) } @@ -118,7 +127,10 @@ func (fd *FD) SetBlocking() error { return err } defer fd.decref() - fd.isBlocking = true + // Atomic store so that concurrent calls to SetBlocking + // do not cause a race condition. isBlocking only ever goes + // from 0 to 1 so there is no real race here. + atomic.StoreUint32(&fd.isBlocking, 1) return syscall.SetNonblock(fd.Sysfd, false) } @@ -433,6 +445,59 @@ func (fd *FD) Fstat(s *syscall.Stat_t) error { return syscall.Fstat(fd.Sysfd, s) } +// Use a helper function to call fcntl. This is defined in C in +// libgo/runtime. +//extern __go_fcntl_uintptr +func fcntl(uintptr, uintptr, uintptr) (uintptr, uintptr) + +// tryDupCloexec indicates whether F_DUPFD_CLOEXEC should be used. +// If the kernel doesn't support it, this is set to 0. +var tryDupCloexec = int32(1) + +// DupCloseOnExec dups fd and marks it close-on-exec. +func DupCloseOnExec(fd int) (int, string, error) { + if atomic.LoadInt32(&tryDupCloexec) == 1 { + syscall.Entersyscall() + r0, errno := fcntl(uintptr(fd), syscall.F_DUPFD_CLOEXEC, 0) + syscall.Exitsyscall() + e1 := syscall.Errno(errno) + switch e1 { + case 0: + return int(r0), "", nil + case syscall.EINVAL, syscall.ENOSYS: + // Old kernel, or js/wasm (which returns + // ENOSYS). Fall back to the portable way from + // now on. + atomic.StoreInt32(&tryDupCloexec, 0) + default: + return -1, "fcntl", e1 + } + } + return dupCloseOnExecOld(fd) +} + +// dupCloseOnExecUnixOld is the traditional way to dup an fd and +// set its O_CLOEXEC bit, using two system calls. +func dupCloseOnExecOld(fd int) (int, string, error) { + syscall.ForkLock.RLock() + defer syscall.ForkLock.RUnlock() + newfd, err := syscall.Dup(fd) + if err != nil { + return -1, "dup", err + } + syscall.CloseOnExec(newfd) + return newfd, "", nil +} + +// Dup duplicates the file descriptor. +func (fd *FD) Dup() (int, string, error) { + if err := fd.incref(); err != nil { + return -1, "", err + } + defer fd.decref() + return DupCloseOnExec(fd.Sysfd) +} + // On Unix variants only, expose the IO event for the net code. // WaitWrite waits until data can be read from fd. diff --git a/libgo/go/internal/poll/fd_windows.go b/libgo/go/internal/poll/fd_windows.go index 187908b..d04d332 100644 --- a/libgo/go/internal/poll/fd_windows.go +++ b/libgo/go/internal/poll/fd_windows.go @@ -34,19 +34,18 @@ var ( var canCancelIO bool // determines if CancelIoEx API is present -// This package uses SetFileCompletionNotificationModes Windows API -// to skip calling GetQueuedCompletionStatus if an IO operation completes -// synchronously. Unfortuently SetFileCompletionNotificationModes is not -// available on Windows XP. Also there is a known bug where -// SetFileCompletionNotificationModes crashes on some systems -// (see http://support.microsoft.com/kb/2568167 for details). +// This package uses the SetFileCompletionNotificationModes Windows +// API to skip calling GetQueuedCompletionStatus if an IO operation +// completes synchronously. There is a known bug where +// SetFileCompletionNotificationModes crashes on some systems (see +// https://support.microsoft.com/kb/2568167 for details). var useSetFileCompletionNotificationModes bool // determines is SetFileCompletionNotificationModes is present and safe to use // checkSetFileCompletionNotificationModes verifies that // SetFileCompletionNotificationModes Windows API is present // on the system and is safe to use. -// See http://support.microsoft.com/kb/2568167 for details. +// See https://support.microsoft.com/kb/2568167 for details. func checkSetFileCompletionNotificationModes() { err := syscall.LoadSetFileCompletionNotificationModes() if err != nil { @@ -225,6 +224,10 @@ func (s *ioSrv) ExecIO(o *operation, submit func(o *operation) error) (int, erro // All is good. Extract our IO results and return. if o.errno != 0 { err = syscall.Errno(o.errno) + // More data available. Return back the size of received data. + if err == syscall.ERROR_MORE_DATA || err == windows.WSAEMSGSIZE { + return int(o.qty), err + } return 0, err } return int(o.qty), nil @@ -379,7 +382,7 @@ func (fd *FD) Init(net string, pollable bool) (string, error) { // We do not use events, so we can skip them always. flags := uint8(syscall.FILE_SKIP_SET_EVENT_ON_HANDLE) // It's not safe to skip completion notifications for UDP: - // http://blogs.technet.com/b/winserverperformance/archive/2008/06/26/designing-applications-for-high-performance-part-iii.aspx + // https://blogs.technet.com/b/winserverperformance/archive/2008/06/26/designing-applications-for-high-performance-part-iii.aspx if net == "tcp" { flags |= syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS } @@ -909,12 +912,46 @@ func (fd *FD) RawControl(f func(uintptr)) error { // RawRead invokes the user-defined function f for a read operation. func (fd *FD) RawRead(f func(uintptr) bool) error { - return errors.New("not implemented") + if err := fd.readLock(); err != nil { + return err + } + defer fd.readUnlock() + for { + if f(uintptr(fd.Sysfd)) { + return nil + } + + // Use a zero-byte read as a way to get notified when this + // socket is readable. h/t https://stackoverflow.com/a/42019668/332798 + o := &fd.rop + o.InitBuf(nil) + if !fd.IsStream { + o.flags |= windows.MSG_PEEK + } + _, err := rsrv.ExecIO(o, func(o *operation) error { + return syscall.WSARecv(o.fd.Sysfd, &o.buf, 1, &o.qty, &o.flags, &o.o, nil) + }) + if err == windows.WSAEMSGSIZE { + // expected with a 0-byte peek, ignore. + } else if err != nil { + return err + } + } } // RawWrite invokes the user-defined function f for a write operation. func (fd *FD) RawWrite(f func(uintptr) bool) error { - return errors.New("not implemented") + if err := fd.writeLock(); err != nil { + return err + } + defer fd.writeUnlock() + + if f(uintptr(fd.Sysfd)) { + return nil + } + + // TODO(tmm1): find a way to detect socket writability + return syscall.EWINDOWS } func sockaddrToRaw(sa syscall.Sockaddr) (unsafe.Pointer, int32, error) { diff --git a/libgo/go/internal/poll/hook_cloexec.go b/libgo/go/internal/poll/hook_cloexec.go index 73df6ed..5c93bda 100644 --- a/libgo/go/internal/poll/hook_cloexec.go +++ b/libgo/go/internal/poll/hook_cloexec.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build dragonfly freebsd linux +// +build dragonfly freebsd linux netbsd openbsd package poll diff --git a/libgo/go/internal/poll/hook_unix.go b/libgo/go/internal/poll/hook_unix.go index 4cf36cc..a7512b1 100644 --- a/libgo/go/internal/poll/hook_unix.go +++ b/libgo/go/internal/poll/hook_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package poll diff --git a/libgo/go/internal/poll/sendfile_windows.go b/libgo/go/internal/poll/sendfile_windows.go index 4a15b75..dc93e85 100644 --- a/libgo/go/internal/poll/sendfile_windows.go +++ b/libgo/go/internal/poll/sendfile_windows.go @@ -25,6 +25,16 @@ func SendFile(fd *FD, src syscall.Handle, n int64) (int64, error) { o := &fd.wop o.qty = uint32(n) o.handle = src + + // TODO(brainman): skip calling syscall.Seek if OS allows it + curpos, err := syscall.Seek(o.handle, 0, 1) + if err != nil { + return 0, err + } + + o.o.Offset = uint32(curpos) + o.o.OffsetHigh = uint32(curpos >> 32) + done, err := wsrv.ExecIO(o, func(o *operation) error { return syscall.TransmitFile(o.fd.Sysfd, o.handle, o.qty, 0, &o.o, nil, syscall.TF_WRITE_BEHIND) }) diff --git a/libgo/go/internal/poll/sock_cloexec.go b/libgo/go/internal/poll/sock_cloexec.go index 0d5c8bd..691cb8e 100644 --- a/libgo/go/internal/poll/sock_cloexec.go +++ b/libgo/go/internal/poll/sock_cloexec.go @@ -5,7 +5,7 @@ // This file implements sysSocket and accept for platforms that // provide a fast path for setting SetNonblock and CloseOnExec. -// +build dragonfly freebsd linux +// +build dragonfly freebsd linux netbsd openbsd package poll diff --git a/libgo/go/internal/poll/splice_linux.go b/libgo/go/internal/poll/splice_linux.go new file mode 100644 index 0000000..aa237e5 --- /dev/null +++ b/libgo/go/internal/poll/splice_linux.go @@ -0,0 +1,183 @@ +// Copyright 2018 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. + +package poll + +import ( + "sync/atomic" + "syscall" + "unsafe" +) + +const ( + // spliceNonblock makes calls to splice(2) non-blocking. + spliceNonblock = 0x2 + + // maxSpliceSize is the maximum amount of data Splice asks + // the kernel to move in a single call to splice(2). + maxSpliceSize = 4 << 20 +) + +// Splice transfers at most remain bytes of data from src to dst, using the +// splice system call to minimize copies of data from and to userspace. +// +// Splice creates a temporary pipe, to serve as a buffer for the data transfer. +// src and dst must both be stream-oriented sockets. +// +// If err != nil, sc is the system call which caused the error. +func Splice(dst, src *FD, remain int64) (written int64, handled bool, sc string, err error) { + prfd, pwfd, sc, err := newTempPipe() + if err != nil { + return 0, false, sc, err + } + defer destroyTempPipe(prfd, pwfd) + // From here on, the operation should be considered handled, + // even if Splice doesn't transfer any data. + var inPipe, n int + for err == nil && remain > 0 { + max := maxSpliceSize + if int64(max) > remain { + max = int(remain) + } + inPipe, err = spliceDrain(pwfd, src, max) + // spliceDrain should never return EAGAIN, so if err != nil, + // Splice cannot continue. If inPipe == 0 && err == nil, + // src is at EOF, and the transfer is complete. + if err != nil || (inPipe == 0 && err == nil) { + break + } + n, err = splicePump(dst, prfd, inPipe) + if n > 0 { + written += int64(n) + remain -= int64(n) + } + } + if err != nil { + return written, true, "splice", err + } + return written, true, "", nil +} + +// spliceDrain moves data from a socket to a pipe. +// +// Invariant: when entering spliceDrain, the pipe is empty. It is either in its +// initial state, or splicePump has emptied it previously. +// +// Given this, spliceDrain can reasonably assume that the pipe is ready for +// writing, so if splice returns EAGAIN, it must be because the socket is not +// ready for reading. +// +// If spliceDrain returns (0, nil), src is at EOF. +func spliceDrain(pipefd int, sock *FD, max int) (int, error) { + if err := sock.readLock(); err != nil { + return 0, err + } + defer sock.readUnlock() + if err := sock.pd.prepareRead(sock.isFile); err != nil { + return 0, err + } + for { + n, err := splice(pipefd, sock.Sysfd, max, spliceNonblock) + if err != syscall.EAGAIN { + return n, err + } + if err := sock.pd.waitRead(sock.isFile); err != nil { + return n, err + } + } +} + +// splicePump moves all the buffered data from a pipe to a socket. +// +// Invariant: when entering splicePump, there are exactly inPipe +// bytes of data in the pipe, from a previous call to spliceDrain. +// +// By analogy to the condition from spliceDrain, splicePump +// only needs to poll the socket for readiness, if splice returns +// EAGAIN. +// +// If splicePump cannot move all the data in a single call to +// splice(2), it loops over the buffered data until it has written +// all of it to the socket. This behavior is similar to the Write +// step of an io.Copy in userspace. +func splicePump(sock *FD, pipefd int, inPipe int) (int, error) { + if err := sock.writeLock(); err != nil { + return 0, err + } + defer sock.writeUnlock() + if err := sock.pd.prepareWrite(sock.isFile); err != nil { + return 0, err + } + written := 0 + for inPipe > 0 { + n, err := splice(sock.Sysfd, pipefd, inPipe, spliceNonblock) + // Here, the condition n == 0 && err == nil should never be + // observed, since Splice controls the write side of the pipe. + if n > 0 { + inPipe -= n + written += n + continue + } + if err != syscall.EAGAIN { + return written, err + } + if err := sock.pd.waitWrite(sock.isFile); err != nil { + return written, err + } + } + return written, nil +} + +// splice wraps the splice system call. Since the current implementation +// only uses splice on sockets and pipes, the offset arguments are unused. +// splice returns int instead of int64, because callers never ask it to +// move more data in a single call than can fit in an int32. +func splice(out int, in int, max int, flags int) (int, error) { + n, err := syscall.Splice(in, nil, out, nil, max, flags) + return int(n), err +} + +var disableSplice unsafe.Pointer + +// newTempPipe sets up a temporary pipe for a splice operation. +func newTempPipe() (prfd, pwfd int, sc string, err error) { + p := (*bool)(atomic.LoadPointer(&disableSplice)) + if p != nil && *p { + return -1, -1, "splice", syscall.EINVAL + } + + var fds [2]int + // pipe2 was added in 2.6.27 and our minimum requirement is 2.6.23, so it + // might not be implemented. Falling back to pipe is possible, but prior to + // 2.6.29 splice returns -EAGAIN instead of 0 when the connection is + // closed. + const flags = syscall.O_CLOEXEC | syscall.O_NONBLOCK + if err := syscall.Pipe2(fds[:], flags); err != nil { + return -1, -1, "pipe2", err + } + + if p == nil { + p = new(bool) + defer atomic.StorePointer(&disableSplice, unsafe.Pointer(p)) + + // F_GETPIPE_SZ was added in 2.6.35, which does not have the -EAGAIN bug. + if _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fds[0]), syscall.F_GETPIPE_SZ, 0); errno != 0 { + *p = true + destroyTempPipe(fds[0], fds[1]) + return -1, -1, "fcntl", errno + } + } + + return fds[0], fds[1], "", nil +} + +// destroyTempPipe destroys a temporary pipe. +func destroyTempPipe(prfd, pwfd int) error { + err := CloseFunc(prfd) + err1 := CloseFunc(pwfd) + if err == nil { + return err1 + } + return err +} diff --git a/libgo/go/internal/poll/sys_cloexec.go b/libgo/go/internal/poll/sys_cloexec.go index fb5f2bc..64e4612 100644 --- a/libgo/go/internal/poll/sys_cloexec.go +++ b/libgo/go/internal/poll/sys_cloexec.go @@ -5,7 +5,7 @@ // This file implements sysSocket and accept for platforms that do not // provide a fast path for setting SetNonblock and CloseOnExec. -// +build aix darwin nacl netbsd openbsd solaris +// +build aix darwin js,wasm nacl solaris package poll diff --git a/libgo/go/internal/syscall/unix/nonblocking.go b/libgo/go/internal/syscall/unix/nonblocking.go new file mode 100644 index 0000000..da7f8bd --- /dev/null +++ b/libgo/go/internal/syscall/unix/nonblocking.go @@ -0,0 +1,23 @@ +// Copyright 2018 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 dragonfly freebsd linux netbsd openbsd solaris + +package unix + +import ( + "syscall" + _ "unsafe" // for go:linkname +) + +//extern __go_fcntl +func syscall_fcntl(fd int32, cmd int32, arg int32) int32 + +func IsNonblock(fd int) (nonblocking bool, err error) { + flag := syscall_fcntl(int32(fd), syscall.F_GETFL, 0) + if flag < 0 { + return false, syscall.GetErrno() + } + return flag&syscall.O_NONBLOCK != 0, nil +} diff --git a/libgo/go/internal/syscall/unix/nonblocking_js.go b/libgo/go/internal/syscall/unix/nonblocking_js.go new file mode 100644 index 0000000..a360b53 --- /dev/null +++ b/libgo/go/internal/syscall/unix/nonblocking_js.go @@ -0,0 +1,11 @@ +// Copyright 2018 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 js,wasm + +package unix + +func IsNonblock(fd int) (nonblocking bool, err error) { + return false, nil +} diff --git a/libgo/go/internal/syscall/unix/nonblocking_nacl.go b/libgo/go/internal/syscall/unix/nonblocking_nacl.go new file mode 100644 index 0000000..ff67c75 --- /dev/null +++ b/libgo/go/internal/syscall/unix/nonblocking_nacl.go @@ -0,0 +1,9 @@ +// Copyright 2018 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. + +package unix + +func IsNonblock(fd int) (nonblocking bool, err error) { + return false, nil +} diff --git a/libgo/go/internal/syscall/windows/exec_windows_test.go b/libgo/go/internal/syscall/windows/exec_windows_test.go index 94fd95b..283d7ce 100644 --- a/libgo/go/internal/syscall/windows/exec_windows_test.go +++ b/libgo/go/internal/syscall/windows/exec_windows_test.go @@ -17,10 +17,6 @@ import ( ) func TestRunAtLowIntegrity(t *testing.T) { - if isWindowsXP(t) { - t.Skip("Windows XP does not support windows integrity levels") - } - if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { wil, err := getProcessIntegrityLevel() if err != nil { @@ -56,15 +52,6 @@ func TestRunAtLowIntegrity(t *testing.T) { } } -func isWindowsXP(t *testing.T) bool { - v, err := syscall.GetVersion() - if err != nil { - t.Fatalf("GetVersion failed: %v", err) - } - major := byte(v) - return major < 6 -} - const ( sidWilLow = `S-1-16-4096` ) diff --git a/libgo/go/internal/syscall/windows/registry/key.go b/libgo/go/internal/syscall/windows/registry/key.go index 62144d3..cc3d0c7 100644 --- a/libgo/go/internal/syscall/windows/registry/key.go +++ b/libgo/go/internal/syscall/windows/registry/key.go @@ -93,12 +93,10 @@ func OpenKey(k Key, path string, access uint32) (Key, error) { // The parameter n controls the number of returned names, // analogous to the way os.File.Readdirnames works. func (k Key) ReadSubKeyNames(n int) ([]string, error) { - ki, err := k.Stat() - if err != nil { - return nil, err - } - names := make([]string, 0, ki.SubKeyCount) - buf := make([]uint16, ki.MaxSubKeyLen+1) // extra room for terminating zero byte + names := make([]string, 0) + // Registry key size limit is 255 bytes and described there: + // https://msdn.microsoft.com/library/windows/desktop/ms724872.aspx + buf := make([]uint16, 256) //plus extra room for terminating zero byte loopItems: for i := uint32(0); ; i++ { if n > 0 { diff --git a/libgo/go/internal/syscall/windows/registry/registry_test.go b/libgo/go/internal/syscall/windows/registry/registry_test.go index 56069d7..c77329c 100644 --- a/libgo/go/internal/syscall/windows/registry/registry_test.go +++ b/libgo/go/internal/syscall/windows/registry/registry_test.go @@ -28,7 +28,7 @@ func randKeyName(prefix string) string { } func TestReadSubKeyNames(t *testing.T) { - k, err := registry.OpenKey(registry.CLASSES_ROOT, "TypeLib", registry.ENUMERATE_SUB_KEYS|registry.QUERY_VALUE) + k, err := registry.OpenKey(registry.CLASSES_ROOT, "TypeLib", registry.ENUMERATE_SUB_KEYS) if err != nil { t.Fatal(err) } diff --git a/libgo/go/internal/syscall/windows/registry/zsyscall_windows.go b/libgo/go/internal/syscall/windows/registry/zsyscall_windows.go index a3a1f5f..c3f3a8a 100644 --- a/libgo/go/internal/syscall/windows/registry/zsyscall_windows.go +++ b/libgo/go/internal/syscall/windows/registry/zsyscall_windows.go @@ -1,4 +1,4 @@ -// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT +// Code generated by 'go generate'; DO NOT EDIT. package registry diff --git a/libgo/go/internal/syscall/windows/security_windows.go b/libgo/go/internal/syscall/windows/security_windows.go index 14ea425..4a2dfc0 100644 --- a/libgo/go/internal/syscall/windows/security_windows.go +++ b/libgo/go/internal/syscall/windows/security_windows.go @@ -81,3 +81,48 @@ const ( TokenPrimary TokenType = 1 TokenImpersonation TokenType = 2 ) + +//sys GetProfilesDirectory(dir *uint16, dirLen *uint32) (err error) = userenv.GetProfilesDirectoryW + +const ( + LG_INCLUDE_INDIRECT = 0x1 + MAX_PREFERRED_LENGTH = 0xFFFFFFFF +) + +type LocalGroupUserInfo0 struct { + Name *uint16 +} + +type UserInfo4 struct { + Name *uint16 + Password *uint16 + PasswordAge uint32 + Priv uint32 + HomeDir *uint16 + Comment *uint16 + Flags uint32 + ScriptPath *uint16 + AuthFlags uint32 + FullName *uint16 + UsrComment *uint16 + Parms *uint16 + Workstations *uint16 + LastLogon uint32 + LastLogoff uint32 + AcctExpires uint32 + MaxStorage uint32 + UnitsPerWeek uint32 + LogonHours *byte + BadPwCount uint32 + NumLogons uint32 + LogonServer *uint16 + CountryCode uint32 + CodePage uint32 + UserSid *syscall.SID + PrimaryGroupID uint32 + Profile *uint16 + HomeDirDrive *uint16 + PasswordExpired uint32 +} + +//sys NetUserGetLocalGroups(serverName *uint16, userName *uint16, level uint32, flags uint32, buf **byte, prefMaxLen uint32, entriesRead *uint32, totalEntries *uint32) (neterr error) = netapi32.NetUserGetLocalGroups diff --git a/libgo/go/internal/syscall/windows/symlink_windows.go b/libgo/go/internal/syscall/windows/symlink_windows.go new file mode 100644 index 0000000..cc2163e --- /dev/null +++ b/libgo/go/internal/syscall/windows/symlink_windows.go @@ -0,0 +1,14 @@ +// Copyright 2018 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. + +package windows + +import "syscall" + +const ( + ERROR_INVALID_PARAMETER syscall.Errno = 87 + + // symlink support for CreateSymbolicLink() starting with Windows 10 (1703, v10.0.14972) + SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2 +) diff --git a/libgo/go/internal/syscall/windows/syscall_windows.go b/libgo/go/internal/syscall/windows/syscall_windows.go index b531f89..66fe932 100644 --- a/libgo/go/internal/syscall/windows/syscall_windows.go +++ b/libgo/go/internal/syscall/windows/syscall_windows.go @@ -12,6 +12,7 @@ import ( const ( ERROR_SHARING_VIOLATION syscall.Errno = 32 + ERROR_INVALID_NAME syscall.Errno = 123 ERROR_NO_UNICODE_TRANSLATION syscall.Errno = 1113 ) @@ -122,6 +123,7 @@ const ( WSAEMSGSIZE syscall.Errno = 10040 + MSG_PEEK = 0x2 MSG_TRUNC = 0x0100 MSG_CTRUNC = 0x0200 diff --git a/libgo/go/internal/syscall/windows/zsyscall_windows.go b/libgo/go/internal/syscall/windows/zsyscall_windows.go index bdca80c..550a8a5 100644 --- a/libgo/go/internal/syscall/windows/zsyscall_windows.go +++ b/libgo/go/internal/syscall/windows/zsyscall_windows.go @@ -1,4 +1,4 @@ -// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT +// Code generated by 'go generate'; DO NOT EDIT. package windows @@ -41,6 +41,7 @@ var ( modws2_32 = syscall.NewLazyDLL(sysdll.Add("ws2_32.dll")) modnetapi32 = syscall.NewLazyDLL(sysdll.Add("netapi32.dll")) modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll")) + moduserenv = syscall.NewLazyDLL(sysdll.Add("userenv.dll")) modpsapi = syscall.NewLazyDLL(sysdll.Add("psapi.dll")) procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") @@ -62,6 +63,8 @@ var ( procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges") procDuplicateTokenEx = modadvapi32.NewProc("DuplicateTokenEx") procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation") + procGetProfilesDirectoryW = moduserenv.NewProc("GetProfilesDirectoryW") + procNetUserGetLocalGroups = modnetapi32.NewProc("NetUserGetLocalGroups") procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") ) @@ -287,6 +290,26 @@ func SetTokenInformation(tokenHandle syscall.Token, tokenInformationClass uint32 return } +func GetProfilesDirectory(dir *uint16, dirLen *uint32) (err error) { + r1, _, e1 := syscall.Syscall(procGetProfilesDirectoryW.Addr(), 2, uintptr(unsafe.Pointer(dir)), uintptr(unsafe.Pointer(dirLen)), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func NetUserGetLocalGroups(serverName *uint16, userName *uint16, level uint32, flags uint32, buf **byte, prefMaxLen uint32, entriesRead *uint32, totalEntries *uint32) (neterr error) { + r0, _, _ := syscall.Syscall9(procNetUserGetLocalGroups.Addr(), 8, uintptr(unsafe.Pointer(serverName)), uintptr(unsafe.Pointer(userName)), uintptr(level), uintptr(flags), uintptr(unsafe.Pointer(buf)), uintptr(prefMaxLen), uintptr(unsafe.Pointer(entriesRead)), uintptr(unsafe.Pointer(totalEntries)), 0) + if r0 != 0 { + neterr = syscall.Errno(r0) + } + return +} + func GetProcessMemoryInfo(handle syscall.Handle, memCounters *PROCESS_MEMORY_COUNTERS, cb uint32) (err error) { r1, _, e1 := syscall.Syscall(procGetProcessMemoryInfo.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(memCounters)), uintptr(cb)) if r1 == 0 { diff --git a/libgo/go/internal/testenv/testenv.go b/libgo/go/internal/testenv/testenv.go index 87d01d2..1518690 100644 --- a/libgo/go/internal/testenv/testenv.go +++ b/libgo/go/internal/testenv/testenv.go @@ -48,7 +48,7 @@ func HasGoBuild() bool { return false } switch runtime.GOOS { - case "android", "nacl": + case "android", "nacl", "js": return false case "darwin": if strings.HasPrefix(runtime.GOARCH, "arm") { @@ -128,7 +128,7 @@ func GoTool() (string, error) { // using os.StartProcess or (more commonly) exec.Command. func HasExec() bool { switch runtime.GOOS { - case "nacl": + case "nacl", "js": return false case "darwin": if strings.HasPrefix(runtime.GOARCH, "arm") { @@ -166,13 +166,16 @@ func MustHaveExec(t testing.TB) { // HasExternalNetwork reports whether the current system can use // external (non-localhost) networks. func HasExternalNetwork() bool { - return !testing.Short() + return !testing.Short() && runtime.GOOS != "nacl" && runtime.GOOS != "js" } // MustHaveExternalNetwork checks that the current system can use // external (non-localhost) networks. // If not, MustHaveExternalNetwork calls t.Skip with an explanation. func MustHaveExternalNetwork(t testing.TB) { + if runtime.GOOS == "nacl" || runtime.GOOS == "js" { + t.Skipf("skipping test: no external network on %s", runtime.GOOS) + } if testing.Short() { t.Skipf("skipping test: no external network in -short mode") } diff --git a/libgo/go/internal/testenv/testenv_notwin.go b/libgo/go/internal/testenv/testenv_notwin.go index 3853973..d8ce6cd 100644 --- a/libgo/go/internal/testenv/testenv_notwin.go +++ b/libgo/go/internal/testenv/testenv_notwin.go @@ -18,7 +18,3 @@ func hasSymlink() (ok bool, reason string) { return true, "" } - -func IsWindowsXP() bool { - return false -} diff --git a/libgo/go/internal/testenv/testenv_windows.go b/libgo/go/internal/testenv/testenv_windows.go index 4a7da5f..eb8d6ac 100644 --- a/libgo/go/internal/testenv/testenv_windows.go +++ b/libgo/go/internal/testenv/testenv_windows.go @@ -46,12 +46,3 @@ func hasSymlink() (ok bool, reason string) { return false, "" } - -func IsWindowsXP() bool { - v, err := syscall.GetVersion() - if err != nil { - panic("GetVersion failed: " + err.Error()) - } - major := byte(v) - return major < 6 -} diff --git a/libgo/go/internal/trace/goroutines.go b/libgo/go/internal/trace/goroutines.go index 923a157..2d7d3aa 100644 --- a/libgo/go/internal/trace/goroutines.go +++ b/libgo/go/internal/trace/goroutines.go @@ -4,7 +4,9 @@ package trace -// GDesc contains statistics about execution of a single goroutine. +import "sort" + +// GDesc contains statistics and execution details of a single goroutine. type GDesc struct { ID uint64 Name string @@ -13,6 +15,37 @@ type GDesc struct { StartTime int64 EndTime int64 + // List of regions in the goroutine, sorted based on the start time. + Regions []*UserRegionDesc + + // Statistics of execution time during the goroutine execution. + GExecutionStat + + *gdesc // private part. +} + +// UserRegionDesc represents a region and goroutine execution stats +// while the region was active. +type UserRegionDesc struct { + TaskID uint64 + Name string + + // Region start event. Normally EvUserRegion start event or nil, + // but can be EvGoCreate event if the region is a synthetic + // region representing task inheritance from the parent goroutine. + Start *Event + + // Region end event. Normally EvUserRegion end event or nil, + // but can be EvGoStop or EvGoEnd event if the goroutine + // terminated without explicitely ending the region. + End *Event + + GExecutionStat +} + +// GExecutionStat contains statistics about a goroutine's execution +// during a period of time. +type GExecutionStat struct { ExecTime int64 SchedWaitTime int64 IOTime int64 @@ -21,8 +54,84 @@ type GDesc struct { GCTime int64 SweepTime int64 TotalTime int64 +} + +// sub returns the stats v-s. +func (s GExecutionStat) sub(v GExecutionStat) (r GExecutionStat) { + r = s + r.ExecTime -= v.ExecTime + r.SchedWaitTime -= v.SchedWaitTime + r.IOTime -= v.IOTime + r.BlockTime -= v.BlockTime + r.SyscallTime -= v.SyscallTime + r.GCTime -= v.GCTime + r.SweepTime -= v.SweepTime + r.TotalTime -= v.TotalTime + return r +} + +// snapshotStat returns the snapshot of the goroutine execution statistics. +// This is called as we process the ordered trace event stream. lastTs and +// activeGCStartTime are used to process pending statistics if this is called +// before any goroutine end event. +func (g *GDesc) snapshotStat(lastTs, activeGCStartTime int64) (ret GExecutionStat) { + ret = g.GExecutionStat + + if g.gdesc == nil { + return ret // finalized GDesc. No pending state. + } + + if activeGCStartTime != 0 { // terminating while GC is active + if g.CreationTime < activeGCStartTime { + ret.GCTime += lastTs - activeGCStartTime + } else { + // The goroutine's lifetime completely overlaps + // with a GC. + ret.GCTime += lastTs - g.CreationTime + } + } + + if g.TotalTime == 0 { + ret.TotalTime = lastTs - g.CreationTime + } + + if g.lastStartTime != 0 { + ret.ExecTime += lastTs - g.lastStartTime + } + if g.blockNetTime != 0 { + ret.IOTime += lastTs - g.blockNetTime + } + if g.blockSyncTime != 0 { + ret.BlockTime += lastTs - g.blockSyncTime + } + if g.blockSyscallTime != 0 { + ret.SyscallTime += lastTs - g.blockSyscallTime + } + if g.blockSchedTime != 0 { + ret.SchedWaitTime += lastTs - g.blockSchedTime + } + if g.blockSweepTime != 0 { + ret.SweepTime += lastTs - g.blockSweepTime + } + return ret +} - *gdesc // private part +// finalize is called when processing a goroutine end event or at +// the end of trace processing. This finalizes the execution stat +// and any active regions in the goroutine, in which case trigger is nil. +func (g *GDesc) finalize(lastTs, activeGCStartTime int64, trigger *Event) { + if trigger != nil { + g.EndTime = trigger.Ts + } + finalStat := g.snapshotStat(lastTs, activeGCStartTime) + + g.GExecutionStat = finalStat + for _, s := range g.activeRegions { + s.End = trigger + s.GExecutionStat = finalStat.sub(s.GExecutionStat) + g.Regions = append(g.Regions, s) + } + *(g.gdesc) = gdesc{} } // gdesc is a private part of GDesc that is required only during analysis. @@ -34,19 +143,34 @@ type gdesc struct { blockSweepTime int64 blockGCTime int64 blockSchedTime int64 + + activeRegions []*UserRegionDesc // stack of active regions } // GoroutineStats generates statistics for all goroutines in the trace. func GoroutineStats(events []*Event) map[uint64]*GDesc { gs := make(map[uint64]*GDesc) var lastTs int64 - var gcStartTime int64 + var gcStartTime int64 // gcStartTime == 0 indicates gc is inactive. for _, ev := range events { lastTs = ev.Ts switch ev.Type { case EvGoCreate: g := &GDesc{ID: ev.Args[0], CreationTime: ev.Ts, gdesc: new(gdesc)} g.blockSchedTime = ev.Ts + // When a goroutine is newly created, inherit the + // task of the active region. For ease handling of + // this case, we create a fake region description with + // the task id. + if creatorG := gs[ev.G]; creatorG != nil && len(creatorG.gdesc.activeRegions) > 0 { + regions := creatorG.gdesc.activeRegions + s := regions[len(regions)-1] + if s.TaskID != 0 { + g.gdesc.activeRegions = []*UserRegionDesc{ + {TaskID: s.TaskID, Start: ev}, + } + } + } gs[g.ID] = g case EvGoStart, EvGoStartLabel: g := gs[ev.G] @@ -64,28 +188,31 @@ func GoroutineStats(events []*Event) map[uint64]*GDesc { } case EvGoEnd, EvGoStop: g := gs[ev.G] - g.ExecTime += ev.Ts - g.lastStartTime - g.TotalTime = ev.Ts - g.CreationTime - g.EndTime = ev.Ts + g.finalize(ev.Ts, gcStartTime, ev) case EvGoBlockSend, EvGoBlockRecv, EvGoBlockSelect, EvGoBlockSync, EvGoBlockCond: g := gs[ev.G] g.ExecTime += ev.Ts - g.lastStartTime + g.lastStartTime = 0 g.blockSyncTime = ev.Ts case EvGoSched, EvGoPreempt: g := gs[ev.G] g.ExecTime += ev.Ts - g.lastStartTime + g.lastStartTime = 0 g.blockSchedTime = ev.Ts case EvGoSleep, EvGoBlock: g := gs[ev.G] g.ExecTime += ev.Ts - g.lastStartTime + g.lastStartTime = 0 case EvGoBlockNet: g := gs[ev.G] g.ExecTime += ev.Ts - g.lastStartTime + g.lastStartTime = 0 g.blockNetTime = ev.Ts case EvGoBlockGC: g := gs[ev.G] g.ExecTime += ev.Ts - g.lastStartTime + g.lastStartTime = 0 g.blockGCTime = ev.Ts case EvGoUnblock: g := gs[ev.Args[0]] @@ -101,6 +228,7 @@ func GoroutineStats(events []*Event) map[uint64]*GDesc { case EvGoSysBlock: g := gs[ev.G] g.ExecTime += ev.Ts - g.lastStartTime + g.lastStartTime = 0 g.blockSyscallTime = ev.Ts case EvGoSysExit: g := gs[ev.G] @@ -125,36 +253,62 @@ func GoroutineStats(events []*Event) map[uint64]*GDesc { gcStartTime = ev.Ts case EvGCDone: for _, g := range gs { - if g.EndTime == 0 { + if g.EndTime != 0 { + continue + } + if gcStartTime < g.CreationTime { + g.GCTime += ev.Ts - g.CreationTime + } else { g.GCTime += ev.Ts - gcStartTime } } + gcStartTime = 0 // indicates gc is inactive. + case EvUserRegion: + g := gs[ev.G] + switch mode := ev.Args[1]; mode { + case 0: // region start + g.activeRegions = append(g.activeRegions, &UserRegionDesc{ + Name: ev.SArgs[0], + TaskID: ev.Args[0], + Start: ev, + GExecutionStat: g.snapshotStat(lastTs, gcStartTime), + }) + case 1: // region end + var sd *UserRegionDesc + if regionStk := g.activeRegions; len(regionStk) > 0 { + n := len(regionStk) + sd = regionStk[n-1] + regionStk = regionStk[:n-1] // pop + g.activeRegions = regionStk + } else { + sd = &UserRegionDesc{ + Name: ev.SArgs[0], + TaskID: ev.Args[0], + } + } + sd.GExecutionStat = g.snapshotStat(lastTs, gcStartTime).sub(sd.GExecutionStat) + sd.End = ev + g.Regions = append(g.Regions, sd) + } } } for _, g := range gs { - if g.TotalTime == 0 { - g.TotalTime = lastTs - g.CreationTime - } - if g.EndTime == 0 { - g.EndTime = lastTs - } - if g.blockNetTime != 0 { - g.IOTime += lastTs - g.blockNetTime - g.blockNetTime = 0 - } - if g.blockSyncTime != 0 { - g.BlockTime += lastTs - g.blockSyncTime - g.blockSyncTime = 0 - } - if g.blockSyscallTime != 0 { - g.SyscallTime += lastTs - g.blockSyscallTime - g.blockSyscallTime = 0 - } - if g.blockSchedTime != 0 { - g.SchedWaitTime += lastTs - g.blockSchedTime - g.blockSchedTime = 0 - } + g.finalize(lastTs, gcStartTime, nil) + + // sort based on region start time + sort.Slice(g.Regions, func(i, j int) bool { + x := g.Regions[i].Start + y := g.Regions[j].Start + if x == nil { + return true + } + if y == nil { + return false + } + return x.Ts < y.Ts + }) + g.gdesc = nil } diff --git a/libgo/go/internal/trace/parser.go b/libgo/go/internal/trace/parser.go index c7954f0..c371ff3 100644 --- a/libgo/go/internal/trace/parser.go +++ b/libgo/go/internal/trace/parser.go @@ -55,6 +55,8 @@ type Event struct { // for blocking GoSysCall: the associated GoSysExit // for GoSysExit: the next GoStart // for GCMarkAssistStart: the associated GCMarkAssistDone + // for UserTaskCreate: the UserTaskEnd + // for UserRegion: if the start region, the corresponding UserRegion end event Link *Event } @@ -106,10 +108,7 @@ func parse(r io.Reader, bin string) (int, ParseResult, error) { if err != nil { return 0, ParseResult{}, err } - events, err = removeFutile(events) - if err != nil { - return 0, ParseResult{}, err - } + events = removeFutile(events) err = postProcessTrace(ver, events) if err != nil { return 0, ParseResult{}, err @@ -130,9 +129,10 @@ func parse(r io.Reader, bin string) (int, ParseResult, error) { // rawEvent is a helper type used during parsing. type rawEvent struct { - off int - typ byte - args []uint64 + off int + typ byte + args []uint64 + sargs []string } // readTrace does wire-format parsing and verification. @@ -150,7 +150,7 @@ func readTrace(r io.Reader) (ver int, events []rawEvent, strings map[uint64]stri return } switch ver { - case 1005, 1007, 1008, 1009, 1010: + case 1005, 1007, 1008, 1009, 1010, 1011: // Note: When adding a new version, add canned traces // from the old version to the test suite using mkcanned.bash. break @@ -259,11 +259,34 @@ func readTrace(r io.Reader) (ver int, events []rawEvent, strings map[uint64]stri return } } + switch ev.typ { + case EvUserLog: // EvUserLog records are followed by a value string of length ev.args[len(ev.args)-1] + var s string + s, off, err = readStr(r, off) + ev.sargs = append(ev.sargs, s) + } events = append(events, ev) } return } +func readStr(r io.Reader, off0 int) (s string, off int, err error) { + var sz uint64 + sz, off, err = readVal(r, off0) + if err != nil || sz == 0 { + return "", off, err + } + if sz > 1e6 { + return "", off, fmt.Errorf("string at offset %d is too large (len=%d)", off, sz) + } + buf := make([]byte, sz) + n, err := io.ReadFull(r, buf) + if err != nil || sz != uint64(n) { + return "", off + n, fmt.Errorf("failed to read trace at offset %d: read %v, want %v, error %v", off, n, sz, err) + } + return string(buf), off + n, nil +} + // parseHeader parses trace header of the form "go 1.7 trace\x00\x00\x00\x00" // and returns parsed version as 1007. func parseHeader(buf []byte) (int, error) { @@ -416,6 +439,15 @@ func parseEvents(ver int, rawEvents []rawEvent, strings map[uint64]string) (even lastG = 0 case EvGoSysExit, EvGoWaiting, EvGoInSyscall: e.G = e.Args[0] + case EvUserTaskCreate: + // e.Args 0: taskID, 1:parentID, 2:nameID + e.SArgs = []string{strings[e.Args[2]]} + case EvUserRegion: + // e.Args 0: taskID, 1: mode, 2:nameID + e.SArgs = []string{strings[e.Args[2]]} + case EvUserLog: + // e.Args 0: taskID, 1:keyID, 2: stackID + e.SArgs = []string{strings[e.Args[1]], raw.sargs[0]} } batches[lastP] = append(batches[lastP], e) } @@ -470,7 +502,7 @@ func parseEvents(ver int, rawEvents []rawEvent, strings map[uint64]string) (even // ahead and acquired the mutex before the first goroutine is scheduled, // so the first goroutine has to block again. Such wakeups happen on buffered // channels and sync.Mutex, but are generally not interesting for end user. -func removeFutile(events []*Event) ([]*Event, error) { +func removeFutile(events []*Event) []*Event { // Two non-trivial aspects: // 1. A goroutine can be preempted during a futile wakeup and migrate to another P. // We want to remove all of that. @@ -517,7 +549,7 @@ func removeFutile(events []*Event) ([]*Event, error) { newEvents = append(newEvents, ev) } } - return newEvents, nil + return newEvents } // ErrTimeOrder is returned by Parse when the trace contains @@ -551,6 +583,8 @@ func postProcessTrace(ver int, events []*Event) error { gs := make(map[uint64]gdesc) ps := make(map[int]pdesc) + tasks := make(map[uint64]*Event) // task id to task creation events + activeRegions := make(map[uint64][]*Event) // goroutine id to stack of regions gs[0] = gdesc{state: gRunning} var evGC, evSTW *Event @@ -695,6 +729,15 @@ func postProcessTrace(ver int, events []*Event) error { g.evStart = nil g.state = gDead p.g = 0 + + if ev.Type == EvGoEnd { // flush all active regions + regions := activeRegions[ev.G] + for _, s := range regions { + s.Link = ev + } + delete(activeRegions, ev.G) + } + case EvGoSched, EvGoPreempt: if err := checkRunning(p, g, ev, false); err != nil { return err @@ -756,6 +799,42 @@ func postProcessTrace(ver int, events []*Event) error { g.evStart.Link = ev g.evStart = nil p.g = 0 + case EvUserTaskCreate: + taskid := ev.Args[0] + if prevEv, ok := tasks[taskid]; ok { + return fmt.Errorf("task id conflicts (id:%d), %q vs %q", taskid, ev, prevEv) + } + tasks[ev.Args[0]] = ev + case EvUserTaskEnd: + taskid := ev.Args[0] + if taskCreateEv, ok := tasks[taskid]; ok { + taskCreateEv.Link = ev + delete(tasks, taskid) + } + case EvUserRegion: + mode := ev.Args[1] + regions := activeRegions[ev.G] + if mode == 0 { // region start + activeRegions[ev.G] = append(regions, ev) // push + } else if mode == 1 { // region end + n := len(regions) + if n > 0 { // matching region start event is in the trace. + s := regions[n-1] + if s.Args[0] != ev.Args[0] || s.SArgs[0] != ev.SArgs[0] { // task id, region name mismatch + return fmt.Errorf("misuse of region in goroutine %d: span end %q when the inner-most active span start event is %q", ev.G, ev, s) + } + // Link region start event with span end event + s.Link = ev + + if n > 1 { + activeRegions[ev.G] = regions[:n-1] + } else { + delete(activeRegions, ev.G) + } + } + } else { + return fmt.Errorf("invalid user region mode: %q", ev) + } } gs[ev.G] = g @@ -869,12 +948,20 @@ func Print(events []*Event) { // PrintEvent dumps the event to stdout. For debugging. func PrintEvent(ev *Event) { + fmt.Printf("%s\n", ev) +} + +func (ev *Event) String() string { desc := EventDescriptions[ev.Type] - fmt.Printf("%v %v p=%v g=%v off=%v", ev.Ts, desc.Name, ev.P, ev.G, ev.Off) + w := new(bytes.Buffer) + fmt.Fprintf(w, "%v %v p=%v g=%v off=%v", ev.Ts, desc.Name, ev.P, ev.G, ev.Off) for i, a := range desc.Args { - fmt.Printf(" %v=%v", a, ev.Args[i]) + fmt.Fprintf(w, " %v=%v", a, ev.Args[i]) + } + for i, a := range desc.SArgs { + fmt.Fprintf(w, " %v=%v", a, ev.SArgs[i]) } - fmt.Printf("\n") + return w.String() } // argNum returns total number of args for the event accounting for timestamps, @@ -967,7 +1054,11 @@ const ( EvGoBlockGC = 42 // goroutine blocks on GC assist [timestamp, stack] EvGCMarkAssistStart = 43 // GC mark assist start [timestamp, stack] EvGCMarkAssistDone = 44 // GC mark assist done [timestamp] - EvCount = 45 + EvUserTaskCreate = 45 // trace.NewContext [timestamp, internal task id, internal parent id, stack, name string] + EvUserTaskEnd = 46 // end of task [timestamp, internal task id, stack] + EvUserRegion = 47 // trace.WithRegion [timestamp, internal task id, mode(0:start, 1:end), stack, name string] + EvUserLog = 48 // trace.Log [timestamp, internal id, key string id, stack, value string] + EvCount = 49 ) var EventDescriptions = [EvCount]struct { @@ -975,50 +1066,55 @@ var EventDescriptions = [EvCount]struct { minVersion int Stack bool Args []string + SArgs []string // string arguments }{ - EvNone: {"None", 1005, false, []string{}}, - EvBatch: {"Batch", 1005, false, []string{"p", "ticks"}}, // in 1.5 format it was {"p", "seq", "ticks"} - EvFrequency: {"Frequency", 1005, false, []string{"freq"}}, // in 1.5 format it was {"freq", "unused"} - EvStack: {"Stack", 1005, false, []string{"id", "siz"}}, - EvGomaxprocs: {"Gomaxprocs", 1005, true, []string{"procs"}}, - EvProcStart: {"ProcStart", 1005, false, []string{"thread"}}, - EvProcStop: {"ProcStop", 1005, false, []string{}}, - EvGCStart: {"GCStart", 1005, true, []string{"seq"}}, // in 1.5 format it was {} - EvGCDone: {"GCDone", 1005, false, []string{}}, - EvGCSTWStart: {"GCSTWStart", 1005, false, []string{"kind"}}, // <= 1.9, args was {} (implicitly {0}) - EvGCSTWDone: {"GCSTWDone", 1005, false, []string{}}, - EvGCSweepStart: {"GCSweepStart", 1005, true, []string{}}, - EvGCSweepDone: {"GCSweepDone", 1005, false, []string{"swept", "reclaimed"}}, // before 1.9, format was {} - EvGoCreate: {"GoCreate", 1005, true, []string{"g", "stack"}}, - EvGoStart: {"GoStart", 1005, false, []string{"g", "seq"}}, // in 1.5 format it was {"g"} - EvGoEnd: {"GoEnd", 1005, false, []string{}}, - EvGoStop: {"GoStop", 1005, true, []string{}}, - EvGoSched: {"GoSched", 1005, true, []string{}}, - EvGoPreempt: {"GoPreempt", 1005, true, []string{}}, - EvGoSleep: {"GoSleep", 1005, true, []string{}}, - EvGoBlock: {"GoBlock", 1005, true, []string{}}, - EvGoUnblock: {"GoUnblock", 1005, true, []string{"g", "seq"}}, // in 1.5 format it was {"g"} - EvGoBlockSend: {"GoBlockSend", 1005, true, []string{}}, - EvGoBlockRecv: {"GoBlockRecv", 1005, true, []string{}}, - EvGoBlockSelect: {"GoBlockSelect", 1005, true, []string{}}, - EvGoBlockSync: {"GoBlockSync", 1005, true, []string{}}, - EvGoBlockCond: {"GoBlockCond", 1005, true, []string{}}, - EvGoBlockNet: {"GoBlockNet", 1005, true, []string{}}, - EvGoSysCall: {"GoSysCall", 1005, true, []string{}}, - EvGoSysExit: {"GoSysExit", 1005, false, []string{"g", "seq", "ts"}}, - EvGoSysBlock: {"GoSysBlock", 1005, false, []string{}}, - EvGoWaiting: {"GoWaiting", 1005, false, []string{"g"}}, - EvGoInSyscall: {"GoInSyscall", 1005, false, []string{"g"}}, - EvHeapAlloc: {"HeapAlloc", 1005, false, []string{"mem"}}, - EvNextGC: {"NextGC", 1005, false, []string{"mem"}}, - EvTimerGoroutine: {"TimerGoroutine", 1005, false, []string{"g"}}, // in 1.5 format it was {"g", "unused"} - EvFutileWakeup: {"FutileWakeup", 1005, false, []string{}}, - EvString: {"String", 1007, false, []string{}}, - EvGoStartLocal: {"GoStartLocal", 1007, false, []string{"g"}}, - EvGoUnblockLocal: {"GoUnblockLocal", 1007, true, []string{"g"}}, - EvGoSysExitLocal: {"GoSysExitLocal", 1007, false, []string{"g", "ts"}}, - EvGoStartLabel: {"GoStartLabel", 1008, false, []string{"g", "seq", "label"}}, - EvGoBlockGC: {"GoBlockGC", 1008, true, []string{}}, - EvGCMarkAssistStart: {"GCMarkAssistStart", 1009, true, []string{}}, - EvGCMarkAssistDone: {"GCMarkAssistDone", 1009, false, []string{}}, + EvNone: {"None", 1005, false, []string{}, nil}, + EvBatch: {"Batch", 1005, false, []string{"p", "ticks"}, nil}, // in 1.5 format it was {"p", "seq", "ticks"} + EvFrequency: {"Frequency", 1005, false, []string{"freq"}, nil}, // in 1.5 format it was {"freq", "unused"} + EvStack: {"Stack", 1005, false, []string{"id", "siz"}, nil}, + EvGomaxprocs: {"Gomaxprocs", 1005, true, []string{"procs"}, nil}, + EvProcStart: {"ProcStart", 1005, false, []string{"thread"}, nil}, + EvProcStop: {"ProcStop", 1005, false, []string{}, nil}, + EvGCStart: {"GCStart", 1005, true, []string{"seq"}, nil}, // in 1.5 format it was {} + EvGCDone: {"GCDone", 1005, false, []string{}, nil}, + EvGCSTWStart: {"GCSTWStart", 1005, false, []string{"kindid"}, []string{"kind"}}, // <= 1.9, args was {} (implicitly {0}) + EvGCSTWDone: {"GCSTWDone", 1005, false, []string{}, nil}, + EvGCSweepStart: {"GCSweepStart", 1005, true, []string{}, nil}, + EvGCSweepDone: {"GCSweepDone", 1005, false, []string{"swept", "reclaimed"}, nil}, // before 1.9, format was {} + EvGoCreate: {"GoCreate", 1005, true, []string{"g", "stack"}, nil}, + EvGoStart: {"GoStart", 1005, false, []string{"g", "seq"}, nil}, // in 1.5 format it was {"g"} + EvGoEnd: {"GoEnd", 1005, false, []string{}, nil}, + EvGoStop: {"GoStop", 1005, true, []string{}, nil}, + EvGoSched: {"GoSched", 1005, true, []string{}, nil}, + EvGoPreempt: {"GoPreempt", 1005, true, []string{}, nil}, + EvGoSleep: {"GoSleep", 1005, true, []string{}, nil}, + EvGoBlock: {"GoBlock", 1005, true, []string{}, nil}, + EvGoUnblock: {"GoUnblock", 1005, true, []string{"g", "seq"}, nil}, // in 1.5 format it was {"g"} + EvGoBlockSend: {"GoBlockSend", 1005, true, []string{}, nil}, + EvGoBlockRecv: {"GoBlockRecv", 1005, true, []string{}, nil}, + EvGoBlockSelect: {"GoBlockSelect", 1005, true, []string{}, nil}, + EvGoBlockSync: {"GoBlockSync", 1005, true, []string{}, nil}, + EvGoBlockCond: {"GoBlockCond", 1005, true, []string{}, nil}, + EvGoBlockNet: {"GoBlockNet", 1005, true, []string{}, nil}, + EvGoSysCall: {"GoSysCall", 1005, true, []string{}, nil}, + EvGoSysExit: {"GoSysExit", 1005, false, []string{"g", "seq", "ts"}, nil}, + EvGoSysBlock: {"GoSysBlock", 1005, false, []string{}, nil}, + EvGoWaiting: {"GoWaiting", 1005, false, []string{"g"}, nil}, + EvGoInSyscall: {"GoInSyscall", 1005, false, []string{"g"}, nil}, + EvHeapAlloc: {"HeapAlloc", 1005, false, []string{"mem"}, nil}, + EvNextGC: {"NextGC", 1005, false, []string{"mem"}, nil}, + EvTimerGoroutine: {"TimerGoroutine", 1005, false, []string{"g"}, nil}, // in 1.5 format it was {"g", "unused"} + EvFutileWakeup: {"FutileWakeup", 1005, false, []string{}, nil}, + EvString: {"String", 1007, false, []string{}, nil}, + EvGoStartLocal: {"GoStartLocal", 1007, false, []string{"g"}, nil}, + EvGoUnblockLocal: {"GoUnblockLocal", 1007, true, []string{"g"}, nil}, + EvGoSysExitLocal: {"GoSysExitLocal", 1007, false, []string{"g", "ts"}, nil}, + EvGoStartLabel: {"GoStartLabel", 1008, false, []string{"g", "seq", "labelid"}, []string{"label"}}, + EvGoBlockGC: {"GoBlockGC", 1008, true, []string{}, nil}, + EvGCMarkAssistStart: {"GCMarkAssistStart", 1009, true, []string{}, nil}, + EvGCMarkAssistDone: {"GCMarkAssistDone", 1009, false, []string{}, nil}, + EvUserTaskCreate: {"UserTaskCreate", 1011, true, []string{"taskid", "pid", "typeid"}, []string{"name"}}, + EvUserTaskEnd: {"UserTaskEnd", 1011, true, []string{"taskid"}, nil}, + EvUserRegion: {"UserRegion", 1011, true, []string{"taskid", "mode", "typeid"}, []string{"name"}}, + EvUserLog: {"UserLog", 1011, true, []string{"id", "keyid"}, []string{"category", "message"}}, } |