Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/doc: garbled -src output #37028

Open
tv42 opened this issue Feb 4, 2020 · 1 comment
Open

cmd/doc: garbled -src output #37028

tv42 opened this issue Feb 4, 2020 · 1 comment
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@tv42
Copy link

tv42 commented Feb 4, 2020

What version of Go are you using (go version)?

$ go version
go version go1.13.7 linux/amd64

Does this issue reproduce with the latest release?

Yes (go1.13.7 is the latest release).

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/tv/.cache/go-build"
GOENV="/home/tv/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GONOPROXY=[censored, not related]
GONOSUMDB=[censored, not related]
GOOS="linux"
GOPATH="/home/tv/go"
GOPRIVATE=[censored, not related]
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/tv/sdk/go1.13.7"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/tv/sdk/go1.13.7/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/tv/go/src/eagain.net/go/securityblanket/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/home/tv/tmp/go-build488236536=/tmp/go-build -gno-record-gcc-switches"

What did you do?

In a module that didn't directly use xerrors, but github.com/google/go-cmp v0.4.0 pulled in golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543:

$ go doc -src xerrors.As
package xerrors // import "golang.org/x/xerrors"

// As finds the first error in err's chain that matches the type to which target
// points, and if so, sets the target to its value and returns true. An error
// matches a type if it is assignable to the target type, or if it has a method
// As(interface{}) bool such that As(target) returns true. As will panic if target
// is not a non-nil pointer to a type which implements error or is of interface type.
//
// The As method should set the target to its value and return true if err
// matches the type to which target points.

// 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.

// FormatError calls the FormatError method of f with an errors.Printer
// configured according to s and verb, and writes the result to s.

// Assuming this function is only called from the Format method, and given
// that FormatError takes precedence over Format, it cannot be called from
// any package that supports errors.Formatter. It is therefore safe to
// disregard that State may be a specific printer implementation and use one
// of our choice instead.

// limitations: does not support printing error as Go struct.

// separator before next error

// Note that this switch must match the preference order
// for ordinary string printing (%#v before %+v, and so on).

// proceed as if it were %v

// Use an intermediate buffer in the rare cases that precision,
// truncation, or one of the alternative verbs (q, x, and X) are
// specified.

func As(err error,

// Construct format string from State s.
target interface{}) bool {
	if target == nil {
		panic("errors: target cannot be nil")
	}
	val := reflect.ValueOf(target)
	typ := val.Type()
	if typ.Kind() != reflect.Ptr || val.IsNil() {
		panic("errors: target must be a non-nil pointer")
	}
	if e := typ.Elem(); e.Kind() != reflect.Interface && !e.Implements(errorType) {
		panic("errors: *target must be interface or implement error")
	}
	targetType := typ.Elem()
	for err != nil {
		if reflect.TypeOf(err).AssignableTo(targetType) {
			val.Elem().Set(reflect.ValueOf(err))
			return true
		}
		if x, ok :=

		// state tracks error printing state. It implements fmt.State.
		err.(interface{ As(interface{}) bool }); ok && x.As(target) {
			return true
		}
		err = Unwrap(err)
	}
	return false
}

// printer wraps a state to implement an xerrors.Printer.

// Copyright 2019 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 xerrors implements functions to manipulate errors.
//
// This package is based on the Go 2 proposal for error values:
//   https://golang.org/design/29934-error-values
//
// These functions were incorporated into the standard library's errors package
// in Go 1.13:
// - Is
// - As
// - Unwrap
//
// Also, Errorf's %w verb was incorporated into fmt.Errorf.
//
// Use this package to get equivalent behavior in all supported Go versions.
//
// No other features of this package were included in Go 1.13, and at present
// there are no plans to include any of them.
// import "golang.org/x/xerrors"

// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// errorString is a trivial implementation of error.

// New returns an error that formats as the given text.
//
// The returned error contains a Frame set to the caller's location and
// implements Formatter to show this information when printed with details.

// 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.

// Errorf formats according to a format specifier and returns the string as a
// value that satisfies error.
//
// The returned error includes the file and line number of the caller when
// formatted with additional detail enabled. If the last argument is an error
// the returned error's Format method will return it if the format string ends
// with ": %s", ": %v", or ": %w". If the last argument is an error and the
// format string ends with ": %w", the returned error implements an Unwrap
// method returning it.
//
// If the format specifier includes a %w verb with an error operand in a
// position other than at the end, the returned error will still implement an
// Unwrap method returning the operand, but the error's Format method will not
// return the wrapped error.
//
// It is invalid to include more than one %w verb or to supply it with an
// operand that does not implement the error interface. The %w verb is otherwise
// a synonym for %v.

// Support a ": %[wsv]" suffix, which works well with xerrors.Formatter.

// TODO: this is not entirely correct. The error value could be
// printed elsewhere in format if it mixes numbered with unnumbered
// substitutions. With relatively small changes to doPrintf we can
// have it optionally ignore extra arguments and pass the argument
// list in its entirety.

// Support %w anywhere.
// TODO: don't repeat the wrapped error's message when %w occurs in the middle.

// Too many %ws or argument of %w is not an error. Approximate the Go
// 1.13 fmt.Errorf message.

// formatPlusW is used to avoid the vet check that will barf at %w.

// Return the index of the only %w in format, or -1 if none.
// Also return a rewritten format string with %w replaced by %v, and
// false if there is more than one %w.
// TODO: handle "%[N]w".

// Loosely copied from golang.org/x/tools/go/analysis/passes/printf/printf.go.

// "%%" is not a format directive.

// "Replace" the last character, the 'w', with a 'v'.

// Parse the printf verb starting with a % at s[0].
// Return how many bytes it occupies and whether the verb is 'w'.

