Commit b8526271 authored by Kirill Smelkov's avatar Kirill Smelkov

X tracing moved -> go123

parent e3937d32
This diff is collapsed.
// Copyright (C) 2017 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
package main
import (
"bytes"
"fmt"
"go/build"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"syscall"
"testing"
"lab.nexedi.com/kirr/go123/exc"
)
func xglob(t *testing.T, pattern string) []string {
t.Helper()
matchv, err := filepath.Glob(pattern)
if err != nil {
t.Fatal(err)
}
return matchv
}
type TreePrepareMode int
const (
TreePrepareGolden TreePrepareMode = iota // prepare golden tree - how `gotrace gen` result should look like
TreePrepareWork // prepare work tree - inital state for `gotrace gen` to run
)
// prepareTestTree copies files from src to dst recursively processing *.ok and *.rm depending on mode.
//
// dst should not initially exist
func prepareTestTree(src, dst string, mode TreePrepareMode) error {
err := os.MkdirAll(dst, 0777)
if err != nil {
return err
}
return filepath.Walk(src, func(srcpath string, info os.FileInfo, err error) error {
if srcpath == src /* skip root */ || err != nil {
return err
}
dstpath := dst + strings.TrimPrefix(srcpath, src)
if info.IsDir() {
err := os.Mkdir(dstpath, 0777)
return err
}
// NOTE since files are walked in lexical order <f>.ok or
// <f>.rm is always guaranteed to go after <f>.
var isOk, isRm bool
if strings.HasSuffix(srcpath, ".ok") {
isOk = true
dstpath = strings.TrimSuffix(dstpath, ".ok")
}
if strings.HasSuffix(srcpath, ".rm") {
isRm = true
dstpath = strings.TrimSuffix(dstpath, ".rm")
}
data, err := ioutil.ReadFile(srcpath)
if err != nil {
return err
}
switch mode {
case TreePrepareGolden:
// ok files are written as is
// no removed files
if isRm {
return nil
}
case TreePrepareWork:
// no ok files initially
if isOk {
return nil
}
// files to remove - prepopulate with magic
if isRm {
data = []byte(magic)
}
}
err = ioutil.WriteFile(dstpath, data, info.Mode())
return err
})
}
func xprepareTree(src, dst string, mode TreePrepareMode) {
err := prepareTestTree(src, dst, mode)
exc.Raiseif(err)
}
// diffR compares two directories recursively
func diffR(patha, pathb string) (diff string, err error) {
cmd := exec.Command("diff", "-urN", patha, pathb)
out, err := cmd.Output()
if e, ok := err.(*exec.ExitError); ok {
if e.Sys().(syscall.WaitStatus).ExitStatus() == 1 {
err = nil // diff signals with 1 just a difference - problem exit code is 2
} else {
err = fmt.Errorf("diff %s %s:\n%s", patha, pathb, e.Stderr)
}
}
return string(out), err
}
func TestGoTrace(t *testing.T) {
tmp, err := ioutil.TempDir("", "t-gotrace")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmp)
good := tmp + "/good"
work := tmp + "/work"
xprepareTree("testdata", good, TreePrepareGolden)
xprepareTree("testdata", work, TreePrepareWork)
// test build context with GOPATH set to work tree
var tBuildCtx = &build.Context{
GOARCH: "amd64",
GOOS: "linux",
GOROOT: runtime.GOROOT(),
GOPATH: work,
CgoEnabled: true,
Compiler: runtime.Compiler,
}
// XXX autodetect (go list ?)
testv := []string{"a/pkg1", "b/pkg2", "c/pkg3", "d/pkg4"}
for _, tpkg := range testv {
// verify `gotrace gen`
err = tracegen(tpkg, tBuildCtx, "" /* = local imorts disabled */)
if err != nil {
t.Errorf("%v: %v", tpkg, err)
}
diff, err := diffR(good+"/src/"+tpkg, work+"/src/"+tpkg)
if err != nil {
t.Fatalf("%v: %v", tpkg, err)
}
if diff != "" {
t.Errorf("%v: gold & work differ:\n%s", tpkg, diff)
}
// verify `gotrace list`
var tlistBuf bytes.Buffer
err = tracelist(&tlistBuf, tpkg, tBuildCtx, "" /* = local imports disabled */)
if err != nil {
t.Fatalf("%v: %v", tpkg, err)
}
tlistOk, err := ioutil.ReadFile(work + "/src/" + tpkg + "/tracelist.txt")
if err != nil {
t.Fatalf("%v: %v", tpkg, err)
}
tlist := tlistBuf.Bytes()
if !bytes.Equal(tlist, tlistOk) {
t.Errorf("%v: tracelist differ:\nhave:\n%s\nwant:\n%s", tpkg, tlist, tlistOk)
}
}
}
package pkg1
import (
"net/url"
// extra import which is used in package but should not be used in tracing code
"fmt"
)
// probe receives no args
//trace:event traceNewTPre()
// probe receives type this package defines
//trace:event traceNewT(t *T)
type T struct {}
func NewT() *T {
traceNewTPre()
t := &T{}
traceNewT(t)
return t
}
// probe receives type from another package
//trace:event traceURLParsed(u *url.URL)
func ParseURL(ustr string) (*url.URL, error) {
u, err := url.Parse(ustr)
if err != nil {
return nil, fmt.Errorf("oh my bad: %v", err)
}
traceURLParsed(u)
return u, nil
}
// probe receives builtin type
//trace:event traceDoSomething(topic string)
func DoSomething(topic string) {
traceDoSomething(topic)
}
// XXX do we need vvv ?
// package-local non-exported tracepoint
//type t struct {}
////trace:event tracedoSomethingLocal(arg *t)
package pkg1
/*
#include <stdio.h>
void hello() {
printf("Hello World (from C)\n");
}
*/
import "C"
// FIXME vvv does not currently work because go/loader gives us already
// preprocessed result without original comments.
//
// trace event defined in a cgo file
//trace:event traceHello()
func Hello() {
//traceHello()
C.hello()
}
a/pkg1:traceDoSomething
a/pkg1:traceNewT
a/pkg1:traceNewTPre
a/pkg1:traceURLParsed
// Code generated by lab.nexedi.com/kirr/go123/tracing/cmd/gotrace; DO NOT EDIT.
Bad bad bad - I'm invalid go file.
// Code generated by lab.nexedi.com/kirr/go123/tracing/cmd/gotrace; DO NOT EDIT.
package pkg1
// code generated for tracepoints
import (
"lab.nexedi.com/kirr/neo/go/xcommon/tracing"
"unsafe"
"net/url"
)
// traceevent: traceDoSomething(topic string)
type _t_traceDoSomething struct {
tracing.Probe
probefunc func(topic string)
}
var _traceDoSomething *_t_traceDoSomething
func traceDoSomething(topic string) {
if _traceDoSomething != nil {
_traceDoSomething_run(topic)
}
}
func _traceDoSomething_run(topic string) {
for p := _traceDoSomething; p != nil; p = (*_t_traceDoSomething)(unsafe.Pointer(p.Next())) {
p.probefunc(topic)
}
}
func traceDoSomething_Attach(pg *tracing.ProbeGroup, probe func(topic string)) *tracing.Probe {
p := _t_traceDoSomething{probefunc: probe}
tracing.AttachProbe(pg, (**tracing.Probe)(unsafe.Pointer(&_traceDoSomething)), &p.Probe)
return &p.Probe
}
// traceevent: traceNewT(t *T)
type _t_traceNewT struct {
tracing.Probe
probefunc func(t *T)
}
var _traceNewT *_t_traceNewT
func traceNewT(t *T) {
if _traceNewT != nil {
_traceNewT_run(t)
}
}
func _traceNewT_run(t *T) {
for p := _traceNewT; p != nil; p = (*_t_traceNewT)(unsafe.Pointer(p.Next())) {
p.probefunc(t)
}
}
func traceNewT_Attach(pg *tracing.ProbeGroup, probe func(t *T)) *tracing.Probe {
p := _t_traceNewT{probefunc: probe}
tracing.AttachProbe(pg, (**tracing.Probe)(unsafe.Pointer(&_traceNewT)), &p.Probe)
return &p.Probe
}
// traceevent: traceNewTPre()
type _t_traceNewTPre struct {
tracing.Probe
probefunc func()
}
var _traceNewTPre *_t_traceNewTPre
func traceNewTPre() {
if _traceNewTPre != nil {
_traceNewTPre_run()
}
}
func _traceNewTPre_run() {
for p := _traceNewTPre; p != nil; p = (*_t_traceNewTPre)(unsafe.Pointer(p.Next())) {
p.probefunc()
}
}
func traceNewTPre_Attach(pg *tracing.ProbeGroup, probe func()) *tracing.Probe {
p := _t_traceNewTPre{probefunc: probe}
tracing.AttachProbe(pg, (**tracing.Probe)(unsafe.Pointer(&_traceNewTPre)), &p.Probe)
return &p.Probe
}
// traceevent: traceURLParsed(u *url.URL)
type _t_traceURLParsed struct {
tracing.Probe
probefunc func(u *url.URL)
}
var _traceURLParsed *_t_traceURLParsed
func traceURLParsed(u *url.URL) {
if _traceURLParsed != nil {
_traceURLParsed_run(u)
}
}
func _traceURLParsed_run(u *url.URL) {
for p := _traceURLParsed; p != nil; p = (*_t_traceURLParsed)(unsafe.Pointer(p.Next())) {
p.probefunc(u)
}
}
func traceURLParsed_Attach(pg *tracing.ProbeGroup, probe func(u *url.URL)) *tracing.Probe {
p := _t_traceURLParsed{probefunc: probe}
tracing.AttachProbe(pg, (**tracing.Probe)(unsafe.Pointer(&_traceURLParsed)), &p.Probe)
return &p.Probe
}
// trace export signature
func _trace_exporthash_965fa599dc3a61119faba1eacf8493973c5d87ad() {}
package pkg2
// trace-import another package
// NOTE "a/pkg1" is not regularly imported
//trace:import "a/pkg1"
// additional tracepoint which pkg2 defines
//trace:event traceDoSomething(i, j int, q string)
func DoSomething(i, j int, q string) {
traceDoSomething(i, j, q)
}
// Code generated by lab.nexedi.com/kirr/go123/tracing/cmd/gotrace; DO NOT EDIT.
package pkg2
// code generated for tracepoints
import (
"lab.nexedi.com/kirr/neo/go/xcommon/tracing"
"unsafe"
"a/pkg1"
"net/url"
)
// traceevent: traceDoSomething(i, j int, q string)
type _t_traceDoSomething struct {
tracing.Probe
probefunc func(i, j int, q string)
}
var _traceDoSomething *_t_traceDoSomething
func traceDoSomething(i, j int, q string) {
if _traceDoSomething != nil {
_traceDoSomething_run(i, j, q)
}
}
func _traceDoSomething_run(i, j int, q string) {
for p := _traceDoSomething; p != nil; p = (*_t_traceDoSomething)(unsafe.Pointer(p.Next())) {
p.probefunc(i, j, q)
}
}
func traceDoSomething_Attach(pg *tracing.ProbeGroup, probe func(i, j int, q string)) *tracing.Probe {
p := _t_traceDoSomething{probefunc: probe}
tracing.AttachProbe(pg, (**tracing.Probe)(unsafe.Pointer(&_traceDoSomething)), &p.Probe)
return &p.Probe
}
// trace export signature
func _trace_exporthash_80ddfc2f6c72bdf357dedbb2f0bbec85e93106fc() {}
// traceimport: "a/pkg1"
// rerun "gotrace gen" if you see link failure ↓↓↓
//go:linkname pkg1_trace_exporthash a/pkg1._trace_exporthash_965fa599dc3a61119faba1eacf8493973c5d87ad
func pkg1_trace_exporthash()
func init() { pkg1_trace_exporthash() }
//go:linkname pkg1_traceDoSomething_Attach a/pkg1.traceDoSomething_Attach
func pkg1_traceDoSomething_Attach(*tracing.ProbeGroup, func(topic string)) *tracing.Probe
//go:linkname pkg1_traceNewT_Attach a/pkg1.traceNewT_Attach
func pkg1_traceNewT_Attach(*tracing.ProbeGroup, func(t *pkg1.T)) *tracing.Probe
//go:linkname pkg1_traceNewTPre_Attach a/pkg1.traceNewTPre_Attach
func pkg1_traceNewTPre_Attach(*tracing.ProbeGroup, func()) *tracing.Probe
//go:linkname pkg1_traceURLParsed_Attach a/pkg1.traceURLParsed_Attach
func pkg1_traceURLParsed_Attach(*tracing.ProbeGroup, func(u *url.URL)) *tracing.Probe
// Code generated by lab.nexedi.com/kirr/go123/tracing/cmd/gotrace; DO NOT EDIT.
// empty .s so `go build` does not use -complete for go:linkname to work
package pkg3_test
import "testing"
// trace import that should be added only to external tests
//trace:import "b/pkg2"
func TestZzzExternal(t *testing.T) {
t.Fatal("...")
}
package pkg3
import "testing"
// trace import that should be added only to tests, and under specified package name
//trace:import aaa1 "a/pkg1"
func TestZzz(t *testing.T) {
if zzz() != 2 {
t.Fatal("zzz wrong")
}
}
// Code generated by lab.nexedi.com/kirr/go123/tracing/cmd/gotrace; DO NOT EDIT.
package pkg3
// code generated for tracepoints
import (
"lab.nexedi.com/kirr/neo/go/xcommon/tracing"
_ "unsafe"
aaa1 "a/pkg1"
"net/url"
)
// traceimport: aaa1 "a/pkg1"
// rerun "gotrace gen" if you see link failure ↓↓↓
//go:linkname aaa1_trace_exporthash a/pkg1._trace_exporthash_965fa599dc3a61119faba1eacf8493973c5d87ad
func aaa1_trace_exporthash()
func init() { aaa1_trace_exporthash() }
//go:linkname aaa1_traceDoSomething_Attach a/pkg1.traceDoSomething_Attach
func aaa1_traceDoSomething_Attach(*tracing.ProbeGroup, func(topic string)) *tracing.Probe
//go:linkname aaa1_traceNewT_Attach a/pkg1.traceNewT_Attach
func aaa1_traceNewT_Attach(*tracing.ProbeGroup, func(t *aaa1.T)) *tracing.Probe
//go:linkname aaa1_traceNewTPre_Attach a/pkg1.traceNewTPre_Attach
func aaa1_traceNewTPre_Attach(*tracing.ProbeGroup, func()) *tracing.Probe
//go:linkname aaa1_traceURLParsed_Attach a/pkg1.traceURLParsed_Attach
func aaa1_traceURLParsed_Attach(*tracing.ProbeGroup, func(u *url.URL)) *tracing.Probe
// Code generated by lab.nexedi.com/kirr/go123/tracing/cmd/gotrace; DO NOT EDIT.
// empty .s so `go build` does not use -complete for go:linkname to work
// Code generated by lab.nexedi.com/kirr/go123/tracing/cmd/gotrace; DO NOT EDIT.
package pkg3_test
// code generated for tracepoints
import (
"lab.nexedi.com/kirr/neo/go/xcommon/tracing"
_ "unsafe"
)
// traceimport: "b/pkg2"
// rerun "gotrace gen" if you see link failure ↓↓↓
//go:linkname pkg2_trace_exporthash b/pkg2._trace_exporthash_80ddfc2f6c72bdf357dedbb2f0bbec85e93106fc
func pkg2_trace_exporthash()
func init() { pkg2_trace_exporthash() }
//go:linkname pkg2_traceDoSomething_Attach b/pkg2.traceDoSomething_Attach
func pkg2_traceDoSomething_Attach(*tracing.ProbeGroup, func(i, j int, q string)) *tracing.Probe
// Code generated by lab.nexedi.com/kirr/go123/tracing/cmd/gotrace; DO NOT EDIT.
// empty .s so `go build` does not use -complete for go:linkname to work
package pkg4
// this package does not use tracepoints at all
// Copyright (C) 2017 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
package main
// misc utilities
import (
"bytes"
"fmt"
"sort"
)
// Buffer is bytes.Buffer + syntatic sugar
type Buffer struct {
bytes.Buffer
}
func (b *Buffer) emit(format string, argv ...interface{}) {
fmt.Fprintf(b, format+"\n", argv...)
}
// StrSet is set<string>
type StrSet map[string]struct{}
func (s StrSet) Add(itemv ...string) {
for _, item := range itemv {
s[item] = struct{}{}
}
}
func (s StrSet) Delete(item string) {
delete(s, item)
}
func (s StrSet) Has(item string) bool {
_, has := s[item]
return has
}
// Itemv returns ordered slice of set items
func (s StrSet) Itemv() []string {
itemv := make([]string, 0, len(s))
for item := range s {
itemv = append(itemv, item)
}
sort.Strings(itemv)
return itemv
}
// Copyright (C) 2017 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
// +build race
package race
import "unsafe"
/*
// symbols are e.g. in go/src/runtime/race/race_linux_amd64.syso
#cgo LDFLAGS: -Wl,--unresolved-symbols=ignore-in-object-files
// __tsan::ThreadIgnoreBegin(__tsan::ThreadState*, unsigned long)
// __tsan::ThreadIgnoreEnd(__tsan::ThreadState*, unsigned long)
extern void _ZN6__tsan17ThreadIgnoreBeginEPNS_11ThreadStateEm(void *, unsigned long);
extern void _ZN6__tsan15ThreadIgnoreEndEPNS_11ThreadStateEm(void *, unsigned long);
*/
import "C"
// Ways to tell race-detector to ignore "read/write" events from current thread.
// NOTE runtime.RaceDisable disables only "sync" part, not "read/write".
func IgnoreBegin(racectx uintptr) {
C._ZN6__tsan17ThreadIgnoreBeginEPNS_11ThreadStateEm(unsafe.Pointer(racectx), 0)
}
func IgnoreEnd(racectx uintptr) {
C._ZN6__tsan15ThreadIgnoreEndEPNS_11ThreadStateEm(unsafe.Pointer(racectx), 0)
}
// Copyright (C) 2017 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
// +build !race
package race
// race ignore begin/end stubs
func IgnoreBegin(racectx uintptr) {}
func IgnoreEnd(racectx uintptr) {}
#!/bin/bash
# g_typedef -- generate type definition of runtime.g
goexec= # set by main driver to current go compiler
gover= # go<maj>.<min> version (without .patch)
gomaj= # go<maj>
gomin= # <min>
govern= # e.g. 109 for go1.9
# goset <goexec> - set <goexec> as current go
goset() {
goexec=$1
# go1.1 go1.2 go1.3 go1.4 go1.5 go1.6 go1.7 go1.8 go1.9 go1.10 -> go1.10
gover=`$goexec list -f '{{ range context.ReleaseTags }} {{ .}}{{end}}' runtime |awk '{print $NF}'`
IFS=. read gomaj gomin < <(echo "$gover")
govern=$((${gomaj#go} * 100 + $gomin))
}
# typedef <type> - print type definition
typedef() {
$goexec doc -c -u $1 |sed -n -e '/^type /,/^}/p'
}
# typedef_g - print <g> & friends definitions
typedef_g() {
typedef runtime.g
typedef runtime.stack
typedef runtime._panic
typedef runtime._defer
typedef runtime.gobuf
typedef runtime.funcval
#typedef runtime.sudog
#typedef runtime.hchan
typedef runtime.timer
#typedef runtime.guintptr
#typedef runtime.m
if (( $govern < 109 )); then
typedef runtime.stkbar
fi
}
# typedef_g_fixed - print adjusted <g> & friends definitions
typedef_g_fixed() {
typedef_g $1 | \
sed -e 's/\<sys.Uintreg\>/uintreg/'
echo "type guintptr uintptr // XXX stub"
echo "type puintptr uintptr // XXX stub"
echo "type uintreg uint // FIXME wrong on amd64p32"
echo "type m struct {} // FIXME stub"
echo "type sudog struct {} // FIXME stub"
}
# gen_zruntime - generate zruntime_g_<gover>.go
gen_zruntime() {
out="zruntime_g_$gover.go"
echo >$out "// Code generated by g_typedef; DO NOT EDIT."
echo >>$out
echo >>$out "// +build $gover,!$gomaj.$((gomin+1))"
echo >>$out
echo >>$out "package xruntime"
echo >>$out
echo >>$out 'import "unsafe"'
echo >>$out
typedef_g_fixed $go >>$out
}
# main driver
gov="go18 go"
for g in $gov; do
goset $g
gen_zruntime
done
// Copyright (C) 2017 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
// +build race
package xruntime
import "lab.nexedi.com/kirr/neo/go/xcommon/tracing/internal/race"
// RaceIgnoreBegin instructs race-detector to ignore memory read/write events from current goroutine.
//
// The events will be back to handled after call to RaceIgnoreEnd.
//
// NOTE runtime.RaceDisable disables "sync" events, not memory read/write.
func RaceIgnoreBegin() {
race.IgnoreBegin(getg().racectx)
}
// RaceIgnoreEnd instructs race-detector to stop ignoring memory read/write events from current goroutine.
//
// NOTE runtime RaceEnable enables "sync" events, not memory read/write.
func RaceIgnoreEnd() {
race.IgnoreEnd(getg().racectx)
}
// Copyright (C) 2017 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
// +build !race
package xruntime
// empty stubs for Race*()
func RaceIgnoreBegin() {}
func RaceIgnoreEnd() {}
// Copyright (C) 2016-2017 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
package xruntime
// stop-the-world that should probably be in public xruntime, but I'm (yet)
// hesitating to expose the API to public.
import _ "unsafe"
//go:linkname runtime_stopTheWorld runtime.stopTheWorld
//go:linkname runtime_startTheWorld runtime.startTheWorld
func runtime_stopTheWorld(reason string)
func runtime_startTheWorld()
// StopTheWorld returns with the world stopped.
//
// Current goroutine remains the only one who is running, with others
// goroutines stopped at safe GC points.
// It requires careful programming as many things that normally work lead to
// fatal errors when the world is stopped - for example using timers would be
// invalid, but adjusting plain values in memory is ok.
func StopTheWorld(reason string) {
runtime_stopTheWorld(reason)
}
// StartTheWorld restarts the world after it was stopped by StopTheWorld.
func StartTheWorld() {
runtime_startTheWorld()
}
// empty .s so `go build` does not use -complete for go:linkname to work
// Copyright (C) 2017 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
package xruntime
//go:generate ./g_typedef
// getg returns pointer to current goroutine descriptor.
func getg() *g
#include "textflag.h"
// +build 386
// func getg() *g
TEXT ·getg(SB),NOSPLIT,$0-8
MOVL (TLS), AX
MOVL AX, ret+0(FP)
RET
#include "textflag.h"
// +build amd64 amd64p
// func getg() *g
TEXT ·getg(SB),NOSPLIT,$0-8
MOVQ (TLS), R14
MOVQ R14, ret+0(FP)
RET
package xruntime
import (
"runtime"
"sync/atomic"
"testing"
"time"
)
func TestStartStopTheWorld(t *testing.T) {
var x, stop int32
ready := make(chan int)
go func() {
// make sure the thread running this goroutine is different from thread for main g.
// this way we can be sure there are 2 OS threads in action and communicating via busywait should work.
runtime.LockOSThread()
ready <- 0
for atomic.LoadInt32(&stop) == 0 {
atomic.AddInt32(&x, 1)
// XXX as of go19 tight loops are not preemptible (golang.org/issues/10958)
// -> explicitly make sure we do not miss STW request.
runtime.Gosched()
}
}()
// wait for spawned goroutine to jump into its own thread
<-ready
// verify g and g2 are indeed running in parallel
xprev := atomic.LoadInt32(&x)
xnext := xprev
:= 0
tstart := time.Now()
for < 100 && time.Now().Sub(tstart) < time.Second {
xnext = atomic.LoadInt32(&x)
if xnext != xprev {
+= 1
xprev = xnext
}
}
if == 0 {
t.Fatal("g and g2 are not running in parallel")
}
// now stop the world and for 1s make sure g2 is not running in parallel with us
StopTheWorld("just for my reason")
xprev = atomic.LoadInt32(&x)
xnext = xprev
= 0
tstart = time.Now()
for time.Now().Sub(tstart) < time.Second {
for i := 0; i < 100 ; i++ {
xnext = atomic.LoadInt32(&x)
if xnext != xprev {
+= 1
xprev = xnext
}
}
}
StartTheWorld()
if != 0 {
t.Fatalf("g2 modified x at least %d times while the world was stopped", )
}
atomic.StoreInt32(&stop, 1)
}
// Code generated by g_typedef; DO NOT EDIT.
// +build go1.8,!go1.9
package xruntime
import "unsafe"
type g struct {
// Stack parameters.
// stack describes the actual stack memory: [stack.lo, stack.hi).
// stackguard0 is the stack pointer compared in the Go stack growth prologue.
// It is stack.lo+StackGuard normally, but can be StackPreempt to trigger a preemption.
// stackguard1 is the stack pointer compared in the C stack growth prologue.
// It is stack.lo+StackGuard on g0 and gsignal stacks.
// It is ~0 on other goroutine stacks, to trigger a call to morestackc (and crash).
stack stack // offset known to runtime/cgo
stackguard0 uintptr // offset known to liblink
stackguard1 uintptr // offset known to liblink
_panic *_panic // innermost panic - offset known to liblink
_defer *_defer // innermost defer
m *m // current m; offset known to arm liblink
stackAlloc uintptr // stack allocation is [stack.lo,stack.lo+stackAlloc)
sched gobuf
syscallsp uintptr // if status==Gsyscall, syscallsp = sched.sp to use during gc
syscallpc uintptr // if status==Gsyscall, syscallpc = sched.pc to use during gc
stkbar []stkbar // stack barriers, from low to high (see top of mstkbar.go)
stkbarPos uintptr // index of lowest stack barrier not hit
stktopsp uintptr // expected sp at top of stack, to check in traceback
param unsafe.Pointer // passed parameter on wakeup
atomicstatus uint32
stackLock uint32 // sigprof/scang lock; TODO: fold in to atomicstatus
goid int64
waitsince int64 // approx time when the g become blocked
waitreason string // if status==Gwaiting
schedlink guintptr
preempt bool // preemption signal, duplicates stackguard0 = stackpreempt
paniconfault bool // panic (instead of crash) on unexpected fault address
preemptscan bool // preempted g does scan for gc
gcscandone bool // g has scanned stack; protected by _Gscan bit in status
gcscanvalid bool // false at start of gc cycle, true if G has not run since last scan; transition from true to false by calling queueRescan and false to true by calling dequeueRescan
throwsplit bool // must not split stack
raceignore int8 // ignore race detection events
sysblocktraced bool // StartTrace has emitted EvGoInSyscall about this goroutine
sysexitticks int64 // cputicks when syscall has returned (for tracing)
traceseq uint64 // trace event sequencer
tracelastp puintptr // last P emitted an event for this goroutine
lockedm *m
sig uint32
writebuf []byte
sigcode0 uintptr
sigcode1 uintptr
sigpc uintptr
gopc uintptr // pc of go statement that created this goroutine
startpc uintptr // pc of goroutine function
racectx uintptr
waiting *sudog // sudog structures this g is waiting on (that have a valid elem ptr); in lock order
cgoCtxt []uintptr // cgo traceback context
// gcRescan is this G's index in work.rescan.list. If this is
// -1, this G is not on the rescan list.
//
// If gcphase != _GCoff and this G is visible to the garbage
// collector, writes to this are protected by work.rescan.lock.
gcRescan int32
// gcAssistBytes is this G's GC assist credit in terms of
// bytes allocated. If this is positive, then the G has credit
// to allocate gcAssistBytes bytes without assisting. If this
// is negative, then the G must correct this by performing
// scan work. We track this in bytes to make it fast to update
// and check for debt in the malloc hot path. The assist ratio
// determines how this corresponds to scan work debt.
gcAssistBytes int64
}
type stack struct {
lo uintptr
hi uintptr
}
type _panic struct {
argp unsafe.Pointer // pointer to arguments of deferred call run during panic; cannot move - known to liblink
arg interface{} // argument to panic
link *_panic // link to earlier panic
recovered bool // whether this panic is over
aborted bool // the panic was aborted
}
type _defer struct {
siz int32
started bool
sp uintptr // sp at time of defer
pc uintptr
fn *funcval
_panic *_panic // panic that is running defer
link *_defer
}
type gobuf struct {
// The offsets of sp, pc, and g are known to (hard-coded in) libmach.
//
// ctxt is unusual with respect to GC: it may be a
// heap-allocated funcval so write require a write barrier,
// but gobuf needs to be cleared from assembly. We take
// advantage of the fact that the only path that uses a
// non-nil ctxt is morestack. As a result, gogo is the only
// place where it may not already be nil, so gogo uses an
// explicit write barrier. Everywhere else that resets the
// gobuf asserts that ctxt is already nil.
sp uintptr
pc uintptr
g guintptr
ctxt unsafe.Pointer // this has to be a pointer so that gc scans it
ret uintreg
lr uintptr
bp uintptr // for GOEXPERIMENT=framepointer
}
type funcval struct {
fn uintptr
}
type timer struct {
i int // heap index
// Timer wakes up at when, and then at when+period, ... (period > 0 only)
// each time calling f(arg, now) in the timer goroutine, so f must be
// a well-behaved function and not block.
when int64
period int64
f func(interface{}, uintptr)
arg interface{}
seq uintptr
}
type stkbar struct {
savedLRPtr uintptr // location overwritten by stack barrier PC
savedLRVal uintptr // value overwritten at savedLRPtr
}
type guintptr uintptr // XXX stub
type puintptr uintptr // XXX stub
type uintreg uint // FIXME wrong on amd64p32
type m struct {} // FIXME stub
type sudog struct {} // FIXME stub
// Code generated by g_typedef; DO NOT EDIT.
// +build go1.9,!go1.10
package xruntime
import "unsafe"
type g struct {
// Stack parameters.
// stack describes the actual stack memory: [stack.lo, stack.hi).
// stackguard0 is the stack pointer compared in the Go stack growth prologue.
// It is stack.lo+StackGuard normally, but can be StackPreempt to trigger a preemption.
// stackguard1 is the stack pointer compared in the C stack growth prologue.
// It is stack.lo+StackGuard on g0 and gsignal stacks.
// It is ~0 on other goroutine stacks, to trigger a call to morestackc (and crash).
stack stack // offset known to runtime/cgo
stackguard0 uintptr // offset known to liblink
stackguard1 uintptr // offset known to liblink
_panic *_panic // innermost panic - offset known to liblink
_defer *_defer // innermost defer
m *m // current m; offset known to arm liblink
sched gobuf
syscallsp uintptr // if status==Gsyscall, syscallsp = sched.sp to use during gc
syscallpc uintptr // if status==Gsyscall, syscallpc = sched.pc to use during gc
stktopsp uintptr // expected sp at top of stack, to check in traceback
param unsafe.Pointer // passed parameter on wakeup
atomicstatus uint32
stackLock uint32 // sigprof/scang lock; TODO: fold in to atomicstatus
goid int64
waitsince int64 // approx time when the g become blocked
waitreason string // if status==Gwaiting
schedlink guintptr
preempt bool // preemption signal, duplicates stackguard0 = stackpreempt
paniconfault bool // panic (instead of crash) on unexpected fault address
preemptscan bool // preempted g does scan for gc
gcscandone bool // g has scanned stack; protected by _Gscan bit in status
gcscanvalid bool // false at start of gc cycle, true if G has not run since last scan; TODO: remove?
throwsplit bool // must not split stack
raceignore int8 // ignore race detection events
sysblocktraced bool // StartTrace has emitted EvGoInSyscall about this goroutine
sysexitticks int64 // cputicks when syscall has returned (for tracing)
traceseq uint64 // trace event sequencer
tracelastp puintptr // last P emitted an event for this goroutine
lockedm *m
sig uint32
writebuf []byte
sigcode0 uintptr
sigcode1 uintptr
sigpc uintptr
gopc uintptr // pc of go statement that created this goroutine
startpc uintptr // pc of goroutine function
racectx uintptr
waiting *sudog // sudog structures this g is waiting on (that have a valid elem ptr); in lock order
cgoCtxt []uintptr // cgo traceback context
labels unsafe.Pointer // profiler labels
timer *timer // cached timer for time.Sleep
// gcAssistBytes is this G's GC assist credit in terms of
// bytes allocated. If this is positive, then the G has credit
// to allocate gcAssistBytes bytes without assisting. If this
// is negative, then the G must correct this by performing
// scan work. We track this in bytes to make it fast to update
// and check for debt in the malloc hot path. The assist ratio
// determines how this corresponds to scan work debt.
gcAssistBytes int64
}
type stack struct {
lo uintptr
hi uintptr
}
type _panic struct {
argp unsafe.Pointer // pointer to arguments of deferred call run during panic; cannot move - known to liblink
arg interface{} // argument to panic
link *_panic // link to earlier panic
recovered bool // whether this panic is over
aborted bool // the panic was aborted
}
type _defer struct {
siz int32
started bool
sp uintptr // sp at time of defer
pc uintptr
fn *funcval
_panic *_panic // panic that is running defer
link *_defer
}
type gobuf struct {
// The offsets of sp, pc, and g are known to (hard-coded in) libmach.
//
// ctxt is unusual with respect to GC: it may be a
// heap-allocated funcval so write require a write barrier,
// but gobuf needs to be cleared from assembly. We take
// advantage of the fact that the only path that uses a
// non-nil ctxt is morestack. As a result, gogo is the only
// place where it may not already be nil, so gogo uses an
// explicit write barrier. Everywhere else that resets the
// gobuf asserts that ctxt is already nil.
sp uintptr
pc uintptr
g guintptr
ctxt unsafe.Pointer // this has to be a pointer so that gc scans it
ret uintreg
lr uintptr
bp uintptr // for GOEXPERIMENT=framepointer
}
type funcval struct {
fn uintptr
}
type timer struct {
i int // heap index
// Timer wakes up at when, and then at when+period, ... (period > 0 only)
// each time calling f(arg, now) in the timer goroutine, so f must be
// a well-behaved function and not block.
when int64
period int64
f func(interface{}, uintptr)
arg interface{}
seq uintptr
}
type guintptr uintptr // XXX stub
type puintptr uintptr // XXX stub
type uintreg uint // FIXME wrong on amd64p32
type m struct {} // FIXME stub
type sudog struct {} // FIXME stub
This diff is collapsed.
// Copyright (C) 2017 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
package tracing
import (
"reflect"
"runtime"
"testing"
"time"
"unsafe"
"github.com/kylelemons/godebug/pretty"
)
func TestAttachDetach(t *testing.T) {
var traceX *Probe // list head of a tracing event
// check that traceX probe list has such and such content and also that .prev
// pointers in all elements are right
checkX := func(probev ...*Probe) {
t.Helper()
var pv []*Probe
pp := (*Probe)(unsafe.Pointer(&traceX))
for p := traceX; p != nil; pp, p = p, p.next {
if p.prev != pp {
t.Fatalf("probe list: %#v: .prev is wrong", p)
}
pv = append(pv, p)
}
if !reflect.DeepEqual(pv, probev) {
t.Fatalf("probe list:\n%s\n", pretty.Compare(probev, pv))
}
}
checkX()
// attach probe to traceX
attachX := func(probe *Probe) {
Lock()
AttachProbe(nil, &traceX, probe)
Unlock()
}
// detach probe
detach := func(probe *Probe) {
Lock()
probe.Detach()
Unlock()
}
p1 := &Probe{}
attachX(p1)
checkX(p1)
detach(p1)
checkX()
detach(p1)
checkX()
attachX(p1)
checkX(p1)
p2 := &Probe{}
attachX(p2)
checkX(p1, p2)
p3 := &Probe{}
attachX(p3)
checkX(p1, p2, p3)
detach(p2)
checkX(p1, p3)
detach(p1)
checkX(p3)
detach(p3)
checkX()
}
// Test use vs concurent detach.
//
// Detach works under tracing lock (= world stopped) - so changing a probe list
// should be ok, but since race detector does not know we stopped the world it
// could complain.
func TestUseDetach(t *testing.T) {
var traceX *Probe // list head of a tracing event
// attach probe to traceX
probe := Probe{}
Lock()
AttachProbe(nil, &traceX, &probe)
Unlock()
// simulate traceX signalling and so probe usage and concurrent probe detach
go func() {
// delay a bit so that main goroutine first spins some time
// with non-empty probe list
time.Sleep(1 * time.Millisecond)
Lock()
probe.Detach()
Unlock()
}()
loop:
for {
np := 0
for p := traceX; p != nil; p = p.Next() {
np++
}
switch np {
case 1:
// ok - not yet detached
case 0:
// ok - detached
break loop
default:
t.Fatalf("probe seen %d times; must be either 1 or 0", np)
}
// XXX as of go19 tight loops are not preemptible (golang.org/issues/10958)
// and Lock does stop-the-world -> make this loop explicitly preemtible.
runtime.Gosched()
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment