Commit 29170dee authored by Kirill Smelkov's avatar Kirill Smelkov

X tracing: Polish

parent 4d0cd894
...@@ -23,16 +23,13 @@ Gotrace is a program to support and interact with go tracing subsystem. ...@@ -23,16 +23,13 @@ Gotrace is a program to support and interact with go tracing subsystem.
Gotrace is a common entry to tracing and provides several subcommands: Gotrace is a common entry to tracing and provides several subcommands:
gen generate code according to tracing annotations and imports gen generate code according to tracing annotations and imports
list lists tracepoints defined in a package list lists tracepoints defined by a package
XXX tracepoints this package defines
XXX tracepoints this package imports
See package lab.nexedi.com/kirr/go123/tracing documentation on how to define See package lab.nexedi.com/kirr/go123/tracing documentation on how to define
and use trace events in programs. XXX and use trace events in programs.
TODO automatically turn every trace:event in an USDT probe so that they can be TODO automatically turn every trace:event into an USDT probe so that they can
traced from outside of the process too. be traced from outside of the process too.
See e.g. https://github.com/iovisor/bcc/issues/327 for context. See e.g. https://github.com/iovisor/bcc/issues/327 for context.
FIXME build tags not taken into account FIXME build tags not taken into account
...@@ -49,6 +46,7 @@ import ( ...@@ -49,6 +46,7 @@ import (
"go/parser" "go/parser"
"go/token" "go/token"
"go/types" "go/types"
"io"
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
...@@ -61,6 +59,7 @@ import ( ...@@ -61,6 +59,7 @@ import (
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
"lab.nexedi.com/kirr/go123/xerr" "lab.nexedi.com/kirr/go123/xerr"
zt "lab.nexedi.com/kirr/neo/go/zodb/zodbtools"
) )
// traceEvent represents 1 trace:event declaration // traceEvent represents 1 trace:event declaration
...@@ -118,8 +117,9 @@ type Package struct { ...@@ -118,8 +117,9 @@ type Package struct {
traceTypeInfo *types.Info // typeinfo for ^^^ traceTypeInfo *types.Info // typeinfo for ^^^
} }
// parseTraceEvent parses trace event definition into traceEvent // parseTraceEvent parses trace event definition into traceEvent.
// text is text argument after "//trace:event " //
// text is text argument after "//trace:event ".
func (p *Package) parseTraceEvent(srcfile *ast.File, pos token.Position, text string) (*traceEvent, error) { func (p *Package) parseTraceEvent(srcfile *ast.File, pos token.Position, text string) (*traceEvent, error) {
posErr := func(format string, argv ...interface{}) error { posErr := func(format string, argv ...interface{}) error {
return fmt.Errorf("%v: "+format, append([]interface{}{pos}, argv...)...) return fmt.Errorf("%v: "+format, append([]interface{}{pos}, argv...)...)
...@@ -189,11 +189,12 @@ func (p *Package) parseTraceEvent(srcfile *ast.File, pos token.Position, text st ...@@ -189,11 +189,12 @@ func (p *Package) parseTraceEvent(srcfile *ast.File, pos token.Position, text st
return &traceEvent{Pos: pos, Pkgt: p, FuncDecl: declf}, nil return &traceEvent{Pos: pos, Pkgt: p, FuncDecl: declf}, nil
} }
// parseTraceImport parses trace import directive into traceImport // parseTraceImport parses trace import directive into traceImport.
// text is text argument after "//trace:import " //
// text is text argument after "//trace:import ".
func (p *Package) parseTraceImport(pos token.Position, text string) (*traceImport, error) { func (p *Package) parseTraceImport(pos token.Position, text string) (*traceImport, error) {
// //trace:import "path/to/pkg" // //trace:import "path/to/pkg"
// //traca:import name "path/to/pkg" // //trace:import name "path/to/pkg"
if len(text) == 0 { if len(text) == 0 {
return nil, fmt.Errorf("%v: empty trace-import spec", pos) return nil, fmt.Errorf("%v: empty trace-import spec", pos)
...@@ -211,7 +212,7 @@ func (p *Package) parseTraceImport(pos token.Position, text string) (*traceImpor ...@@ -211,7 +212,7 @@ func (p *Package) parseTraceImport(pos token.Position, text string) (*traceImpor
pkgqpath = textv[1] pkgqpath = textv[1]
} }
// Unqote pkgqpath as in regular import does // Unquote pkgqpath as regular import does
pkgpath, err := strconv.Unquote(pkgqpath) pkgpath, err := strconv.Unquote(pkgqpath)
if err != nil || pkgpath == "" || pkgpath[0] == '\'' { if err != nil || pkgpath == "" || pkgpath[0] == '\'' {
return nil, fmt.Errorf("%v: invalid trace-import path %v", pos, pkgqpath) return nil, fmt.Errorf("%v: invalid trace-import path %v", pos, pkgqpath)
...@@ -256,18 +257,17 @@ func packageTrace(prog *loader.Program, pkgi *loader.PackageInfo) (*Package, err ...@@ -256,18 +257,17 @@ func packageTrace(prog *loader.Program, pkgi *loader.PackageInfo) (*Package, err
DisableUnusedImportCheck: true, DisableUnusedImportCheck: true,
} }
// tfset := token.NewFileSet() // XXX ok to separate or use original package fset? // tfset := token.NewFileSet() // XXX ok to separate or use original package fset?
tfset := prog.Fset
tpkg := types.NewPackage(pkgi.Pkg.Path(), pkgi.Pkg.Name()) tpkg := types.NewPackage(pkgi.Pkg.Path(), pkgi.Pkg.Name())
tinfo := &types.Info{Types: make(map[ast.Expr]types.TypeAndValue)} tinfo := &types.Info{Types: make(map[ast.Expr]types.TypeAndValue)}
p := &Package{ p := &Package{
Pkgi: pkgi, Pkgi: pkgi,
// traceFset: tfset,
// traceChecker: types.NewChecker(tconf, tfset, tpkg, tinfo),
// XXX vvv do we need separate field for traceFset if it is = prog.Fset? // XXX vvv do we need separate field for traceFset if it is = prog.Fset?
traceFset: prog.Fset, traceFset: tfset,
traceChecker: types.NewChecker(tconf, prog.Fset, tpkg, tinfo), traceChecker: types.NewChecker(tconf, tfset, tpkg, tinfo),
tracePkg: tpkg, tracePkg: tpkg,
traceTypeInfo: tinfo, traceTypeInfo: tinfo,
} }
...@@ -284,7 +284,7 @@ func packageTrace(prog *loader.Program, pkgi *loader.PackageInfo) (*Package, err ...@@ -284,7 +284,7 @@ func packageTrace(prog *loader.Program, pkgi *loader.PackageInfo) (*Package, err
// FIXME we currently don't process cgo files as go/loader passes to us // FIXME we currently don't process cgo files as go/loader passes to us
// already preprocessed results with comments stripped, not original source. // already preprocessed results with comments stripped, not original source.
// Maybe in some time it will be possible to have AST of original source: // Maybe in some time it will be possible to have AST of original source:
// https://github.com/golang/go/issues/16623 // https://golang.org/issues/16623
for _, file := range pkgi.Files { // ast.File for _, file := range pkgi.Files { // ast.File
for _, commgroup := range file.Comments { // ast.CommentGroup for _, commgroup := range file.Comments { // ast.CommentGroup
for _, comment := range commgroup.List { // ast.Comment for _, comment := range commgroup.List { // ast.Comment
...@@ -323,7 +323,7 @@ func packageTrace(prog *loader.Program, pkgi *loader.PackageInfo) (*Package, err ...@@ -323,7 +323,7 @@ func packageTrace(prog *loader.Program, pkgi *loader.PackageInfo) (*Package, err
return nil, err return nil, err
} }
// XXX needed here? - better append in parseTraceEvent // XXX needed here? - better append in parseTraceImport
p.Importv = append(p.Importv, imported) p.Importv = append(p.Importv, imported)
default: default:
...@@ -401,13 +401,15 @@ func (te *traceEvent) Argv() string { ...@@ -401,13 +401,15 @@ func (te *traceEvent) Argv() string {
return strings.Join(argv, ", ") return strings.Join(argv, ", ")
} }
// ArgvTyped returns argument list with types // ArgvTyped returns argument list with types.
//
// types are qualified relative to original package // types are qualified relative to original package
func (te *traceEvent) ArgvTyped() string { func (te *traceEvent) ArgvTyped() string {
return te.ArgvTypedRelativeTo(te.Pkgt.tracePkg, nil) return te.ArgvTypedRelativeTo(te.Pkgt.tracePkg, nil)
} }
// ArgvTypedRelativeTo returns argument list with types qualified relative to specified package // ArgvTypedRelativeTo returns argument list with types qualified relative to specified package.
//
// importedAs specifies under which name a package was imported, if name was explicitly set // importedAs specifies under which name a package was imported, if name was explicitly set
func (te *traceEvent) ArgvTypedRelativeTo(pkg *types.Package, importedAs map[string]string /*pkgpath -> pkgname*/) string { func (te *traceEvent) ArgvTypedRelativeTo(pkg *types.Package, importedAs map[string]string /*pkgpath -> pkgname*/) string {
argv := []string{} argv := []string{}
...@@ -530,7 +532,8 @@ func init() { {{.ImportSpec.PkgName}}_trace_exporthash() } ...@@ -530,7 +532,8 @@ func init() { {{.ImportSpec.PkgName}}_trace_exporthash() }
// magic begins all files generated by gotrace // magic begins all files generated by gotrace
const magic = "// Code generated by lab.nexedi.com/kirr/go123/tracing/cmd/gotrace; DO NOT EDIT.\n" const magic = "// Code generated by lab.nexedi.com/kirr/go123/tracing/cmd/gotrace; DO NOT EDIT.\n"
// checkCanWrite checks whether it is safe to write to file at path // checkCanWrite checks whether it is safe to write to file at path.
//
// it is safe to write when either // it is safe to write when either
// - the file does not exist, or // - the file does not exist, or
// - it exits but was previously generated by us // - it exits but was previously generated by us
...@@ -575,7 +578,8 @@ func removeFile(path string) error { ...@@ -575,7 +578,8 @@ func removeFile(path string) error {
return err return err
} }
// Program represents loaded program for tracepoint analysis // Program represents loaded program for tracepoint analysis.
//
// It is generalization of loader.Program due to loader not allowing to // It is generalization of loader.Program due to loader not allowing to
// construct programs incrementally. // construct programs incrementally.
type Program struct { type Program struct {
...@@ -596,7 +600,7 @@ type Program struct { ...@@ -596,7 +600,7 @@ type Program struct {
// NewProgram constructs new empty Program ready to load packages according to specified build context // NewProgram constructs new empty Program ready to load packages according to specified build context
func NewProgram(ctxt *build.Context, cwd string) *Program { func NewProgram(ctxt *build.Context, cwd string) *Program {
// adjust build context to filter-out ztrace* files when disovering packages // adjust build context to filter-out ztrace* files when discovering packages
// //
// we don't load what should be generated by us for 2 reasons: // we don't load what should be generated by us for 2 reasons:
// - code generated could be wrong with older version of the // - code generated could be wrong with older version of the
...@@ -631,7 +635,7 @@ func NewProgram(ctxt *build.Context, cwd string) *Program { ...@@ -631,7 +635,7 @@ func NewProgram(ctxt *build.Context, cwd string) *Program {
} }
// Import imports a package and returns associated package info and program // Import imports a package and returns associated package info and program
// under which it was loaded // under which it was loaded.
func (p *Program) Import(pkgpath string) (prog *loader.Program, pkgi *loader.PackageInfo, err error) { func (p *Program) Import(pkgpath string) (prog *loader.Program, pkgi *loader.PackageInfo, err error) {
// let's see - maybe it is already there // let's see - maybe it is already there
for _, prog := range p.progv { for _, prog := range p.progv {
...@@ -660,7 +664,7 @@ func (p *Program) Import(pkgpath string) (prog *loader.Program, pkgi *loader.Pac ...@@ -660,7 +664,7 @@ func (p *Program) Import(pkgpath string) (prog *loader.Program, pkgi *loader.Pac
} }
// ImportWithTests imports a package augmented with code from _test.go files + // ImportWithTests imports a package augmented with code from _test.go files +
// imports external test package (if present) // imports external test package (if present).
func (p *Program) ImportWithTests(pkgpath string) (prog *loader.Program, pkgi *loader.PackageInfo, xtestPkgi *loader.PackageInfo, err error) { func (p *Program) ImportWithTests(pkgpath string) (prog *loader.Program, pkgi *loader.PackageInfo, xtestPkgi *loader.PackageInfo, err error) {
// NOTE always reimporting not to interfere with regular imports // NOTE always reimporting not to interfere with regular imports
p.loaderConf.ImportPkgs = nil p.loaderConf.ImportPkgs = nil
...@@ -684,7 +688,9 @@ func (p *Program) ImportWithTests(pkgpath string) (prog *loader.Program, pkgi *l ...@@ -684,7 +688,9 @@ func (p *Program) ImportWithTests(pkgpath string) (prog *loader.Program, pkgi *l
return prog, pkgi, xtestPkgi, nil return prog, pkgi, xtestPkgi, nil
} }
// tracegen generates code according to tracing directives in a package @ pkgpath // ---- `gotrace gen` ----
// tracegen generates code according to tracing directives in a package @ pkgpath.
// //
// ctxt is build context for discovering packages // ctxt is build context for discovering packages
// cwd is "current" directory for resolving local imports (e.g. packages like "./some/package") // cwd is "current" directory for resolving local imports (e.g. packages like "./some/package")
...@@ -729,7 +735,8 @@ func tracegen(pkgpath string, ctxt *build.Context, cwd string) error { ...@@ -729,7 +735,8 @@ func tracegen(pkgpath string, ctxt *build.Context, cwd string) error {
return xerr.Merge(err1, err2, err3) return xerr.Merge(err1, err2, err3)
} }
// tracegen1 generates code according to tracing directives for a (sub)package @pkgpath // tracegen1 generates code according to tracing directives for a (sub)package @pkgpath.
//
// subpackage is either original package, testing code, or external test package // subpackage is either original package, testing code, or external test package
func tracegen1(P *Program, tpkg *Package, pkgdir string, kind string) error { func tracegen1(P *Program, tpkg *Package, pkgdir string, kind string) error {
var err error var err error
...@@ -881,7 +888,7 @@ func tracegen1(P *Program, tpkg *Package, pkgdir string, kind string) error { ...@@ -881,7 +888,7 @@ func tracegen1(P *Program, tpkg *Package, pkgdir string, kind string) error {
} }
// traceExport returns signatures of all tracing-related exports of a package // traceExport returns signatures of all tracing-related exports of a package
// in canonical order as would be seen from universe scope // in canonical order as would be seen from universe scope.
func traceExport(tpkg *Package, kind string) []byte { func traceExport(tpkg *Package, kind string) []byte {
pkgpath := tpkg.Pkgi.Pkg.Path() pkgpath := tpkg.Pkgi.Pkg.Path()
pkgname := tpkg.Pkgi.Pkg.Name() pkgname := tpkg.Pkgi.Pkg.Name()
...@@ -912,37 +919,130 @@ func traceExportHash(tpkg *Package, kind string) string { ...@@ -912,37 +919,130 @@ func traceExportHash(tpkg *Package, kind string) string {
return fmt.Sprintf("%x", sha1.Sum(traceExport(tpkg, kind))) return fmt.Sprintf("%x", sha1.Sum(traceExport(tpkg, kind)))
} }
const genSummary = "generate code according to tracing annotations and imports"
// TODO func genUsage(w io.Writer) {
// func tracelist(...) fmt.Fprintf(w,
`Usage: gotrace gen <package>
Generate code according to tracing annotations and imports
func main() { options:
log.SetFlags(0)
log.SetPrefix("gotrace: ")
flag.Usage = func() { -h --help this help text.
fmt.Fprintf(os.Stderr,
`gotracegen [options] [package]
TODO ...
`) `)
} }
flag.Parse() func genMain(argv []string) {
flags := flag.FlagSet{Usage: func() { genUsage(os.Stderr) }}
flags.Init("", flag.ExitOnError)
flags.Parse(argv[1:])
argv := flag.Args() argv = flags.Args()
if len(argv) < 1 { if len(argv) < 1 {
flag.Usage() flags.Usage()
os.Exit(2) zt.Exit(2)
} }
pkgpath := argv[0] pkgpath := argv[0]
cwd, err := os.Getwd() cwd, err := os.Getwd()
if err != nil { if err != nil {
log.Fatal(err) zt.Fatal(err)
} }
err = tracegen(pkgpath, &build.Default, cwd) err = tracegen(pkgpath, &build.Default, cwd)
if err != nil { if err != nil {
log.Fatal(err) zt.Fatal(err)
}
}
// ---- `gotrace list` ----
// tracelist lists trace-events defined by a package @ pkgpath.
//
// ctxt and cwd are tunables for discovering packages. See tracegen for details.
//
// TODO support listing by pkgspec (e.g. "./...")
func tracelist(w io.Writer, pkgpath string, ctxt *build.Context, cwd string) error {
P := NewProgram(ctxt, cwd)
// NOTE only listing trace-events provided by main package, not tests or xtest
lprog, pkgi, err := P.Import(pkgpath)
if err != nil {
return err
}
tpkg, err := packageTrace(lprog, pkgi)
if err != nil {
return err // XXX err ctx
}
for _, event := range tpkg.Eventv {
_, err = fmt.Fprintf(w, "%s:%s\n", event.Pkgt.Pkgi.Pkg.Path(), event.Name)
if err != nil {
return err
}
}
return nil
}
const listSummary = "lists tracepoints defined by a package"
func listUsage(w io.Writer) {
fmt.Fprintf(w,
`Usage: gotrace list <package>
List tracepoints defined by a package
options:
-h --help this help text.
`)
}
func listMain(argv []string) {
flags := flag.FlagSet{Usage: func() { genUsage(os.Stderr) }}
flags.Init("", flag.ExitOnError)
flags.Parse(argv[1:])
argv = flags.Args()
if len(argv) < 1 {
flags.Usage()
zt.Exit(2)
}
pkgpath := argv[0]
cwd, err := os.Getwd()
if err != nil {
zt.Fatal(err)
}
err = tracelist(os.Stdout, pkgpath, &build.Default, cwd)
if err != nil {
zt.Fatal(err)
} }
} }
// ---- main driver ----
var commands = zt.CommandRegistry{
{"gen", genSummary, genUsage, genMain},
{"list", listSummary, listUsage, listMain},
}
var helpTopics = zt.HelpRegistry{
// XXX for now empty
}
var prog = zt.MainProg{
Name: "gotrace",
Summary: "Gotrace is a program to support and interact with go tracing subsystem",
Commands: commands,
HelpTopics: helpTopics,
}
func main() {
log.SetFlags(0)
log.SetPrefix("gotrace: ")
prog.Main()
}
// 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 package main
import ( import (
"bytes"
"fmt" "fmt"
"go/build" "go/build"
"io/ioutil" "io/ioutil"
...@@ -28,10 +48,11 @@ func xglob(t *testing.T, pattern string) []string { ...@@ -28,10 +48,11 @@ func xglob(t *testing.T, pattern string) []string {
type TreePrepareMode int type TreePrepareMode int
const ( const (
TreePrepareGolden TreePrepareMode = iota // prepare golden tree - how `gotrace gen` result should look like TreePrepareGolden TreePrepareMode = iota // prepare golden tree - how `gotrace gen` result should look like
TreePrepareWork // prepare work tree - inital state for `gotrace gen` to run 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 // prepareTestTree copies files from src to dst recursively processing *.ok and *.rm depending on mode.
//
// dst should not initially exist // dst should not initially exist
func prepareTestTree(src, dst string, mode TreePrepareMode) error { func prepareTestTree(src, dst string, mode TreePrepareMode) error {
err := os.MkdirAll(dst, 0777) err := os.MkdirAll(dst, 0777)
...@@ -113,12 +134,12 @@ func diffR(patha, pathb string) (diff string, err error) { ...@@ -113,12 +134,12 @@ func diffR(patha, pathb string) (diff string, err error) {
return string(out), err return string(out), err
} }
func TestGoTraceGen(t *testing.T) { func TestGoTrace(t *testing.T) {
tmp, err := ioutil.TempDir("", "t-gotrace") tmp, err := ioutil.TempDir("", "t-gotrace")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
//defer os.RemoveAll(tmp) defer os.RemoveAll(tmp)
good := tmp + "/good" good := tmp + "/good"
work := tmp + "/work" work := tmp + "/work"
...@@ -128,19 +149,19 @@ func TestGoTraceGen(t *testing.T) { ...@@ -128,19 +149,19 @@ func TestGoTraceGen(t *testing.T) {
// test build context with GOPATH set to work tree // test build context with GOPATH set to work tree
var tBuildCtx = &build.Context{ var tBuildCtx = &build.Context{
GOARCH: "amd64", GOARCH: "amd64",
GOOS: "linux", GOOS: "linux",
GOROOT: runtime.GOROOT(), GOROOT: runtime.GOROOT(),
GOPATH: work, GOPATH: work,
CgoEnabled: true, CgoEnabled: true,
Compiler: runtime.Compiler, Compiler: runtime.Compiler,
} }
// XXX autodetect (go list ?) // XXX autodetect (go list ?)
testv := []string{"a/pkg1", "b/pkg2", "c/pkg3", "d/pkg4"} testv := []string{"a/pkg1", "b/pkg2", "c/pkg3", "d/pkg4"}
//testv := []string{"c/pkg3"}
for _, tpkg := range testv { for _, tpkg := range testv {
// verify `gotrace gen`
err = tracegen(tpkg, tBuildCtx, "" /* = local imorts disabled */) err = tracegen(tpkg, tBuildCtx, "" /* = local imorts disabled */)
if err != nil { if err != nil {
t.Errorf("%v: %v", tpkg, err) t.Errorf("%v: %v", tpkg, err)
...@@ -154,5 +175,22 @@ func TestGoTraceGen(t *testing.T) { ...@@ -154,5 +175,22 @@ func TestGoTraceGen(t *testing.T) {
if diff != "" { if diff != "" {
t.Errorf("%v: gold & work differ:\n%s", tpkg, 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)
}
} }
} }
a/pkg1:traceDoSomething
a/pkg1:traceNewT
a/pkg1:traceNewTPre
a/pkg1:traceURLParsed
...@@ -32,7 +32,7 @@ type Buffer struct { ...@@ -32,7 +32,7 @@ type Buffer struct {
} }
func (b *Buffer) emit(format string, argv ...interface{}) { func (b *Buffer) emit(format string, argv ...interface{}) {
fmt.Fprintf(b, format + "\n", argv...) fmt.Fprintf(b, format+"\n", argv...)
} }
......
...@@ -27,12 +27,15 @@ import "unsafe" ...@@ -27,12 +27,15 @@ import "unsafe"
// symbols are e.g. in go/src/runtime/race/race_linux_amd64.syso // symbols are e.g. in go/src/runtime/race/race_linux_amd64.syso
#cgo LDFLAGS: -Wl,--unresolved-symbols=ignore-in-object-files #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__tsan17ThreadIgnoreBeginEPNS_11ThreadStateEm(void *, unsigned long);
extern void _ZN6__tsan15ThreadIgnoreEndEPNS_11ThreadStateEm(void *, unsigned long); extern void _ZN6__tsan15ThreadIgnoreEndEPNS_11ThreadStateEm(void *, unsigned long);
*/ */
import "C" import "C"
// NOTE runtime.RaceDisable disables only "sync" part, not "read/write" // 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) { func IgnoreBegin(racectx uintptr) {
C._ZN6__tsan17ThreadIgnoreBeginEPNS_11ThreadStateEm(unsafe.Pointer(racectx), 0) C._ZN6__tsan17ThreadIgnoreBeginEPNS_11ThreadStateEm(unsafe.Pointer(racectx), 0)
......
...@@ -23,5 +23,5 @@ package race ...@@ -23,5 +23,5 @@ package race
// race ignore begin/end stubs // race ignore begin/end stubs
func IgnoreBegin(racectx uintptr) {} func IgnoreBegin(racectx uintptr) {}
func IgnoreEnd(racectx uintptr) {} func IgnoreEnd(racectx uintptr) {}
...@@ -10,7 +10,7 @@ govern= # e.g. 109 for go1.9 ...@@ -10,7 +10,7 @@ govern= # e.g. 109 for go1.9
# goset <goexec> - set <goexec> as current go # goset <goexec> - set <goexec> as current go
goset() { goset() {
goexec=$1 goexec=$1
# go1.1 go1.2 go1.3 go1.4 go1.5 go1.6 go1.7 go1.8 go1.9 go1.10 -> go1.10` # 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}'` gover=`$goexec list -f '{{ range context.ReleaseTags }} {{ .}}{{end}}' runtime |awk '{print $NF}'`
IFS=. read gomaj gomin < <(echo "$gover") IFS=. read gomaj gomin < <(echo "$gover")
govern=$((${gomaj#go} * 100 + $gomin)) govern=$((${gomaj#go} * 100 + $gomin))
......
...@@ -23,5 +23,5 @@ package xruntime ...@@ -23,5 +23,5 @@ package xruntime
// empty stubs for Race*() // empty stubs for Race*()
func RaceIgnoreBegin() {} func RaceIgnoreBegin() {}
func RaceIgnoreEnd() {} func RaceIgnoreEnd() {}
...@@ -18,12 +18,11 @@ ...@@ -18,12 +18,11 @@
// See https://www.nexedi.com/licensing for rationale and options. // See https://www.nexedi.com/licensing for rationale and options.
package xruntime package xruntime
// stop-the-world that should probably be in xruntime, but I'm (yet) hesitating // stop-the-world that should probably be in public xruntime, but I'm (yet)
// to expose the API to public. // hesitating to expose the API to public.
import _ "unsafe" import _ "unsafe"
//go:linkname runtime_stopTheWorld runtime.stopTheWorld //go:linkname runtime_stopTheWorld runtime.stopTheWorld
//go:linkname runtime_startTheWorld runtime.startTheWorld //go:linkname runtime_startTheWorld runtime.startTheWorld
...@@ -41,7 +40,7 @@ func StopTheWorld(reason string) { ...@@ -41,7 +40,7 @@ func StopTheWorld(reason string) {
runtime_stopTheWorld(reason) runtime_stopTheWorld(reason)
} }
// StartTheWorld restarts the world after it was stopped by StopTheWorld // StartTheWorld restarts the world after it was stopped by StopTheWorld.
func StartTheWorld() { func StartTheWorld() {
runtime_startTheWorld() runtime_startTheWorld()
} }
...@@ -21,5 +21,5 @@ package xruntime ...@@ -21,5 +21,5 @@ package xruntime
//go:generate ./g_typedef //go:generate ./g_typedef
// getg returns pointer to current goroutine descriptor // getg returns pointer to current goroutine descriptor.
func getg() *g func getg() *g
...@@ -106,9 +106,9 @@ To get better understanding of what happens when it is possible to record ...@@ -106,9 +106,9 @@ To get better understanding of what happens when it is possible to record
events into a stream and later either visualize or postprocess them. events into a stream and later either visualize or postprocess them.
This is similar to how Go execution tracer works: This is similar to how Go execution tracer works:
https://golang.org/s/go15trace https://golang.org/s/go15trace
https://golang.org/pkg/runtime/trace https://golang.org/pkg/runtime/trace
https://golang.org/cmd/trace https://golang.org/cmd/trace
though there it records only predefined set of events related to Go runtime. though there it records only predefined set of events related to Go runtime.
...@@ -146,7 +146,7 @@ a set of goroutines in tested code in question ...@@ -146,7 +146,7 @@ a set of goroutines in tested code in question
- produce events in correct order, and - produce events in correct order, and
- at every event associated internal state is correct. - at every event associated internal state is correct.
TODO example TODO example.
Cross package tracing Cross package tracing
...@@ -291,7 +291,7 @@ func AttachProbe(pg *ProbeGroup, listp **Probe, probe *Probe) { ...@@ -291,7 +291,7 @@ func AttachProbe(pg *ProbeGroup, listp **Probe, probe *Probe) {
// Detach detaches probe from a tracepoint. // Detach detaches probe from a tracepoint.
// //
// Must be called under Lock // Must be called under Lock.
func (p *Probe) Detach() { func (p *Probe) Detach() {
verifyLocked() verifyLocked()
...@@ -326,7 +326,7 @@ type ProbeGroup struct { ...@@ -326,7 +326,7 @@ type ProbeGroup struct {
// Add adds a probe to the group. // Add adds a probe to the group.
// //
// Must be called under Lock // Must be called under Lock.
func (pg *ProbeGroup) Add(p *Probe) { func (pg *ProbeGroup) Add(p *Probe) {
verifyLocked() verifyLocked()
pg.probev = append(pg.probev, p) pg.probev = append(pg.probev, p)
...@@ -334,7 +334,7 @@ func (pg *ProbeGroup) Add(p *Probe) { ...@@ -334,7 +334,7 @@ func (pg *ProbeGroup) Add(p *Probe) {
// Done detaches all probes registered to the group. // Done detaches all probes registered to the group.
// //
// Must be called under normal conditions, not under Lock // Must be called under normal conditions, not under Lock.
func (pg *ProbeGroup) Done() { func (pg *ProbeGroup) Done() {
verifyUnlocked() verifyUnlocked()
Lock() Lock()
......
...@@ -30,7 +30,7 @@ import ( ...@@ -30,7 +30,7 @@ import (
) )
func TestAttachDetach(t *testing.T) { func TestAttachDetach(t *testing.T) {
var traceX *Probe // list head of a tracing event var traceX *Probe // list head of a tracing event
// check that traceX probe list has such and such content and also that .prev // check that traceX probe list has such and such content and also that .prev
// pointers in all elements are right // pointers in all elements are right
...@@ -102,7 +102,7 @@ func TestAttachDetach(t *testing.T) { ...@@ -102,7 +102,7 @@ func TestAttachDetach(t *testing.T) {
// should be ok, but since race detector does not know we stopped the world it // should be ok, but since race detector does not know we stopped the world it
// could complain. // could complain.
func TestUseDetach(t *testing.T) { func TestUseDetach(t *testing.T) {
var traceX *Probe // list head of a tracing event var traceX *Probe // list head of a tracing event
// attach probe to traceX // attach probe to traceX
probe := Probe{} probe := Probe{}
...@@ -114,7 +114,7 @@ func TestUseDetach(t *testing.T) { ...@@ -114,7 +114,7 @@ func TestUseDetach(t *testing.T) {
go func() { go func() {
// delay a bit so that main goroutine first spins some time // delay a bit so that main goroutine first spins some time
// with non-empty probe list // with non-empty probe list
time.Sleep(1*time.Millisecond) time.Sleep(1 * time.Millisecond)
Lock() Lock()
probe.Detach() probe.Detach()
......
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