// Assume only that the directive is a sequence of non-letters followed by a single letter.

// 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.

// A Formatter formats error messages.

// FormatError prints the receiver's first error and returns the next error in
// the error chain, if any.

// A Printer formats error messages.
//
// The most common implementation of Printer is the one provided by package fmt
// during Printf (as of Go 1.13). Localization packages such as golang.org/x/text/message
// typically provide their own implementations.

// Print appends args to the message output.

// Printf writes a formatted string.

// Detail reports whether error detail is requested.
// After the first call to Detail, all text written to the Printer
// is formatted as additional detail, or ignored when
// detail has not been requested.
// If Detail returns false, the caller can avoid printing the detail at all.

Run again:

$ go doc -src xerrors.As
package xerrors // import "golang.org/x/xerrors"

// As finds the first error in err's chain that matches the type to which target
// points, and if so, sets the target to its value and returns true. An error
// matches a type if it is assignable to the target type, or if it has a method
// As(interface{}) bool such that As(target) returns true. As will panic if target
// is not a non-nil pointer to a type which implements error or is of interface type.
//
// The As method should set the target to its value and return true if err
// matches the type to which target points.
func As(err error, target interface{}) bool {
	if target == nil {
		panic("errors: target cannot be nil")
	}
	val := reflect.ValueOf(target)
	typ := val.Type()
	if typ.Kind() != reflect.Ptr || val.IsNil() {
		panic("errors: target must be a non-nil pointer")
	}
	if e := typ.Elem(); e.Kind() != reflect.Interface && !e.Implements(errorType) {
		panic("errors: *target must be interface or implement error")
	}
	targetType := typ.Elem()
	for err != nil {
		if reflect.TypeOf(err).AssignableTo(targetType) {
			val.Elem().Set(reflect.ValueOf(err))
			return true
		}
		if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) {
			return true
		}
		err = Unwrap(err)
	}
	return false
}
$

The garbled output shows up approximately 50% of the time.

What did you expect to see?

Function comment and body for the As function only.

What did you see instead?

Output that seems to combine two streams of source.

@agnivade agnivade changed the title go doc: Race in -src output cmd/doc: garbled -src output Feb 5, 2020
@agnivade agnivade added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Feb 5, 2020
@agnivade agnivade added this to the Backlog milestone Feb 5, 2020
@tv42
Copy link
Author

tv42 commented Feb 7, 2020

It seems this happened again, with crawshaw.io/sqlite/sqlitex:

$ go doc -src sqlitex.Pool.GetSnapshot
package sqlitex // import "crawshaw.io/sqlite/sqlitex"

// GetSnapshot returns a Snapshot that should remain available for reads until
// it is garbage collected.
//
// This sets aside a Conn from the Pool with an open read transaction until the
// Snapshot is garbage collected or the Pool is closed.  Thus, until the
// returned Snapshot is garbage collected, the Pool will have one fewer Conn,
// and it should not be possible for the WAL to be checkpointed beyond the
// point of the Snapshot.
//
// See sqlite.Conn.GetSnapshot and sqlite.Snapshot for more details.
func (p *Pool) GetSnapshot(ctx context.Context, schema string) (*sqlite.Snapshot, error) {
	conn := p.Get(ctx)
	if conn == nil {
		return nil, context.Canceled
	}
	conn.SetInterrupt(nil)
	s, release, err := conn.GetSnapshot(schema)
	if err != nil {
		return nil, err
	}

	snapshotGCd := make(chan struct{})
	runtime.SetFinalizer(s, nil)
	runtime.SetFinalizer(s, func(s *sqlite.Snapshot) {
		// Free the C resources associated with the Snapshot.
		s.Free()
		close(snapshotGCd)
	})

	go func() {
		select {
		case <-p.closed:
		case <-snapshotGCd:
		}
		// Allow the WAL to be checkpointed past the point of
		// the Snapshot.
		release()
		// Return the conn to the Pool for reuse.

		// Copyright (c) 2018 David Crawshaw <david@zentus.com>
		//
		// Permission to use, copy, modify, and distribute this software for any
		// purpose with or without fee is hereby granted, provided that the above
		// copyright notice and this permission notice appear in all copies.
		//
		// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
		// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
		// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
		// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
		// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
		// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
		// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

		// A Buffer is a variable-sized bytes buffer backed by SQLite blobs.
		//
		// The bytes are broken into pages, with the first and last pages
		// stored in memory, and intermediate pages loaded into blobs.
		// Unlike a single SQLite blob, a Buffer can grow beyond its initial size.
		// The blobs are allocated in a temporary table.
		//
		// A Buffer is very similar to a bytes.Buffer.

		p.Put(conn)
	}()

	return s, nil
}

// cap(rbuf) == cap(wbuf) == blobs[N].Size()

// read buffer
// read head position in roff
// blobs storing data between rbuf and wbuf
// write buffer

// NewBuffer creates a Buffer with 16KB pages.

// NewBufferSize creates a Buffer with a specified page size.

// Flush the write buffer.

// Short cut. The write buffer is full, but
// there are no on-disk blobs and the read
// buffer is empty. So push these bytes
// directly to the front of the Buffer.

// WriteByte appends a byte to the buffer, growing it as needed.

// TODO: shortcut for writing large p directly into a new blob

// never grows wbuf

// TODO: shortcut for writing large p directly into a new blob

// never grows wbuf

// Read buffer is empty. Fill it.

// Read the first blob entirely into the read buffer.
// TODO: shortcut for if len(p) >= blob.Size()

// No blobs. Swap the write buffer bytes here directly.

The license text and the trailing comments are not part of the function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

2 participants