aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/internal
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2018-09-24 21:46:21 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2018-09-24 21:46:21 +0000
commitdd931d9b48647e898dc80927c532ae93cc09e192 (patch)
tree71be2295cd79b8a182f6130611658db8628772d5 /libgo/go/internal
parent779d8a5ad09b01428726ea5a0e6c87bd9ac3c0e4 (diff)
downloadgcc-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')
-rw-r--r--libgo/go/internal/bytealg/bytealg.c116
-rw-r--r--libgo/go/internal/bytealg/bytealg.go24
-rw-r--r--libgo/go/internal/bytealg/compare_generic.go89
-rw-r--r--libgo/go/internal/bytealg/compare_native.go10
-rw-r--r--libgo/go/internal/bytealg/count_generic.go27
-rw-r--r--libgo/go/internal/bytealg/count_native.go34
-rw-r--r--libgo/go/internal/bytealg/equal_native.go14
-rw-r--r--libgo/go/internal/bytealg/gccgo.go12
-rw-r--r--libgo/go/internal/bytealg/index_amd64.go28
-rw-r--r--libgo/go/internal/bytealg/index_arm64.go25
-rw-r--r--libgo/go/internal/bytealg/index_generic.go30
-rw-r--r--libgo/go/internal/bytealg/index_native.go24
-rw-r--r--libgo/go/internal/bytealg/index_s390x.go33
-rw-r--r--libgo/go/internal/bytealg/indexbyte_generic.go48
-rw-r--r--libgo/go/internal/bytealg/indexbyte_native.go13
-rw-r--r--libgo/go/internal/cpu/cpu.go142
-rw-r--r--libgo/go/internal/cpu/cpu_386.go7
-rw-r--r--libgo/go/internal/cpu/cpu_amd64.go7
-rw-r--r--libgo/go/internal/cpu/cpu_amd64p32.go7
-rw-r--r--libgo/go/internal/cpu/cpu_arm64.go107
-rw-r--r--libgo/go/internal/cpu/cpu_arm64_test.go26
-rw-r--r--libgo/go/internal/cpu/cpu_gccgo.c66
-rw-r--r--libgo/go/internal/cpu/cpu_no_init.go16
-rw-r--r--libgo/go/internal/cpu/cpu_ppc64x.go46
-rw-r--r--libgo/go/internal/cpu/cpu_ppc64x_test.go33
-rw-r--r--libgo/go/internal/cpu/cpu_s390x.go146
-rw-r--r--libgo/go/internal/cpu/cpu_s390x_test.go63
-rw-r--r--libgo/go/internal/cpu/cpu_test.go68
-rw-r--r--libgo/go/internal/cpu/cpu_wasm.go7
-rw-r--r--libgo/go/internal/cpu/cpu_x86.go91
-rw-r--r--libgo/go/internal/cpu/cpu_x86_test.go47
-rw-r--r--libgo/go/internal/cpu/export_test.go9
-rw-r--r--libgo/go/internal/goroot/gc.go141
-rw-r--r--libgo/go/internal/goroot/gccgo.go27
-rw-r--r--libgo/go/internal/poll/fd_mutex.go10
-rw-r--r--libgo/go/internal/poll/fd_mutex_test.go22
-rw-r--r--libgo/go/internal/poll/fd_poll_nacljs.go (renamed from libgo/go/internal/poll/fd_poll_nacl.go)5
-rw-r--r--libgo/go/internal/poll/fd_posix.go2
-rw-r--r--libgo/go/internal/poll/fd_unix.go81
-rw-r--r--libgo/go/internal/poll/fd_windows.go57
-rw-r--r--libgo/go/internal/poll/hook_cloexec.go2
-rw-r--r--libgo/go/internal/poll/hook_unix.go2
-rw-r--r--libgo/go/internal/poll/sendfile_windows.go10
-rw-r--r--libgo/go/internal/poll/sock_cloexec.go2
-rw-r--r--libgo/go/internal/poll/splice_linux.go183
-rw-r--r--libgo/go/internal/poll/sys_cloexec.go2
-rw-r--r--libgo/go/internal/syscall/unix/nonblocking.go23
-rw-r--r--libgo/go/internal/syscall/unix/nonblocking_js.go11
-rw-r--r--libgo/go/internal/syscall/unix/nonblocking_nacl.go9
-rw-r--r--libgo/go/internal/syscall/windows/exec_windows_test.go13
-rw-r--r--libgo/go/internal/syscall/windows/registry/key.go10
-rw-r--r--libgo/go/internal/syscall/windows/registry/registry_test.go2
-rw-r--r--libgo/go/internal/syscall/windows/registry/zsyscall_windows.go2
-rw-r--r--libgo/go/internal/syscall/windows/security_windows.go45
-rw-r--r--libgo/go/internal/syscall/windows/symlink_windows.go14
-rw-r--r--libgo/go/internal/syscall/windows/syscall_windows.go2
-rw-r--r--libgo/go/internal/syscall/windows/zsyscall_windows.go25
-rw-r--r--libgo/go/internal/testenv/testenv.go9
-rw-r--r--libgo/go/internal/testenv/testenv_notwin.go4
-rw-r--r--libgo/go/internal/testenv/testenv_windows.go9
-rw-r--r--libgo/go/internal/trace/goroutines.go212
-rw-r--r--libgo/go/internal/trace/parser.go214
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"}},
}