mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
[dev.typeparams] merge master into dev.typeparams
Change-Id: Ib2a0f85e00a7366b784e3615366ca3bde4ec8c49
This commit is contained in:
commit
986cad14e2
170 changed files with 3046 additions and 1321 deletions
|
|
@ -38,6 +38,17 @@ Do not send CLs removing the interior tags from such phrases.
|
||||||
<code>netbsd/arm64</code> port).
|
<code>netbsd/arm64</code> port).
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<h3 id="386">386</h3>
|
||||||
|
|
||||||
|
<p><!-- golang.org/issue/40255, golang.org/issue/41848, CL 258957, and CL 260017 -->
|
||||||
|
As <a href="go1.15#386">announced</a> in the Go 1.15 release notes,
|
||||||
|
Go 1.16 drops support for x87 mode compilation (<code>GO386=387</code>).
|
||||||
|
Support for non-SSE2 processors is now available using soft float
|
||||||
|
mode (<code>GO386=softfloat</code>).
|
||||||
|
Users running on non-SSE2 processors should replace <code>GO386=387</code>
|
||||||
|
with <code>GO386=softfloat</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2 id="tools">Tools</h2>
|
<h2 id="tools">Tools</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
@ -162,6 +173,8 @@ Do not send CLs removing the interior tags from such phrases.
|
||||||
TODO: update with final numbers later in the release.
|
TODO: update with final numbers later in the release.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<!-- CL 255259: https://golang.org/cl/255259: cmd/link: enable ASLR on windows binaries built with -buildmode=c-shared -->
|
||||||
|
|
||||||
<h2 id="library">Core library</h2>
|
<h2 id="library">Core library</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
@ -200,6 +213,14 @@ Do not send CLs removing the interior tags from such phrases.
|
||||||
with <code>"use of closed network connection"</code>.
|
with <code>"use of closed network connection"</code>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<h3 id="reflect"><a href="/pkg/reflect/">reflect</a></h3>
|
||||||
|
|
||||||
|
<p><!-- CL 259237, golang.org/issue/22075 -->
|
||||||
|
For interface types and values, <a href="/pkg/reflect/#Value.Method">Method</a>,
|
||||||
|
<a href="/pkg/reflect/#Value.MethodByName">MethodByName</a>, and
|
||||||
|
<a href="/pkg/reflect/#Value.NumMethod">NumMethod</a> now
|
||||||
|
operate on the interface's exported method set, rather than its full method set.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h3 id="text/template/parse"><a href="/pkg/text/template/parse/">text/template/parse</a></h3>
|
<h3 id="text/template/parse"><a href="/pkg/text/template/parse/">text/template/parse</a></h3>
|
||||||
|
|
||||||
|
|
@ -273,5 +294,18 @@ Do not send CLs removing the interior tags from such phrases.
|
||||||
of the form <code>"Range": "bytes=--N"</code> where <code>"-N"</code> is a negative suffix length, for
|
of the form <code>"Range": "bytes=--N"</code> where <code>"-N"</code> is a negative suffix length, for
|
||||||
example <code>"Range": "bytes=--2"</code>. It now replies with a <code>416 "Range Not Satisfiable"</code> response.
|
example <code>"Range": "bytes=--2"</code>. It now replies with a <code>416 "Range Not Satisfiable"</code> response.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p><!-- CL 256498, golang.org/issue/36990 -->
|
||||||
|
Cookies set with <code>SameSiteDefaultMode</code> now behave according to the current
|
||||||
|
spec (no attribute is set) instead of generating a SameSite key without a value.
|
||||||
|
</p>
|
||||||
</dd>
|
</dd>
|
||||||
</dl><!-- net/http -->
|
</dl><!-- net/http -->
|
||||||
|
|
||||||
|
<dl id="runtime/debug"><dt><a href="/pkg/runtime/debug/">runtime/debug</a></dt>
|
||||||
|
<dd>
|
||||||
|
<p><!-- CL 249677 -->
|
||||||
|
TODO: <a href="https://golang.org/cl/249677">https://golang.org/cl/249677</a>: provide Addr method for errors from SetPanicOnFault
|
||||||
|
</p>
|
||||||
|
</dd>
|
||||||
|
</dl><!-- runtime/debug -->
|
||||||
|
|
|
||||||
|
|
@ -666,16 +666,13 @@ For example, you should not set <code>$GOHOSTARCH</code> to
|
||||||
<code>arm</code> on an x86 system.
|
<code>arm</code> on an x86 system.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<li><code>$GO386</code> (for <code>386</code> only, default is auto-detected
|
<li><code>$GO386</code> (for <code>386</code> only, defaults to <code>sse2</code>)
|
||||||
if built on either <code>386</code> or <code>amd64</code>, <code>387</code> otherwise)
|
|
||||||
<p>
|
<p>
|
||||||
This controls the code generated by gc to use either the 387 floating-point unit
|
This variable controls how gc implements floating point computations.
|
||||||
(set to <code>387</code>) or SSE2 instructions (set to <code>sse2</code>) for
|
|
||||||
floating point computations.
|
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>GO386=387</code>: use x87 for floating point operations; should support all x86 chips (Pentium MMX or later).</li>
|
<li><code>GO386=softfloat</code>: use software floating point operations; should support all x86 chips (Pentium MMX or later).</li>
|
||||||
<li><code>GO386=sse2</code>: use SSE2 for floating point operations; has better performance than 387, but only available on Pentium 4/Opteron/Athlon 64 or later.</li>
|
<li><code>GO386=sse2</code>: use SSE2 for floating point operations; has better performance but only available on Pentium 4/Opteron/Athlon 64 or later.</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ import (
|
||||||
|
|
||||||
func testSigaltstack(t *testing.T) {
|
func testSigaltstack(t *testing.T) {
|
||||||
switch {
|
switch {
|
||||||
case runtime.GOOS == "solaris", runtime.GOOS == "illumos", (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64":
|
case runtime.GOOS == "solaris", runtime.GOOS == "illumos", runtime.GOOS == "ios" && runtime.GOARCH == "arm64":
|
||||||
t.Skipf("switching signal stack not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
t.Skipf("switching signal stack not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -603,7 +603,7 @@ func TestExtar(t *testing.T) {
|
||||||
if runtime.Compiler == "gccgo" {
|
if runtime.Compiler == "gccgo" {
|
||||||
t.Skip("skipping -extar test when using gccgo")
|
t.Skip("skipping -extar test when using gccgo")
|
||||||
}
|
}
|
||||||
if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" {
|
if runtime.GOOS == "ios" {
|
||||||
t.Skip("shell scripts are not executable on iOS hosts")
|
t.Skip("shell scripts are not executable on iOS hosts")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
4
src/cmd/asm/internal/asm/testdata/arm64.s
vendored
4
src/cmd/asm/internal/asm/testdata/arm64.s
vendored
|
|
@ -81,6 +81,8 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
|
||||||
SHA512H2 V4.D2, V3, V2 // 628464ce
|
SHA512H2 V4.D2, V3, V2 // 628464ce
|
||||||
SHA512SU0 V9.D2, V8.D2 // 2881c0ce
|
SHA512SU0 V9.D2, V8.D2 // 2881c0ce
|
||||||
SHA512SU1 V7.D2, V6.D2, V5.D2 // c58867ce
|
SHA512SU1 V7.D2, V6.D2, V5.D2 // c58867ce
|
||||||
|
VRAX1 V26.D2, V29.D2, V30.D2 // be8f7ace
|
||||||
|
VXAR $63, V27.D2, V21.D2, V26.D2 // bafe9bce
|
||||||
VADDV V0.S4, V0 // 00b8b14e
|
VADDV V0.S4, V0 // 00b8b14e
|
||||||
VMOVI $82, V0.B16 // 40e6024f
|
VMOVI $82, V0.B16 // 40e6024f
|
||||||
VUADDLV V6.B16, V6 // c638306e
|
VUADDLV V6.B16, V6 // c638306e
|
||||||
|
|
@ -139,6 +141,8 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
|
||||||
VTBL V14.B16, [V3.B16, V4.B16, V5.B16], V17.B16 // 71400e4e
|
VTBL V14.B16, [V3.B16, V4.B16, V5.B16], V17.B16 // 71400e4e
|
||||||
VTBL V13.B16, [V29.B16, V30.B16, V31.B16, V0.B16], V28.B16 // bc630d4e
|
VTBL V13.B16, [V29.B16, V30.B16, V31.B16, V0.B16], V28.B16 // bc630d4e
|
||||||
VTBL V3.B8, [V27.B16], V8.B8 // 6803030e
|
VTBL V3.B8, [V27.B16], V8.B8 // 6803030e
|
||||||
|
VEOR3 V2.B16, V7.B16, V12.B16, V25.B16 // 990907ce
|
||||||
|
VBCAX V1.B16, V2.B16, V26.B16, V31.B16 // 5f0722ce
|
||||||
VZIP1 V16.H8, V3.H8, V19.H8 // 7338504e
|
VZIP1 V16.H8, V3.H8, V19.H8 // 7338504e
|
||||||
VZIP2 V22.D2, V25.D2, V21.D2 // 357bd64e
|
VZIP2 V22.D2, V25.D2, V21.D2 // 357bd64e
|
||||||
VZIP1 V6.D2, V9.D2, V11.D2 // 2b39c64e
|
VZIP1 V6.D2, V9.D2, V11.D2 // 2b39c64e
|
||||||
|
|
|
||||||
|
|
@ -170,35 +170,51 @@ func usage() {
|
||||||
|
|
||||||
var ptrSizeMap = map[string]int64{
|
var ptrSizeMap = map[string]int64{
|
||||||
"386": 4,
|
"386": 4,
|
||||||
|
"alpha": 8,
|
||||||
"amd64": 8,
|
"amd64": 8,
|
||||||
"arm": 4,
|
"arm": 4,
|
||||||
"arm64": 8,
|
"arm64": 8,
|
||||||
|
"m68k": 4,
|
||||||
"mips": 4,
|
"mips": 4,
|
||||||
"mipsle": 4,
|
"mipsle": 4,
|
||||||
"mips64": 8,
|
"mips64": 8,
|
||||||
"mips64le": 8,
|
"mips64le": 8,
|
||||||
|
"nios2": 4,
|
||||||
|
"ppc": 4,
|
||||||
"ppc64": 8,
|
"ppc64": 8,
|
||||||
"ppc64le": 8,
|
"ppc64le": 8,
|
||||||
|
"riscv": 4,
|
||||||
"riscv64": 8,
|
"riscv64": 8,
|
||||||
"s390": 4,
|
"s390": 4,
|
||||||
"s390x": 8,
|
"s390x": 8,
|
||||||
|
"sh": 4,
|
||||||
|
"shbe": 4,
|
||||||
|
"sparc": 4,
|
||||||
"sparc64": 8,
|
"sparc64": 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
var intSizeMap = map[string]int64{
|
var intSizeMap = map[string]int64{
|
||||||
"386": 4,
|
"386": 4,
|
||||||
|
"alpha": 8,
|
||||||
"amd64": 8,
|
"amd64": 8,
|
||||||
"arm": 4,
|
"arm": 4,
|
||||||
"arm64": 8,
|
"arm64": 8,
|
||||||
|
"m68k": 4,
|
||||||
"mips": 4,
|
"mips": 4,
|
||||||
"mipsle": 4,
|
"mipsle": 4,
|
||||||
"mips64": 8,
|
"mips64": 8,
|
||||||
"mips64le": 8,
|
"mips64le": 8,
|
||||||
|
"nios2": 4,
|
||||||
|
"ppc": 4,
|
||||||
"ppc64": 8,
|
"ppc64": 8,
|
||||||
"ppc64le": 8,
|
"ppc64le": 8,
|
||||||
|
"riscv": 4,
|
||||||
"riscv64": 8,
|
"riscv64": 8,
|
||||||
"s390": 4,
|
"s390": 4,
|
||||||
"s390x": 8,
|
"s390x": 8,
|
||||||
|
"sh": 4,
|
||||||
|
"shbe": 4,
|
||||||
|
"sparc": 4,
|
||||||
"sparc64": 8,
|
"sparc64": 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -126,30 +126,6 @@ const (
|
||||||
aliasTag
|
aliasTag
|
||||||
)
|
)
|
||||||
|
|
||||||
// untype returns the "pseudo" untyped type for a Ctype (import/export use only).
|
|
||||||
// (we can't use a pre-initialized array because we must be sure all types are
|
|
||||||
// set up)
|
|
||||||
func untype(ctype Ctype) *types.Type {
|
|
||||||
switch ctype {
|
|
||||||
case CTINT:
|
|
||||||
return types.Idealint
|
|
||||||
case CTRUNE:
|
|
||||||
return types.Idealrune
|
|
||||||
case CTFLT:
|
|
||||||
return types.Idealfloat
|
|
||||||
case CTCPLX:
|
|
||||||
return types.Idealcomplex
|
|
||||||
case CTSTR:
|
|
||||||
return types.Idealstring
|
|
||||||
case CTBOOL:
|
|
||||||
return types.Idealbool
|
|
||||||
case CTNIL:
|
|
||||||
return types.Types[TNIL]
|
|
||||||
}
|
|
||||||
Fatalf("exporter: unknown Ctype")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var predecl []*types.Type // initialized lazily
|
var predecl []*types.Type // initialized lazily
|
||||||
|
|
||||||
func predeclared() []*types.Type {
|
func predeclared() []*types.Type {
|
||||||
|
|
@ -184,13 +160,13 @@ func predeclared() []*types.Type {
|
||||||
types.Errortype,
|
types.Errortype,
|
||||||
|
|
||||||
// untyped types
|
// untyped types
|
||||||
untype(CTBOOL),
|
types.UntypedBool,
|
||||||
untype(CTINT),
|
types.UntypedInt,
|
||||||
untype(CTRUNE),
|
types.UntypedRune,
|
||||||
untype(CTFLT),
|
types.UntypedFloat,
|
||||||
untype(CTCPLX),
|
types.UntypedComplex,
|
||||||
untype(CTSTR),
|
types.UntypedString,
|
||||||
untype(CTNIL),
|
types.Types[TNIL],
|
||||||
|
|
||||||
// package unsafe
|
// package unsafe
|
||||||
types.Types[TUNSAFEPTR],
|
types.Types[TUNSAFEPTR],
|
||||||
|
|
|
||||||
|
|
@ -1019,17 +1019,17 @@ func nodlit(v Val) *Node {
|
||||||
func idealType(ct Ctype) *types.Type {
|
func idealType(ct Ctype) *types.Type {
|
||||||
switch ct {
|
switch ct {
|
||||||
case CTSTR:
|
case CTSTR:
|
||||||
return types.Idealstring
|
return types.UntypedString
|
||||||
case CTBOOL:
|
case CTBOOL:
|
||||||
return types.Idealbool
|
return types.UntypedBool
|
||||||
case CTINT:
|
case CTINT:
|
||||||
return types.Idealint
|
return types.UntypedInt
|
||||||
case CTRUNE:
|
case CTRUNE:
|
||||||
return types.Idealrune
|
return types.UntypedRune
|
||||||
case CTFLT:
|
case CTFLT:
|
||||||
return types.Idealfloat
|
return types.UntypedFloat
|
||||||
case CTCPLX:
|
case CTCPLX:
|
||||||
return types.Idealcomplex
|
return types.UntypedComplex
|
||||||
case CTNIL:
|
case CTNIL:
|
||||||
return types.Types[TNIL]
|
return types.Types[TNIL]
|
||||||
}
|
}
|
||||||
|
|
@ -1080,17 +1080,17 @@ func defaultlit2(l *Node, r *Node, force bool) (*Node, *Node) {
|
||||||
|
|
||||||
func ctype(t *types.Type) Ctype {
|
func ctype(t *types.Type) Ctype {
|
||||||
switch t {
|
switch t {
|
||||||
case types.Idealbool:
|
case types.UntypedBool:
|
||||||
return CTBOOL
|
return CTBOOL
|
||||||
case types.Idealstring:
|
case types.UntypedString:
|
||||||
return CTSTR
|
return CTSTR
|
||||||
case types.Idealint:
|
case types.UntypedInt:
|
||||||
return CTINT
|
return CTINT
|
||||||
case types.Idealrune:
|
case types.UntypedRune:
|
||||||
return CTRUNE
|
return CTRUNE
|
||||||
case types.Idealfloat:
|
case types.UntypedFloat:
|
||||||
return CTFLT
|
return CTFLT
|
||||||
case types.Idealcomplex:
|
case types.UntypedComplex:
|
||||||
return CTCPLX
|
return CTCPLX
|
||||||
}
|
}
|
||||||
Fatalf("bad type %v", t)
|
Fatalf("bad type %v", t)
|
||||||
|
|
@ -1111,17 +1111,17 @@ func defaultType(t *types.Type) *types.Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch t {
|
switch t {
|
||||||
case types.Idealbool:
|
case types.UntypedBool:
|
||||||
return types.Types[TBOOL]
|
return types.Types[TBOOL]
|
||||||
case types.Idealstring:
|
case types.UntypedString:
|
||||||
return types.Types[TSTRING]
|
return types.Types[TSTRING]
|
||||||
case types.Idealint:
|
case types.UntypedInt:
|
||||||
return types.Types[TINT]
|
return types.Types[TINT]
|
||||||
case types.Idealrune:
|
case types.UntypedRune:
|
||||||
return types.Runetype
|
return types.Runetype
|
||||||
case types.Idealfloat:
|
case types.UntypedFloat:
|
||||||
return types.Types[TFLOAT64]
|
return types.Types[TFLOAT64]
|
||||||
case types.Idealcomplex:
|
case types.UntypedComplex:
|
||||||
return types.Types[TCOMPLEX128]
|
return types.Types[TCOMPLEX128]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ func importsym(ipkg *types.Pkg, s *types.Sym, op Op) *Node {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
// pkgtype returns the named type declared by symbol s.
|
// importtype returns the named type declared by symbol s.
|
||||||
// If no such type has been declared yet, a forward declaration is returned.
|
// If no such type has been declared yet, a forward declaration is returned.
|
||||||
// ipkg is the package being imported
|
// ipkg is the package being imported
|
||||||
func importtype(ipkg *types.Pkg, pos src.XPos, s *types.Sym) *types.Type {
|
func importtype(ipkg *types.Pkg, pos src.XPos, s *types.Sym) *types.Type {
|
||||||
|
|
|
||||||
|
|
@ -773,17 +773,17 @@ func tconv2(b *bytes.Buffer, t *types.Type, flag FmtFlag, mode fmtMode, visited
|
||||||
if int(t.Etype) < len(basicnames) && basicnames[t.Etype] != "" {
|
if int(t.Etype) < len(basicnames) && basicnames[t.Etype] != "" {
|
||||||
var name string
|
var name string
|
||||||
switch t {
|
switch t {
|
||||||
case types.Idealbool:
|
case types.UntypedBool:
|
||||||
name = "untyped bool"
|
name = "untyped bool"
|
||||||
case types.Idealstring:
|
case types.UntypedString:
|
||||||
name = "untyped string"
|
name = "untyped string"
|
||||||
case types.Idealint:
|
case types.UntypedInt:
|
||||||
name = "untyped int"
|
name = "untyped int"
|
||||||
case types.Idealrune:
|
case types.UntypedRune:
|
||||||
name = "untyped rune"
|
name = "untyped rune"
|
||||||
case types.Idealfloat:
|
case types.UntypedFloat:
|
||||||
name = "untyped float"
|
name = "untyped float"
|
||||||
case types.Idealcomplex:
|
case types.UntypedComplex:
|
||||||
name = "untyped complex"
|
name = "untyped complex"
|
||||||
default:
|
default:
|
||||||
name = basicnames[t.Etype]
|
name = basicnames[t.Etype]
|
||||||
|
|
@ -1333,7 +1333,7 @@ func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) {
|
||||||
n.Orig.exprfmt(s, prec, mode)
|
n.Orig.exprfmt(s, prec, mode)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if n.Type != nil && n.Type.Etype != TIDEAL && n.Type.Etype != TNIL && n.Type != types.Idealbool && n.Type != types.Idealstring {
|
if n.Type != nil && n.Type.Etype != TIDEAL && n.Type.Etype != TNIL && n.Type != types.UntypedBool && n.Type != types.UntypedString {
|
||||||
// Need parens when type begins with what might
|
// Need parens when type begins with what might
|
||||||
// be misinterpreted as a unary operator: * or <-.
|
// be misinterpreted as a unary operator: * or <-.
|
||||||
if n.Type.IsPtr() || (n.Type.IsChan() && n.Type.ChanDir() == types.Crecv) {
|
if n.Type.IsPtr() || (n.Type.IsChan() && n.Type.ChanDir() == types.Crecv) {
|
||||||
|
|
|
||||||
|
|
@ -751,11 +751,11 @@ func (w *exportWriter) param(f *types.Field) {
|
||||||
|
|
||||||
func constTypeOf(typ *types.Type) Ctype {
|
func constTypeOf(typ *types.Type) Ctype {
|
||||||
switch typ {
|
switch typ {
|
||||||
case types.Idealint, types.Idealrune:
|
case types.UntypedInt, types.UntypedRune:
|
||||||
return CTINT
|
return CTINT
|
||||||
case types.Idealfloat:
|
case types.UntypedFloat:
|
||||||
return CTFLT
|
return CTFLT
|
||||||
case types.Idealcomplex:
|
case types.UntypedComplex:
|
||||||
return CTCPLX
|
return CTCPLX
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -780,8 +780,8 @@ func constTypeOf(typ *types.Type) Ctype {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *exportWriter) value(typ *types.Type, v Val) {
|
func (w *exportWriter) value(typ *types.Type, v Val) {
|
||||||
if typ.IsUntyped() {
|
if vt := idealType(v.Ctype()); typ.IsUntyped() && typ != vt {
|
||||||
typ = untype(v.Ctype())
|
Fatalf("exporter: untyped type mismatch, have: %v, want: %v", typ, vt)
|
||||||
}
|
}
|
||||||
w.typ(typ)
|
w.typ(typ)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -375,7 +375,7 @@ func (p *importReader) value() (typ *types.Type, v Val) {
|
||||||
v.U = p.string()
|
v.U = p.string()
|
||||||
case CTINT:
|
case CTINT:
|
||||||
x := new(Mpint)
|
x := new(Mpint)
|
||||||
x.Rune = typ == types.Idealrune
|
x.Rune = typ == types.UntypedRune
|
||||||
p.mpint(&x.Val, typ)
|
p.mpint(&x.Val, typ)
|
||||||
v.U = x
|
v.U = x
|
||||||
case CTFLT:
|
case CTFLT:
|
||||||
|
|
|
||||||
|
|
@ -1275,8 +1275,9 @@ func dtypesym(t *types.Type) *obj.LSym {
|
||||||
}
|
}
|
||||||
ot = dgopkgpath(lsym, ot, tpkg)
|
ot = dgopkgpath(lsym, ot, tpkg)
|
||||||
|
|
||||||
|
xcount := sort.Search(n, func(i int) bool { return !types.IsExported(m[i].name.Name) })
|
||||||
ot = dsymptr(lsym, ot, lsym, ot+3*Widthptr+uncommonSize(t))
|
ot = dsymptr(lsym, ot, lsym, ot+3*Widthptr+uncommonSize(t))
|
||||||
ot = duintptr(lsym, ot, uint64(n))
|
ot = duintptr(lsym, ot, uint64(xcount))
|
||||||
ot = duintptr(lsym, ot, uint64(n))
|
ot = duintptr(lsym, ot, uint64(n))
|
||||||
dataAdd := imethodSize() * n
|
dataAdd := imethodSize() * n
|
||||||
ot = dextratype(lsym, ot, t, dataAdd)
|
ot = dextratype(lsym, ot, t, dataAdd)
|
||||||
|
|
|
||||||
|
|
@ -50,12 +50,10 @@ func initssaconfig() {
|
||||||
// Caching is disabled in the backend, so generating these here avoids allocations.
|
// Caching is disabled in the backend, so generating these here avoids allocations.
|
||||||
_ = types.NewPtr(types.Types[TINTER]) // *interface{}
|
_ = types.NewPtr(types.Types[TINTER]) // *interface{}
|
||||||
_ = types.NewPtr(types.NewPtr(types.Types[TSTRING])) // **string
|
_ = types.NewPtr(types.NewPtr(types.Types[TSTRING])) // **string
|
||||||
_ = types.NewPtr(types.NewPtr(types.Idealstring)) // **string
|
|
||||||
_ = types.NewPtr(types.NewSlice(types.Types[TINTER])) // *[]interface{}
|
_ = types.NewPtr(types.NewSlice(types.Types[TINTER])) // *[]interface{}
|
||||||
_ = types.NewPtr(types.NewPtr(types.Bytetype)) // **byte
|
_ = types.NewPtr(types.NewPtr(types.Bytetype)) // **byte
|
||||||
_ = types.NewPtr(types.NewSlice(types.Bytetype)) // *[]byte
|
_ = types.NewPtr(types.NewSlice(types.Bytetype)) // *[]byte
|
||||||
_ = types.NewPtr(types.NewSlice(types.Types[TSTRING])) // *[]string
|
_ = types.NewPtr(types.NewSlice(types.Types[TSTRING])) // *[]string
|
||||||
_ = types.NewPtr(types.NewSlice(types.Idealstring)) // *[]string
|
|
||||||
_ = types.NewPtr(types.NewPtr(types.NewPtr(types.Types[TUINT8]))) // ***uint8
|
_ = types.NewPtr(types.NewPtr(types.NewPtr(types.Types[TUINT8]))) // ***uint8
|
||||||
_ = types.NewPtr(types.Types[TINT16]) // *int16
|
_ = types.NewPtr(types.Types[TINT16]) // *int16
|
||||||
_ = types.NewPtr(types.Types[TINT64]) // *int64
|
_ = types.NewPtr(types.Types[TINT64]) // *int64
|
||||||
|
|
@ -4251,6 +4249,7 @@ func (s *state) openDeferExit() {
|
||||||
s.lastDeferExit = deferExit
|
s.lastDeferExit = deferExit
|
||||||
s.lastDeferCount = len(s.openDefers)
|
s.lastDeferCount = len(s.openDefers)
|
||||||
zeroval := s.constInt8(types.Types[TUINT8], 0)
|
zeroval := s.constInt8(types.Types[TUINT8], 0)
|
||||||
|
testLateExpansion := ssa.LateCallExpansionEnabledWithin(s.f)
|
||||||
// Test for and run defers in reverse order
|
// Test for and run defers in reverse order
|
||||||
for i := len(s.openDefers) - 1; i >= 0; i-- {
|
for i := len(s.openDefers) - 1; i >= 0; i-- {
|
||||||
r := s.openDefers[i]
|
r := s.openDefers[i]
|
||||||
|
|
@ -4288,23 +4287,38 @@ func (s *state) openDeferExit() {
|
||||||
stksize := fn.Type.ArgWidth()
|
stksize := fn.Type.ArgWidth()
|
||||||
var ACArgs []ssa.Param
|
var ACArgs []ssa.Param
|
||||||
var ACResults []ssa.Param
|
var ACResults []ssa.Param
|
||||||
|
var callArgs []*ssa.Value
|
||||||
if r.rcvr != nil {
|
if r.rcvr != nil {
|
||||||
// rcvr in case of OCALLINTER
|
// rcvr in case of OCALLINTER
|
||||||
v := s.load(r.rcvr.Type.Elem(), r.rcvr)
|
v := s.load(r.rcvr.Type.Elem(), r.rcvr)
|
||||||
addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart)
|
addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart)
|
||||||
ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart)})
|
ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart)})
|
||||||
s.store(types.Types[TUINTPTR], addr, v)
|
if testLateExpansion {
|
||||||
|
callArgs = append(callArgs, v)
|
||||||
|
} else {
|
||||||
|
s.store(types.Types[TUINTPTR], addr, v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for j, argAddrVal := range r.argVals {
|
for j, argAddrVal := range r.argVals {
|
||||||
f := getParam(r.n, j)
|
f := getParam(r.n, j)
|
||||||
pt := types.NewPtr(f.Type)
|
pt := types.NewPtr(f.Type)
|
||||||
addr := s.constOffPtrSP(pt, argStart+f.Offset)
|
ACArgs = append(ACArgs, ssa.Param{Type: f.Type, Offset: int32(argStart + f.Offset)})
|
||||||
ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(argStart + f.Offset)})
|
if testLateExpansion {
|
||||||
if !canSSAType(f.Type) {
|
var a *ssa.Value
|
||||||
s.move(f.Type, addr, argAddrVal)
|
if !canSSAType(f.Type) {
|
||||||
|
a = s.newValue2(ssa.OpDereference, f.Type, argAddrVal, s.mem())
|
||||||
|
} else {
|
||||||
|
a = s.load(f.Type, argAddrVal)
|
||||||
|
}
|
||||||
|
callArgs = append(callArgs, a)
|
||||||
} else {
|
} else {
|
||||||
argVal := s.load(f.Type, argAddrVal)
|
addr := s.constOffPtrSP(pt, argStart+f.Offset)
|
||||||
s.storeType(f.Type, addr, argVal, 0, false)
|
if !canSSAType(f.Type) {
|
||||||
|
s.move(f.Type, addr, argAddrVal)
|
||||||
|
} else {
|
||||||
|
argVal := s.load(f.Type, argAddrVal)
|
||||||
|
s.storeType(f.Type, addr, argVal, 0, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var call *ssa.Value
|
var call *ssa.Value
|
||||||
|
|
@ -4312,13 +4326,31 @@ func (s *state) openDeferExit() {
|
||||||
v := s.load(r.closure.Type.Elem(), r.closure)
|
v := s.load(r.closure.Type.Elem(), r.closure)
|
||||||
s.maybeNilCheckClosure(v, callDefer)
|
s.maybeNilCheckClosure(v, callDefer)
|
||||||
codeptr := s.rawLoad(types.Types[TUINTPTR], v)
|
codeptr := s.rawLoad(types.Types[TUINTPTR], v)
|
||||||
call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, ssa.ClosureAuxCall(ACArgs, ACResults), codeptr, v, s.mem())
|
aux := ssa.ClosureAuxCall(ACArgs, ACResults)
|
||||||
|
if testLateExpansion {
|
||||||
|
callArgs = append(callArgs, s.mem())
|
||||||
|
call = s.newValue2A(ssa.OpClosureLECall, aux.LateExpansionResultType(), aux, codeptr, v)
|
||||||
|
call.AddArgs(callArgs...)
|
||||||
|
} else {
|
||||||
|
call = s.newValue3A(ssa.OpClosureCall, types.TypeMem, aux, codeptr, v, s.mem())
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Do a static call if the original call was a static function or method
|
aux := ssa.StaticAuxCall(fn.Sym.Linksym(), ACArgs, ACResults)
|
||||||
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn.Sym.Linksym(), ACArgs, ACResults), s.mem())
|
if testLateExpansion {
|
||||||
|
callArgs = append(callArgs, s.mem())
|
||||||
|
call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
|
||||||
|
call.AddArgs(callArgs...)
|
||||||
|
} else {
|
||||||
|
// Do a static call if the original call was a static function or method
|
||||||
|
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
call.AuxInt = stksize
|
call.AuxInt = stksize
|
||||||
s.vars[&memVar] = call
|
if testLateExpansion {
|
||||||
|
s.vars[&memVar] = s.newValue1I(ssa.OpSelectN, types.TypeMem, int64(len(ACResults)), call)
|
||||||
|
} else {
|
||||||
|
s.vars[&memVar] = call
|
||||||
|
}
|
||||||
// Make sure that the stack slots with pointers are kept live
|
// Make sure that the stack slots with pointers are kept live
|
||||||
// through the call (which is a pre-emption point). Also, we will
|
// through the call (which is a pre-emption point). Also, we will
|
||||||
// use the first call of the last defer exit to compute liveness
|
// use the first call of the last defer exit to compute liveness
|
||||||
|
|
@ -4375,9 +4407,7 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value {
|
||||||
|
|
||||||
switch n.Op {
|
switch n.Op {
|
||||||
case OCALLFUNC:
|
case OCALLFUNC:
|
||||||
if k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f) {
|
testLateExpansion = k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f)
|
||||||
testLateExpansion = true
|
|
||||||
}
|
|
||||||
if k == callNormal && fn.Op == ONAME && fn.Class() == PFUNC {
|
if k == callNormal && fn.Op == ONAME && fn.Class() == PFUNC {
|
||||||
sym = fn.Sym
|
sym = fn.Sym
|
||||||
break
|
break
|
||||||
|
|
@ -4392,9 +4422,7 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value {
|
||||||
if fn.Op != ODOTMETH {
|
if fn.Op != ODOTMETH {
|
||||||
s.Fatalf("OCALLMETH: n.Left not an ODOTMETH: %v", fn)
|
s.Fatalf("OCALLMETH: n.Left not an ODOTMETH: %v", fn)
|
||||||
}
|
}
|
||||||
if k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f) {
|
testLateExpansion = k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f)
|
||||||
testLateExpansion = true
|
|
||||||
}
|
|
||||||
if k == callNormal {
|
if k == callNormal {
|
||||||
sym = fn.Sym
|
sym = fn.Sym
|
||||||
break
|
break
|
||||||
|
|
@ -4406,9 +4434,7 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value {
|
||||||
if fn.Op != ODOTINTER {
|
if fn.Op != ODOTINTER {
|
||||||
s.Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op)
|
s.Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op)
|
||||||
}
|
}
|
||||||
if k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f) {
|
testLateExpansion = k != callDeferStack && ssa.LateCallExpansionEnabledWithin(s.f)
|
||||||
testLateExpansion = true
|
|
||||||
}
|
|
||||||
var iclosure *ssa.Value
|
var iclosure *ssa.Value
|
||||||
iclosure, rcvr = s.getClosureAndRcvr(fn)
|
iclosure, rcvr = s.getClosureAndRcvr(fn)
|
||||||
if k == callNormal {
|
if k == callNormal {
|
||||||
|
|
@ -4427,6 +4453,7 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value {
|
||||||
|
|
||||||
var call *ssa.Value
|
var call *ssa.Value
|
||||||
if k == callDeferStack {
|
if k == callDeferStack {
|
||||||
|
testLateExpansion = ssa.LateCallExpansionEnabledWithin(s.f)
|
||||||
// Make a defer struct d on the stack.
|
// Make a defer struct d on the stack.
|
||||||
t := deferstruct(stksize)
|
t := deferstruct(stksize)
|
||||||
d := tempAt(n.Pos, s.curfn, t)
|
d := tempAt(n.Pos, s.curfn, t)
|
||||||
|
|
@ -4477,10 +4504,17 @@ func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call runtime.deferprocStack with pointer to _defer record.
|
// Call runtime.deferprocStack with pointer to _defer record.
|
||||||
arg0 := s.constOffPtrSP(types.Types[TUINTPTR], Ctxt.FixedFrameSize())
|
|
||||||
s.store(types.Types[TUINTPTR], arg0, addr)
|
|
||||||
ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(Ctxt.FixedFrameSize())})
|
ACArgs = append(ACArgs, ssa.Param{Type: types.Types[TUINTPTR], Offset: int32(Ctxt.FixedFrameSize())})
|
||||||
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(deferprocStack, ACArgs, ACResults), s.mem())
|
aux := ssa.StaticAuxCall(deferprocStack, ACArgs, ACResults)
|
||||||
|
if testLateExpansion {
|
||||||
|
callArgs = append(callArgs, addr, s.mem())
|
||||||
|
call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
|
||||||
|
call.AddArgs(callArgs...)
|
||||||
|
} else {
|
||||||
|
arg0 := s.constOffPtrSP(types.Types[TUINTPTR], Ctxt.FixedFrameSize())
|
||||||
|
s.store(types.Types[TUINTPTR], arg0, addr)
|
||||||
|
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem())
|
||||||
|
}
|
||||||
if stksize < int64(Widthptr) {
|
if stksize < int64(Widthptr) {
|
||||||
// We need room for both the call to deferprocStack and the call to
|
// We need room for both the call to deferprocStack and the call to
|
||||||
// the deferred function.
|
// the deferred function.
|
||||||
|
|
@ -5039,15 +5073,22 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args .
|
||||||
s.prevCall = nil
|
s.prevCall = nil
|
||||||
// Write args to the stack
|
// Write args to the stack
|
||||||
off := Ctxt.FixedFrameSize()
|
off := Ctxt.FixedFrameSize()
|
||||||
|
testLateExpansion := ssa.LateCallExpansionEnabledWithin(s.f)
|
||||||
var ACArgs []ssa.Param
|
var ACArgs []ssa.Param
|
||||||
var ACResults []ssa.Param
|
var ACResults []ssa.Param
|
||||||
|
var callArgs []*ssa.Value
|
||||||
|
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
t := arg.Type
|
t := arg.Type
|
||||||
off = Rnd(off, t.Alignment())
|
off = Rnd(off, t.Alignment())
|
||||||
ptr := s.constOffPtrSP(t.PtrTo(), off)
|
|
||||||
size := t.Size()
|
size := t.Size()
|
||||||
ACArgs = append(ACArgs, ssa.Param{Type: t, Offset: int32(off)})
|
ACArgs = append(ACArgs, ssa.Param{Type: t, Offset: int32(off)})
|
||||||
s.store(t, ptr, arg)
|
if testLateExpansion {
|
||||||
|
callArgs = append(callArgs, arg)
|
||||||
|
} else {
|
||||||
|
ptr := s.constOffPtrSP(t.PtrTo(), off)
|
||||||
|
s.store(t, ptr, arg)
|
||||||
|
}
|
||||||
off += size
|
off += size
|
||||||
}
|
}
|
||||||
off = Rnd(off, int64(Widthreg))
|
off = Rnd(off, int64(Widthreg))
|
||||||
|
|
@ -5061,8 +5102,17 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args .
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issue call
|
// Issue call
|
||||||
call := s.newValue1A(ssa.OpStaticCall, types.TypeMem, ssa.StaticAuxCall(fn, ACArgs, ACResults), s.mem())
|
var call *ssa.Value
|
||||||
s.vars[&memVar] = call
|
aux := ssa.StaticAuxCall(fn, ACArgs, ACResults)
|
||||||
|
if testLateExpansion {
|
||||||
|
callArgs = append(callArgs, s.mem())
|
||||||
|
call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
|
||||||
|
call.AddArgs(callArgs...)
|
||||||
|
s.vars[&memVar] = s.newValue1I(ssa.OpSelectN, types.TypeMem, int64(len(ACResults)), call)
|
||||||
|
} else {
|
||||||
|
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem())
|
||||||
|
s.vars[&memVar] = call
|
||||||
|
}
|
||||||
|
|
||||||
if !returns {
|
if !returns {
|
||||||
// Finish block
|
// Finish block
|
||||||
|
|
@ -5078,11 +5128,24 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args .
|
||||||
|
|
||||||
// Load results
|
// Load results
|
||||||
res := make([]*ssa.Value, len(results))
|
res := make([]*ssa.Value, len(results))
|
||||||
for i, t := range results {
|
if testLateExpansion {
|
||||||
off = Rnd(off, t.Alignment())
|
for i, t := range results {
|
||||||
ptr := s.constOffPtrSP(types.NewPtr(t), off)
|
off = Rnd(off, t.Alignment())
|
||||||
res[i] = s.load(t, ptr)
|
if canSSAType(t) {
|
||||||
off += t.Size()
|
res[i] = s.newValue1I(ssa.OpSelectN, t, int64(i), call)
|
||||||
|
} else {
|
||||||
|
addr := s.newValue1I(ssa.OpSelectNAddr, types.NewPtr(t), int64(i), call)
|
||||||
|
res[i] = s.rawLoad(t, addr)
|
||||||
|
}
|
||||||
|
off += t.Size()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i, t := range results {
|
||||||
|
off = Rnd(off, t.Alignment())
|
||||||
|
ptr := s.constOffPtrSP(types.NewPtr(t), off)
|
||||||
|
res[i] = s.load(t, ptr)
|
||||||
|
off += t.Size()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
off = Rnd(off, int64(Widthptr))
|
off = Rnd(off, int64(Widthptr))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -825,7 +825,7 @@ func assignconvfn(n *Node, t *types.Type, context func() string) *Node {
|
||||||
|
|
||||||
// Convert ideal bool from comparison to plain bool
|
// Convert ideal bool from comparison to plain bool
|
||||||
// if the next step is non-bool (like interface{}).
|
// if the next step is non-bool (like interface{}).
|
||||||
if n.Type == types.Idealbool && !t.IsBoolean() {
|
if n.Type == types.UntypedBool && !t.IsBoolean() {
|
||||||
if n.Op == ONAME || n.Op == OLITERAL {
|
if n.Op == ONAME || n.Op == OLITERAL {
|
||||||
r := nod(OCONVNOP, n, nil)
|
r := nod(OCONVNOP, n, nil)
|
||||||
r.Type = types.Types[TBOOL]
|
r.Type = types.Types[TBOOL]
|
||||||
|
|
|
||||||
|
|
@ -440,7 +440,7 @@ func (c *exprClause) test(exprname *Node) *Node {
|
||||||
|
|
||||||
// Optimize "switch true { ...}" and "switch false { ... }".
|
// Optimize "switch true { ...}" and "switch false { ... }".
|
||||||
if Isconst(exprname, CTBOOL) && !c.lo.Type.IsInterface() {
|
if Isconst(exprname, CTBOOL) && !c.lo.Type.IsInterface() {
|
||||||
if exprname.Val().U.(bool) {
|
if exprname.Bool() {
|
||||||
return c.lo
|
return c.lo
|
||||||
} else {
|
} else {
|
||||||
return nodl(c.pos, ONOT, c.lo, nil)
|
return nodl(c.pos, ONOT, c.lo, nil)
|
||||||
|
|
|
||||||
|
|
@ -361,7 +361,7 @@ func typecheck1(n *Node, top int) (res *Node) {
|
||||||
ok |= ctxExpr
|
ok |= ctxExpr
|
||||||
|
|
||||||
if n.Type == nil && n.Val().Ctype() == CTSTR {
|
if n.Type == nil && n.Val().Ctype() == CTSTR {
|
||||||
n.Type = types.Idealstring
|
n.Type = types.UntypedString
|
||||||
}
|
}
|
||||||
|
|
||||||
case ONONAME:
|
case ONONAME:
|
||||||
|
|
@ -623,8 +623,8 @@ func typecheck1(n *Node, top int) (res *Node) {
|
||||||
// no defaultlit for left
|
// no defaultlit for left
|
||||||
// the outer context gives the type
|
// the outer context gives the type
|
||||||
n.Type = l.Type
|
n.Type = l.Type
|
||||||
if (l.Type == types.Idealfloat || l.Type == types.Idealcomplex) && r.Op == OLITERAL {
|
if (l.Type == types.UntypedFloat || l.Type == types.UntypedComplex) && r.Op == OLITERAL {
|
||||||
n.Type = types.Idealint
|
n.Type = types.UntypedInt
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
@ -777,7 +777,7 @@ func typecheck1(n *Node, top int) (res *Node) {
|
||||||
|
|
||||||
if iscmp[n.Op] {
|
if iscmp[n.Op] {
|
||||||
evconst(n)
|
evconst(n)
|
||||||
t = types.Idealbool
|
t = types.UntypedBool
|
||||||
if n.Op != OLITERAL {
|
if n.Op != OLITERAL {
|
||||||
l, r = defaultlit2(l, r, true)
|
l, r = defaultlit2(l, r, true)
|
||||||
n.Left = l
|
n.Left = l
|
||||||
|
|
@ -1458,7 +1458,7 @@ func typecheck1(n *Node, top int) (res *Node) {
|
||||||
// Determine result type.
|
// Determine result type.
|
||||||
switch t.Etype {
|
switch t.Etype {
|
||||||
case TIDEAL:
|
case TIDEAL:
|
||||||
n.Type = types.Idealfloat
|
n.Type = types.UntypedFloat
|
||||||
case TCOMPLEX64:
|
case TCOMPLEX64:
|
||||||
n.Type = types.Types[TFLOAT32]
|
n.Type = types.Types[TFLOAT32]
|
||||||
case TCOMPLEX128:
|
case TCOMPLEX128:
|
||||||
|
|
@ -1504,7 +1504,7 @@ func typecheck1(n *Node, top int) (res *Node) {
|
||||||
return n
|
return n
|
||||||
|
|
||||||
case TIDEAL:
|
case TIDEAL:
|
||||||
t = types.Idealcomplex
|
t = types.UntypedComplex
|
||||||
|
|
||||||
case TFLOAT32:
|
case TFLOAT32:
|
||||||
t = types.Types[TCOMPLEX64]
|
t = types.Types[TCOMPLEX64]
|
||||||
|
|
@ -2724,9 +2724,9 @@ func errorDetails(nl Nodes, tstruct *types.Type, isddd bool) string {
|
||||||
// e.g in error messages about wrong arguments to return.
|
// e.g in error messages about wrong arguments to return.
|
||||||
func sigrepr(t *types.Type, isddd bool) string {
|
func sigrepr(t *types.Type, isddd bool) string {
|
||||||
switch t {
|
switch t {
|
||||||
case types.Idealstring:
|
case types.UntypedString:
|
||||||
return "string"
|
return "string"
|
||||||
case types.Idealbool:
|
case types.UntypedBool:
|
||||||
return "bool"
|
return "bool"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -123,21 +123,21 @@ func lexinit() {
|
||||||
asNode(s2.Def).SetSubOp(s.op)
|
asNode(s2.Def).SetSubOp(s.op)
|
||||||
}
|
}
|
||||||
|
|
||||||
types.Idealstring = types.New(TSTRING)
|
types.UntypedString = types.New(TSTRING)
|
||||||
types.Idealbool = types.New(TBOOL)
|
types.UntypedBool = types.New(TBOOL)
|
||||||
types.Types[TANY] = types.New(TANY)
|
types.Types[TANY] = types.New(TANY)
|
||||||
|
|
||||||
s := builtinpkg.Lookup("true")
|
s := builtinpkg.Lookup("true")
|
||||||
s.Def = asTypesNode(nodbool(true))
|
s.Def = asTypesNode(nodbool(true))
|
||||||
asNode(s.Def).Sym = lookup("true")
|
asNode(s.Def).Sym = lookup("true")
|
||||||
asNode(s.Def).Name = new(Name)
|
asNode(s.Def).Name = new(Name)
|
||||||
asNode(s.Def).Type = types.Idealbool
|
asNode(s.Def).Type = types.UntypedBool
|
||||||
|
|
||||||
s = builtinpkg.Lookup("false")
|
s = builtinpkg.Lookup("false")
|
||||||
s.Def = asTypesNode(nodbool(false))
|
s.Def = asTypesNode(nodbool(false))
|
||||||
asNode(s.Def).Sym = lookup("false")
|
asNode(s.Def).Sym = lookup("false")
|
||||||
asNode(s.Def).Name = new(Name)
|
asNode(s.Def).Name = new(Name)
|
||||||
asNode(s.Def).Type = types.Idealbool
|
asNode(s.Def).Type = types.UntypedBool
|
||||||
|
|
||||||
s = lookup("_")
|
s = lookup("_")
|
||||||
s.Block = -100
|
s.Block = -100
|
||||||
|
|
@ -351,7 +351,7 @@ func typeinit() {
|
||||||
sizeofString = Rnd(sliceLenOffset+int64(Widthptr), int64(Widthptr))
|
sizeofString = Rnd(sliceLenOffset+int64(Widthptr), int64(Widthptr))
|
||||||
|
|
||||||
dowidth(types.Types[TSTRING])
|
dowidth(types.Types[TSTRING])
|
||||||
dowidth(types.Idealstring)
|
dowidth(types.UntypedString)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeErrorInterface() *types.Type {
|
func makeErrorInterface() *types.Type {
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,9 @@ func expandCalls(f *Func) {
|
||||||
hiOffset = 4
|
hiOffset = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
pairTypes := func(et types.EType) (tHi, tLo *types.Type) {
|
// intPairTypes returns the pair of 32-bit int types needed to encode a 64-bit integer type on a target
|
||||||
|
// that has no 64-bit integer registers.
|
||||||
|
intPairTypes := func(et types.EType) (tHi, tLo *types.Type) {
|
||||||
tHi = tUint32
|
tHi = tUint32
|
||||||
if et == types.TINT64 {
|
if et == types.TINT64 {
|
||||||
tHi = tInt32
|
tHi = tInt32
|
||||||
|
|
@ -147,8 +149,8 @@ func expandCalls(f *Func) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// storeArg converts stores of SSA-able aggregates into a series of stores of smaller types into
|
// storeArg converts stores of SSA-able aggregate arguments (passed to a call) into a series of stores of
|
||||||
// individual parameter slots.
|
// smaller types into individual parameter slots.
|
||||||
// TODO when registers really arrive, must also decompose anything split across two registers or registers and memory.
|
// TODO when registers really arrive, must also decompose anything split across two registers or registers and memory.
|
||||||
var storeArg func(pos src.XPos, b *Block, a *Value, t *types.Type, offset int64, mem *Value) *Value
|
var storeArg func(pos src.XPos, b *Block, a *Value, t *types.Type, offset int64, mem *Value) *Value
|
||||||
storeArg = func(pos src.XPos, b *Block, a *Value, t *types.Type, offset int64, mem *Value) *Value {
|
storeArg = func(pos src.XPos, b *Block, a *Value, t *types.Type, offset int64, mem *Value) *Value {
|
||||||
|
|
@ -165,7 +167,7 @@ func expandCalls(f *Func) {
|
||||||
return storeArg(pos, b, a.Args[0], t.Elem(), offset, mem)
|
return storeArg(pos, b, a.Args[0], t.Elem(), offset, mem)
|
||||||
|
|
||||||
case OpInt64Make:
|
case OpInt64Make:
|
||||||
tHi, tLo := pairTypes(t.Etype)
|
tHi, tLo := intPairTypes(t.Etype)
|
||||||
mem = storeArg(pos, b, a.Args[0], tHi, offset+hiOffset, mem)
|
mem = storeArg(pos, b, a.Args[0], tHi, offset+hiOffset, mem)
|
||||||
return storeArg(pos, b, a.Args[1], tLo, offset+lowOffset, mem)
|
return storeArg(pos, b, a.Args[1], tLo, offset+lowOffset, mem)
|
||||||
}
|
}
|
||||||
|
|
@ -207,7 +209,7 @@ func expandCalls(f *Func) {
|
||||||
if t.Width == regSize {
|
if t.Width == regSize {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
tHi, tLo := pairTypes(t.Etype)
|
tHi, tLo := intPairTypes(t.Etype)
|
||||||
sel := src.Block.NewValue1(pos, OpInt64Hi, tHi, src)
|
sel := src.Block.NewValue1(pos, OpInt64Hi, tHi, src)
|
||||||
mem = splitStore(dst, sel, mem, v, tHi, offset+hiOffset, firstStorePos)
|
mem = splitStore(dst, sel, mem, v, tHi, offset+hiOffset, firstStorePos)
|
||||||
firstStorePos = firstStorePos.WithNotStmt()
|
firstStorePos = firstStorePos.WithNotStmt()
|
||||||
|
|
@ -261,6 +263,9 @@ func expandCalls(f *Func) {
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rewriteArgs removes all the Args from a call and converts the call args into appropriate
|
||||||
|
// stores (or later, register movement). Extra args for interface and closure calls are ignored,
|
||||||
|
// but removed.
|
||||||
rewriteArgs := func(v *Value, firstArg int) *Value {
|
rewriteArgs := func(v *Value, firstArg int) *Value {
|
||||||
// Thread the stores on the memory arg
|
// Thread the stores on the memory arg
|
||||||
aux := v.Aux.(*AuxCall)
|
aux := v.Aux.(*AuxCall)
|
||||||
|
|
@ -283,7 +288,7 @@ func expandCalls(f *Func) {
|
||||||
// TODO this will be more complicated with registers in the picture.
|
// TODO this will be more complicated with registers in the picture.
|
||||||
src := a.Args[0]
|
src := a.Args[0]
|
||||||
dst := f.ConstOffPtrSP(src.Type, aux.OffsetOfArg(auxI), sp)
|
dst := f.ConstOffPtrSP(src.Type, aux.OffsetOfArg(auxI), sp)
|
||||||
if a.Uses == 1 {
|
if a.Uses == 1 && a.Block == v.Block {
|
||||||
a.reset(OpMove)
|
a.reset(OpMove)
|
||||||
a.Pos = pos
|
a.Pos = pos
|
||||||
a.Type = types.TypeMem
|
a.Type = types.TypeMem
|
||||||
|
|
@ -292,7 +297,7 @@ func expandCalls(f *Func) {
|
||||||
a.SetArgs3(dst, src, mem)
|
a.SetArgs3(dst, src, mem)
|
||||||
mem = a
|
mem = a
|
||||||
} else {
|
} else {
|
||||||
mem = a.Block.NewValue3A(pos, OpMove, types.TypeMem, aux.TypeOfArg(auxI), dst, src, mem)
|
mem = v.Block.NewValue3A(pos, OpMove, types.TypeMem, aux.TypeOfArg(auxI), dst, src, mem)
|
||||||
mem.AuxInt = aux.SizeOfArg(auxI)
|
mem.AuxInt = aux.SizeOfArg(auxI)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -672,7 +672,7 @@ func (f *Func) Idom() []*Block {
|
||||||
return f.cachedIdom
|
return f.cachedIdom
|
||||||
}
|
}
|
||||||
|
|
||||||
// sdom returns a sparse tree representing the dominator relationships
|
// Sdom returns a sparse tree representing the dominator relationships
|
||||||
// among the blocks of f.
|
// among the blocks of f.
|
||||||
func (f *Func) Sdom() SparseTree {
|
func (f *Func) Sdom() SparseTree {
|
||||||
if f.cachedSdom == nil {
|
if f.cachedSdom == nil {
|
||||||
|
|
|
||||||
|
|
@ -1274,8 +1274,8 @@
|
||||||
(CMPQconst (ANDQconst _ [m]) [n]) && 0 <= m && m < n => (FlagLT_ULT)
|
(CMPQconst (ANDQconst _ [m]) [n]) && 0 <= m && m < n => (FlagLT_ULT)
|
||||||
(CMPQconst (ANDLconst _ [m]) [n]) && 0 <= m && m < n => (FlagLT_ULT)
|
(CMPQconst (ANDLconst _ [m]) [n]) && 0 <= m && m < n => (FlagLT_ULT)
|
||||||
(CMPLconst (ANDLconst _ [m]) [n]) && 0 <= m && m < n => (FlagLT_ULT)
|
(CMPLconst (ANDLconst _ [m]) [n]) && 0 <= m && m < n => (FlagLT_ULT)
|
||||||
(CMPWconst (ANDLconst _ [m]) [n]) && 0 <= m && int16(m) < n => (FlagLT_ULT)
|
(CMPWconst (ANDLconst _ [m]) [n]) && 0 <= int16(m) && int16(m) < n => (FlagLT_ULT)
|
||||||
(CMPBconst (ANDLconst _ [m]) [n]) && 0 <= m && int8(m) < n => (FlagLT_ULT)
|
(CMPBconst (ANDLconst _ [m]) [n]) && 0 <= int8(m) && int8(m) < n => (FlagLT_ULT)
|
||||||
|
|
||||||
// TESTQ c c sets flags like CMPQ c 0.
|
// TESTQ c c sets flags like CMPQ c 0.
|
||||||
(TESTQconst [c] (MOVQconst [d])) && int64(c) == d && c == 0 => (FlagEQ)
|
(TESTQconst [c] (MOVQconst [d])) && int64(c) == d && c == 0 => (FlagEQ)
|
||||||
|
|
|
||||||
|
|
@ -541,10 +541,10 @@ var genericOps = []opData{
|
||||||
{name: "SelectN", argLength: 1, aux: "Int64"}, // arg0=tuple, auxint=field index. Returns the auxint'th member.
|
{name: "SelectN", argLength: 1, aux: "Int64"}, // arg0=tuple, auxint=field index. Returns the auxint'th member.
|
||||||
{name: "SelectNAddr", argLength: 1, aux: "Int64"}, // arg0=tuple, auxint=field index. Returns the address of auxint'th member. Used for un-SSA-able result types.
|
{name: "SelectNAddr", argLength: 1, aux: "Int64"}, // arg0=tuple, auxint=field index. Returns the address of auxint'th member. Used for un-SSA-able result types.
|
||||||
|
|
||||||
// Atomic operations used for semantically inlining runtime/internal/atomic.
|
// Atomic operations used for semantically inlining sync/atomic and
|
||||||
// Atomic loads return a new memory so that the loads are properly ordered
|
// runtime/internal/atomic. Atomic loads return a new memory so that
|
||||||
// with respect to other loads and stores.
|
// the loads are properly ordered with respect to other loads and
|
||||||
// TODO: use for sync/atomic at some point.
|
// stores.
|
||||||
{name: "AtomicLoad8", argLength: 2, typ: "(UInt8,Mem)"}, // Load from arg0. arg1=memory. Returns loaded value and new memory.
|
{name: "AtomicLoad8", argLength: 2, typ: "(UInt8,Mem)"}, // Load from arg0. arg1=memory. Returns loaded value and new memory.
|
||||||
{name: "AtomicLoad32", argLength: 2, typ: "(UInt32,Mem)"}, // Load from arg0. arg1=memory. Returns loaded value and new memory.
|
{name: "AtomicLoad32", argLength: 2, typ: "(UInt32,Mem)"}, // Load from arg0. arg1=memory. Returns loaded value and new memory.
|
||||||
{name: "AtomicLoad64", argLength: 2, typ: "(UInt64,Mem)"}, // Load from arg0. arg1=memory. Returns loaded value and new memory.
|
{name: "AtomicLoad64", argLength: 2, typ: "(UInt64,Mem)"}, // Load from arg0. arg1=memory. Returns loaded value and new memory.
|
||||||
|
|
|
||||||
|
|
@ -6886,7 +6886,7 @@ func rewriteValueAMD64_OpAMD64CMPBconst(v *Value) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// match: (CMPBconst (ANDLconst _ [m]) [n])
|
// match: (CMPBconst (ANDLconst _ [m]) [n])
|
||||||
// cond: 0 <= m && int8(m) < n
|
// cond: 0 <= int8(m) && int8(m) < n
|
||||||
// result: (FlagLT_ULT)
|
// result: (FlagLT_ULT)
|
||||||
for {
|
for {
|
||||||
n := auxIntToInt8(v.AuxInt)
|
n := auxIntToInt8(v.AuxInt)
|
||||||
|
|
@ -6894,7 +6894,7 @@ func rewriteValueAMD64_OpAMD64CMPBconst(v *Value) bool {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
m := auxIntToInt32(v_0.AuxInt)
|
m := auxIntToInt32(v_0.AuxInt)
|
||||||
if !(0 <= m && int8(m) < n) {
|
if !(0 <= int8(m) && int8(m) < n) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
v.reset(OpAMD64FlagLT_ULT)
|
v.reset(OpAMD64FlagLT_ULT)
|
||||||
|
|
@ -8243,7 +8243,7 @@ func rewriteValueAMD64_OpAMD64CMPWconst(v *Value) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// match: (CMPWconst (ANDLconst _ [m]) [n])
|
// match: (CMPWconst (ANDLconst _ [m]) [n])
|
||||||
// cond: 0 <= m && int16(m) < n
|
// cond: 0 <= int16(m) && int16(m) < n
|
||||||
// result: (FlagLT_ULT)
|
// result: (FlagLT_ULT)
|
||||||
for {
|
for {
|
||||||
n := auxIntToInt16(v.AuxInt)
|
n := auxIntToInt16(v.AuxInt)
|
||||||
|
|
@ -8251,7 +8251,7 @@ func rewriteValueAMD64_OpAMD64CMPWconst(v *Value) bool {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
m := auxIntToInt32(v_0.AuxInt)
|
m := auxIntToInt32(v_0.AuxInt)
|
||||||
if !(0 <= m && int16(m) < n) {
|
if !(0 <= int16(m) && int16(m) < n) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
v.reset(OpAMD64FlagLT_ULT)
|
v.reset(OpAMD64FlagLT_ULT)
|
||||||
|
|
|
||||||
|
|
@ -287,6 +287,7 @@ func tokstring(tok token) string {
|
||||||
|
|
||||||
// Convenience methods using the current token position.
|
// Convenience methods using the current token position.
|
||||||
func (p *parser) pos() Pos { return p.posAt(p.line, p.col) }
|
func (p *parser) pos() Pos { return p.posAt(p.line, p.col) }
|
||||||
|
func (p *parser) error(msg string) { p.errorAt(p.pos(), msg) }
|
||||||
func (p *parser) syntaxError(msg string) { p.syntaxErrorAt(p.pos(), msg) }
|
func (p *parser) syntaxError(msg string) { p.syntaxErrorAt(p.pos(), msg) }
|
||||||
|
|
||||||
// The stopset contains keywords that start a statement.
|
// The stopset contains keywords that start a statement.
|
||||||
|
|
@ -997,17 +998,20 @@ loop:
|
||||||
// x[i:j...
|
// x[i:j...
|
||||||
t.Index[1] = p.expr()
|
t.Index[1] = p.expr()
|
||||||
}
|
}
|
||||||
if p.got(_Colon) {
|
if p.tok == _Colon {
|
||||||
t.Full = true
|
t.Full = true
|
||||||
// x[i:j:...]
|
// x[i:j:...]
|
||||||
if t.Index[1] == nil {
|
if t.Index[1] == nil {
|
||||||
p.error("middle index required in 3-index slice")
|
p.error("middle index required in 3-index slice")
|
||||||
|
t.Index[1] = p.badExpr()
|
||||||
}
|
}
|
||||||
|
p.next()
|
||||||
if p.tok != _Rbrack {
|
if p.tok != _Rbrack {
|
||||||
// x[i:j:k...
|
// x[i:j:k...
|
||||||
t.Index[2] = p.expr()
|
t.Index[2] = p.expr()
|
||||||
} else {
|
} else {
|
||||||
p.error("final index required in 3-index slice")
|
p.error("final index required in 3-index slice")
|
||||||
|
t.Index[2] = p.badExpr()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.want(_Rbrack)
|
p.want(_Rbrack)
|
||||||
|
|
@ -1836,6 +1840,7 @@ func (p *parser) header(keyword token) (init SimpleStmt, cond Expr, post SimpleS
|
||||||
if p.tok == _Lbrace {
|
if p.tok == _Lbrace {
|
||||||
if keyword == _If {
|
if keyword == _If {
|
||||||
p.syntaxError("missing condition in if statement")
|
p.syntaxError("missing condition in if statement")
|
||||||
|
cond = p.badExpr()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -1907,6 +1912,9 @@ done:
|
||||||
} else {
|
} else {
|
||||||
p.syntaxErrorAt(semi.pos, "missing condition in if statement")
|
p.syntaxErrorAt(semi.pos, "missing condition in if statement")
|
||||||
}
|
}
|
||||||
|
b := new(BadExpr)
|
||||||
|
b.pos = semi.pos
|
||||||
|
cond = b
|
||||||
}
|
}
|
||||||
case *ExprStmt:
|
case *ExprStmt:
|
||||||
cond = s.X
|
cond = s.X
|
||||||
|
|
|
||||||
|
|
@ -105,14 +105,14 @@ var (
|
||||||
Errortype *Type
|
Errortype *Type
|
||||||
|
|
||||||
// Types to represent untyped string and boolean constants.
|
// Types to represent untyped string and boolean constants.
|
||||||
Idealstring *Type
|
UntypedString *Type
|
||||||
Idealbool *Type
|
UntypedBool *Type
|
||||||
|
|
||||||
// Types to represent untyped numeric constants.
|
// Types to represent untyped numeric constants.
|
||||||
Idealint = New(TIDEAL)
|
UntypedInt = New(TIDEAL)
|
||||||
Idealrune = New(TIDEAL)
|
UntypedRune = New(TIDEAL)
|
||||||
Idealfloat = New(TIDEAL)
|
UntypedFloat = New(TIDEAL)
|
||||||
Idealcomplex = New(TIDEAL)
|
UntypedComplex = New(TIDEAL)
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Type represents a Go type.
|
// A Type represents a Go type.
|
||||||
|
|
@ -1436,7 +1436,7 @@ func (t *Type) IsUntyped() bool {
|
||||||
if t == nil {
|
if t == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if t == Idealstring || t == Idealbool {
|
if t == UntypedString || t == UntypedBool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
switch t.Etype {
|
switch t.Etype {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,9 @@ package x86
|
||||||
import (
|
import (
|
||||||
"cmd/compile/internal/gc"
|
"cmd/compile/internal/gc"
|
||||||
"cmd/internal/obj/x86"
|
"cmd/internal/obj/x86"
|
||||||
|
"cmd/internal/objabi"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Init(arch *gc.Arch) {
|
func Init(arch *gc.Arch) {
|
||||||
|
|
@ -15,6 +18,18 @@ func Init(arch *gc.Arch) {
|
||||||
arch.SSAGenValue = ssaGenValue
|
arch.SSAGenValue = ssaGenValue
|
||||||
arch.SSAGenBlock = ssaGenBlock
|
arch.SSAGenBlock = ssaGenBlock
|
||||||
arch.MAXWIDTH = (1 << 32) - 1
|
arch.MAXWIDTH = (1 << 32) - 1
|
||||||
|
switch v := objabi.GO386; v {
|
||||||
|
case "sse2":
|
||||||
|
case "softfloat":
|
||||||
|
arch.SoftFloat = true
|
||||||
|
case "387":
|
||||||
|
fmt.Fprintf(os.Stderr, "unsupported setting GO386=387. Consider using GO386=softfloat instead.\n")
|
||||||
|
gc.Exit(1)
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(os.Stderr, "unsupported setting GO386=%s\n", v)
|
||||||
|
gc.Exit(1)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
arch.ZeroRange = zerorange
|
arch.ZeroRange = zerorange
|
||||||
arch.Ginsnop = ginsnop
|
arch.Ginsnop = ginsnop
|
||||||
|
|
|
||||||
11
src/cmd/dist/build.go
vendored
11
src/cmd/dist/build.go
vendored
|
|
@ -30,6 +30,7 @@ var (
|
||||||
gohostos string
|
gohostos string
|
||||||
goos string
|
goos string
|
||||||
goarm string
|
goarm string
|
||||||
|
go386 string
|
||||||
gomips string
|
gomips string
|
||||||
gomips64 string
|
gomips64 string
|
||||||
goppc64 string
|
goppc64 string
|
||||||
|
|
@ -141,6 +142,12 @@ func xinit() {
|
||||||
}
|
}
|
||||||
goarm = b
|
goarm = b
|
||||||
|
|
||||||
|
b = os.Getenv("GO386")
|
||||||
|
if b == "" {
|
||||||
|
b = "sse2"
|
||||||
|
}
|
||||||
|
go386 = b
|
||||||
|
|
||||||
b = os.Getenv("GOMIPS")
|
b = os.Getenv("GOMIPS")
|
||||||
if b == "" {
|
if b == "" {
|
||||||
b = "hardfloat"
|
b = "hardfloat"
|
||||||
|
|
@ -212,6 +219,7 @@ func xinit() {
|
||||||
defaultldso = os.Getenv("GO_LDSO")
|
defaultldso = os.Getenv("GO_LDSO")
|
||||||
|
|
||||||
// For tools being invoked but also for os.ExpandEnv.
|
// For tools being invoked but also for os.ExpandEnv.
|
||||||
|
os.Setenv("GO386", go386)
|
||||||
os.Setenv("GOARCH", goarch)
|
os.Setenv("GOARCH", goarch)
|
||||||
os.Setenv("GOARM", goarm)
|
os.Setenv("GOARM", goarm)
|
||||||
os.Setenv("GOHOSTARCH", gohostarch)
|
os.Setenv("GOHOSTARCH", gohostarch)
|
||||||
|
|
@ -1153,6 +1161,9 @@ func cmdenv() {
|
||||||
if goarch == "arm" {
|
if goarch == "arm" {
|
||||||
xprintf(format, "GOARM", goarm)
|
xprintf(format, "GOARM", goarm)
|
||||||
}
|
}
|
||||||
|
if goarch == "386" {
|
||||||
|
xprintf(format, "GO386", go386)
|
||||||
|
}
|
||||||
if goarch == "mips" || goarch == "mipsle" {
|
if goarch == "mips" || goarch == "mipsle" {
|
||||||
xprintf(format, "GOMIPS", gomips)
|
xprintf(format, "GOMIPS", gomips)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
src/cmd/dist/buildruntime.go
vendored
2
src/cmd/dist/buildruntime.go
vendored
|
|
@ -41,6 +41,7 @@ func mkzversion(dir, file string) {
|
||||||
// package objabi
|
// package objabi
|
||||||
//
|
//
|
||||||
// const defaultGOROOT = <goroot>
|
// const defaultGOROOT = <goroot>
|
||||||
|
// const defaultGO386 = <go386>
|
||||||
// const defaultGOARM = <goarm>
|
// const defaultGOARM = <goarm>
|
||||||
// const defaultGOMIPS = <gomips>
|
// const defaultGOMIPS = <gomips>
|
||||||
// const defaultGOMIPS64 = <gomips64>
|
// const defaultGOMIPS64 = <gomips64>
|
||||||
|
|
@ -69,6 +70,7 @@ func mkzbootstrap(file string) {
|
||||||
fmt.Fprintln(&buf)
|
fmt.Fprintln(&buf)
|
||||||
fmt.Fprintf(&buf, "import \"runtime\"\n")
|
fmt.Fprintf(&buf, "import \"runtime\"\n")
|
||||||
fmt.Fprintln(&buf)
|
fmt.Fprintln(&buf)
|
||||||
|
fmt.Fprintf(&buf, "const defaultGO386 = `%s`\n", go386)
|
||||||
fmt.Fprintf(&buf, "const defaultGOARM = `%s`\n", goarm)
|
fmt.Fprintf(&buf, "const defaultGOARM = `%s`\n", goarm)
|
||||||
fmt.Fprintf(&buf, "const defaultGOMIPS = `%s`\n", gomips)
|
fmt.Fprintf(&buf, "const defaultGOMIPS = `%s`\n", gomips)
|
||||||
fmt.Fprintf(&buf, "const defaultGOMIPS64 = `%s`\n", gomips64)
|
fmt.Fprintf(&buf, "const defaultGOMIPS64 = `%s`\n", gomips64)
|
||||||
|
|
|
||||||
8
src/cmd/dist/test.go
vendored
8
src/cmd/dist/test.go
vendored
|
|
@ -903,7 +903,7 @@ func (t *tester) addCmd(dt *distTest, dir string, cmdline ...interface{}) *exec.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tester) iOS() bool {
|
func (t *tester) iOS() bool {
|
||||||
return (goos == "darwin" || goos == "ios") && goarch == "arm64"
|
return goos == "ios"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tester) out(v string) {
|
func (t *tester) out(v string) {
|
||||||
|
|
@ -992,7 +992,7 @@ func (t *tester) supportedBuildmode(mode string) bool {
|
||||||
case "c-shared":
|
case "c-shared":
|
||||||
switch pair {
|
switch pair {
|
||||||
case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x",
|
case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x",
|
||||||
"darwin-amd64",
|
"darwin-amd64", "darwin-arm64",
|
||||||
"freebsd-amd64",
|
"freebsd-amd64",
|
||||||
"android-arm", "android-arm64", "android-386",
|
"android-arm", "android-arm64", "android-386",
|
||||||
"windows-amd64", "windows-386":
|
"windows-amd64", "windows-386":
|
||||||
|
|
@ -1011,7 +1011,7 @@ func (t *tester) supportedBuildmode(mode string) bool {
|
||||||
switch pair {
|
switch pair {
|
||||||
case "linux-386", "linux-amd64", "linux-arm", "linux-s390x", "linux-ppc64le":
|
case "linux-386", "linux-amd64", "linux-arm", "linux-s390x", "linux-ppc64le":
|
||||||
return true
|
return true
|
||||||
case "darwin-amd64":
|
case "darwin-amd64", "darwin-arm64":
|
||||||
return true
|
return true
|
||||||
case "freebsd-amd64":
|
case "freebsd-amd64":
|
||||||
return true
|
return true
|
||||||
|
|
@ -1023,7 +1023,7 @@ func (t *tester) supportedBuildmode(mode string) bool {
|
||||||
"linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x",
|
"linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x",
|
||||||
"android-amd64", "android-arm", "android-arm64", "android-386":
|
"android-amd64", "android-arm", "android-arm64", "android-386":
|
||||||
return true
|
return true
|
||||||
case "darwin-amd64":
|
case "darwin-amd64", "darwin-arm64":
|
||||||
return true
|
return true
|
||||||
case "windows-amd64", "windows-386", "windows-arm":
|
case "windows-amd64", "windows-386", "windows-arm":
|
||||||
return true
|
return true
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ func TestMain(m *testing.M) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func maybeSkip(t *testing.T) {
|
func maybeSkip(t *testing.T) {
|
||||||
if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" {
|
if runtime.GOOS == "ios" {
|
||||||
t.Skip("iOS does not have a full file tree")
|
t.Skip("iOS does not have a full file tree")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,11 @@ var gotypesFix = fix{
|
||||||
}
|
}
|
||||||
|
|
||||||
func gotypes(f *ast.File) bool {
|
func gotypes(f *ast.File) bool {
|
||||||
truth := fixGoTypes(f)
|
fixed := fixGoTypes(f)
|
||||||
if fixGoExact(f) {
|
if fixGoExact(f) {
|
||||||
truth = true
|
fixed = true
|
||||||
}
|
}
|
||||||
return truth
|
return fixed
|
||||||
}
|
}
|
||||||
|
|
||||||
func fixGoTypes(f *ast.File) bool {
|
func fixGoTypes(f *ast.File) bool {
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,21 @@ func processFile(filename string, useStdin bool) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure file is in canonical format.
|
||||||
|
// This "fmt" pseudo-fix cannot be disabled.
|
||||||
|
newSrc, err := gofmtFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !bytes.Equal(newSrc, src) {
|
||||||
|
newFile, err := parser.ParseFile(fset, filename, newSrc, parserMode)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
file = newFile
|
||||||
|
fmt.Fprintf(&fixlog, " fmt")
|
||||||
|
}
|
||||||
|
|
||||||
// Apply all fixes to file.
|
// Apply all fixes to file.
|
||||||
newFile := file
|
newFile := file
|
||||||
fixed := false
|
fixed := false
|
||||||
|
|
@ -180,7 +195,7 @@ func processFile(filename string, useStdin bool) error {
|
||||||
// output of the printer run on a standard AST generated by the parser,
|
// output of the printer run on a standard AST generated by the parser,
|
||||||
// but the source we generated inside the loop above is the
|
// but the source we generated inside the loop above is the
|
||||||
// output of the printer run on a mangled AST generated by a fixer.
|
// output of the printer run on a mangled AST generated by a fixer.
|
||||||
newSrc, err := gofmtFile(newFile)
|
newSrc, err = gofmtFile(newFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ func typecheck(cfg *TypeConfig, f *ast.File) (typeof map[interface{}]string, ass
|
||||||
return nil
|
return nil
|
||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("warning: no cgo types: %s\n", err)
|
fmt.Fprintf(os.Stderr, "go fix: warning: no cgo types: %s\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ module cmd
|
||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3
|
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200414190113-039b1ae3a340 // indirect
|
github.com/ianlancetaylor/demangle v0.0.0-20200414190113-039b1ae3a340 // indirect
|
||||||
golang.org/x/arch v0.0.0-20200826200359-b19915210f00
|
golang.org/x/arch v0.0.0-20200826200359-b19915210f00
|
||||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
|
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3 h1:SRgJV+IoxM5MKyFdlSUeNy6/ycRUF2yBAKdAQswoHUk=
|
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99 h1:Ak8CrdlwwXwAZxzS66vgPt4U8yUZX7JwLvVR58FN5jM=
|
||||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200414190113-039b1ae3a340 h1:S1+yTUaFPXuDZnPDbO+TrDFIjPzQraYH8/CwSlu9Fac=
|
github.com/ianlancetaylor/demangle v0.0.0-20200414190113-039b1ae3a340 h1:S1+yTUaFPXuDZnPDbO+TrDFIjPzQraYH8/CwSlu9Fac=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200414190113-039b1ae3a340/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20200414190113-039b1ae3a340/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
|
|
||||||
|
|
@ -1852,6 +1852,9 @@
|
||||||
// GOARM
|
// GOARM
|
||||||
// For GOARCH=arm, the ARM architecture for which to compile.
|
// For GOARCH=arm, the ARM architecture for which to compile.
|
||||||
// Valid values are 5, 6, 7.
|
// Valid values are 5, 6, 7.
|
||||||
|
// GO386
|
||||||
|
// For GOARCH=386, how to implement floating point instructions.
|
||||||
|
// Valid values are sse2 (default), softfloat.
|
||||||
// GOMIPS
|
// GOMIPS
|
||||||
// For GOARCH=mips{,le}, whether to use floating point instructions.
|
// For GOARCH=mips{,le}, whether to use floating point instructions.
|
||||||
// Valid values are hardfloat (default), softfloat.
|
// Valid values are hardfloat (default), softfloat.
|
||||||
|
|
|
||||||
|
|
@ -58,11 +58,10 @@ func init() {
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "android", "js":
|
case "android", "js":
|
||||||
canRun = false
|
canRun = false
|
||||||
case "darwin", "ios":
|
case "darwin":
|
||||||
switch runtime.GOARCH {
|
// nothing to do
|
||||||
case "arm64":
|
case "ios":
|
||||||
canRun = false
|
canRun = false
|
||||||
}
|
|
||||||
case "linux":
|
case "linux":
|
||||||
switch runtime.GOARCH {
|
switch runtime.GOARCH {
|
||||||
case "arm":
|
case "arm":
|
||||||
|
|
|
||||||
|
|
@ -256,6 +256,7 @@ var (
|
||||||
|
|
||||||
// Used in envcmd.MkEnv and build ID computations.
|
// Used in envcmd.MkEnv and build ID computations.
|
||||||
GOARM = envOr("GOARM", fmt.Sprint(objabi.GOARM))
|
GOARM = envOr("GOARM", fmt.Sprint(objabi.GOARM))
|
||||||
|
GO386 = envOr("GO386", objabi.GO386)
|
||||||
GOMIPS = envOr("GOMIPS", objabi.GOMIPS)
|
GOMIPS = envOr("GOMIPS", objabi.GOMIPS)
|
||||||
GOMIPS64 = envOr("GOMIPS64", objabi.GOMIPS64)
|
GOMIPS64 = envOr("GOMIPS64", objabi.GOMIPS64)
|
||||||
GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", objabi.GOPPC64))
|
GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", objabi.GOPPC64))
|
||||||
|
|
@ -279,6 +280,8 @@ func GetArchEnv() (key, val string) {
|
||||||
switch Goarch {
|
switch Goarch {
|
||||||
case "arm":
|
case "arm":
|
||||||
return "GOARM", GOARM
|
return "GOARM", GOARM
|
||||||
|
case "386":
|
||||||
|
return "GO386", GO386
|
||||||
case "mips", "mipsle":
|
case "mips", "mipsle":
|
||||||
return "GOMIPS", GOMIPS
|
return "GOMIPS", GOMIPS
|
||||||
case "mips64", "mips64le":
|
case "mips64", "mips64le":
|
||||||
|
|
|
||||||
|
|
@ -203,10 +203,19 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do we need to call ExtraEnvVarsCostly, which is a bit expensive?
|
// Do we need to call ExtraEnvVarsCostly, which is a bit expensive?
|
||||||
// Only if we're listing all environment variables ("go env")
|
needCostly := false
|
||||||
// or the variables being requested are in the extra list.
|
if *envU || *envW {
|
||||||
needCostly := true
|
// We're overwriting or removing default settings,
|
||||||
if len(args) > 0 {
|
// so it doesn't really matter what the existing settings are.
|
||||||
|
//
|
||||||
|
// Moreover, we haven't validated the new settings yet, so it is
|
||||||
|
// important that we NOT perform any actions based on them,
|
||||||
|
// such as initializing the builder to compute other variables.
|
||||||
|
} else if len(args) == 0 {
|
||||||
|
// We're listing all environment variables ("go env"),
|
||||||
|
// including the expensive ones.
|
||||||
|
needCostly = true
|
||||||
|
} else {
|
||||||
needCostly = false
|
needCostly = false
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
switch argKey(arg) {
|
switch argKey(arg) {
|
||||||
|
|
@ -269,6 +278,13 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gotmp, okGOTMP := add["GOTMPDIR"]
|
||||||
|
if okGOTMP {
|
||||||
|
if !filepath.IsAbs(gotmp) && gotmp != "" {
|
||||||
|
base.Fatalf("go env -w: GOTMPDIR must be an absolute path")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateEnvFile(add, nil)
|
updateEnvFile(add, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -581,6 +581,9 @@ Architecture-specific environment variables:
|
||||||
GOARM
|
GOARM
|
||||||
For GOARCH=arm, the ARM architecture for which to compile.
|
For GOARCH=arm, the ARM architecture for which to compile.
|
||||||
Valid values are 5, 6, 7.
|
Valid values are 5, 6, 7.
|
||||||
|
GO386
|
||||||
|
For GOARCH=386, how to implement floating point instructions.
|
||||||
|
Valid values are sse2 (default), softfloat.
|
||||||
GOMIPS
|
GOMIPS
|
||||||
For GOARCH=mips{,le}, whether to use floating point instructions.
|
For GOARCH=mips{,le}, whether to use floating point instructions.
|
||||||
Valid values are hardfloat (default), softfloat.
|
Valid values are hardfloat (default), softfloat.
|
||||||
|
|
|
||||||
|
|
@ -75,19 +75,20 @@ type PackagePublic struct {
|
||||||
// Source files
|
// Source files
|
||||||
// If you add to this list you MUST add to p.AllFiles (below) too.
|
// If you add to this list you MUST add to p.AllFiles (below) too.
|
||||||
// Otherwise file name security lists will not apply to any new additions.
|
// Otherwise file name security lists will not apply to any new additions.
|
||||||
GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
|
GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
|
||||||
CgoFiles []string `json:",omitempty"` // .go source files that import "C"
|
CgoFiles []string `json:",omitempty"` // .go source files that import "C"
|
||||||
CompiledGoFiles []string `json:",omitempty"` // .go output from running cgo on CgoFiles
|
CompiledGoFiles []string `json:",omitempty"` // .go output from running cgo on CgoFiles
|
||||||
IgnoredGoFiles []string `json:",omitempty"` // .go source files ignored due to build constraints
|
IgnoredGoFiles []string `json:",omitempty"` // .go source files ignored due to build constraints
|
||||||
CFiles []string `json:",omitempty"` // .c source files
|
IgnoredOtherFiles []string `json:",omitempty"` // non-.go source files ignored due to build constraints
|
||||||
CXXFiles []string `json:",omitempty"` // .cc, .cpp and .cxx source files
|
CFiles []string `json:",omitempty"` // .c source files
|
||||||
MFiles []string `json:",omitempty"` // .m source files
|
CXXFiles []string `json:",omitempty"` // .cc, .cpp and .cxx source files
|
||||||
HFiles []string `json:",omitempty"` // .h, .hh, .hpp and .hxx source files
|
MFiles []string `json:",omitempty"` // .m source files
|
||||||
FFiles []string `json:",omitempty"` // .f, .F, .for and .f90 Fortran source files
|
HFiles []string `json:",omitempty"` // .h, .hh, .hpp and .hxx source files
|
||||||
SFiles []string `json:",omitempty"` // .s source files
|
FFiles []string `json:",omitempty"` // .f, .F, .for and .f90 Fortran source files
|
||||||
SwigFiles []string `json:",omitempty"` // .swig files
|
SFiles []string `json:",omitempty"` // .s source files
|
||||||
SwigCXXFiles []string `json:",omitempty"` // .swigcxx files
|
SwigFiles []string `json:",omitempty"` // .swig files
|
||||||
SysoFiles []string `json:",omitempty"` // .syso system object files added to package
|
SwigCXXFiles []string `json:",omitempty"` // .swigcxx files
|
||||||
|
SysoFiles []string `json:",omitempty"` // .syso system object files added to package
|
||||||
|
|
||||||
// Cgo directives
|
// Cgo directives
|
||||||
CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler
|
CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler
|
||||||
|
|
@ -127,6 +128,7 @@ func (p *Package) AllFiles() []string {
|
||||||
p.CgoFiles,
|
p.CgoFiles,
|
||||||
// no p.CompiledGoFiles, because they are from GoFiles or generated by us
|
// no p.CompiledGoFiles, because they are from GoFiles or generated by us
|
||||||
p.IgnoredGoFiles,
|
p.IgnoredGoFiles,
|
||||||
|
p.IgnoredOtherFiles,
|
||||||
p.CFiles,
|
p.CFiles,
|
||||||
p.CXXFiles,
|
p.CXXFiles,
|
||||||
p.MFiles,
|
p.MFiles,
|
||||||
|
|
@ -185,7 +187,7 @@ type NoGoError struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *NoGoError) Error() string {
|
func (e *NoGoError) Error() string {
|
||||||
if len(e.Package.constraintIgnoredGoFiles()) > 0 {
|
if len(e.Package.IgnoredGoFiles) > 0 {
|
||||||
// Go files exist, but they were ignored due to build constraints.
|
// Go files exist, but they were ignored due to build constraints.
|
||||||
return "build constraints exclude all Go files in " + e.Package.Dir
|
return "build constraints exclude all Go files in " + e.Package.Dir
|
||||||
}
|
}
|
||||||
|
|
@ -330,6 +332,7 @@ func (p *Package) copyBuild(pp *build.Package) {
|
||||||
p.GoFiles = pp.GoFiles
|
p.GoFiles = pp.GoFiles
|
||||||
p.CgoFiles = pp.CgoFiles
|
p.CgoFiles = pp.CgoFiles
|
||||||
p.IgnoredGoFiles = pp.IgnoredGoFiles
|
p.IgnoredGoFiles = pp.IgnoredGoFiles
|
||||||
|
p.IgnoredOtherFiles = pp.IgnoredOtherFiles
|
||||||
p.CFiles = pp.CFiles
|
p.CFiles = pp.CFiles
|
||||||
p.CXXFiles = pp.CXXFiles
|
p.CXXFiles = pp.CXXFiles
|
||||||
p.MFiles = pp.MFiles
|
p.MFiles = pp.MFiles
|
||||||
|
|
@ -2009,22 +2012,7 @@ func (p *Package) InternalXGoFiles() []string {
|
||||||
// using absolute paths. "Possibly relevant" means that files are not excluded
|
// using absolute paths. "Possibly relevant" means that files are not excluded
|
||||||
// due to build tags, but files with names beginning with . or _ are still excluded.
|
// due to build tags, but files with names beginning with . or _ are still excluded.
|
||||||
func (p *Package) InternalAllGoFiles() []string {
|
func (p *Package) InternalAllGoFiles() []string {
|
||||||
return p.mkAbs(str.StringList(p.constraintIgnoredGoFiles(), p.GoFiles, p.CgoFiles, p.TestGoFiles, p.XTestGoFiles))
|
return p.mkAbs(str.StringList(p.IgnoredGoFiles, p.GoFiles, p.CgoFiles, p.TestGoFiles, p.XTestGoFiles))
|
||||||
}
|
|
||||||
|
|
||||||
// constraintIgnoredGoFiles returns the list of Go files ignored for reasons
|
|
||||||
// other than having a name beginning with '.' or '_'.
|
|
||||||
func (p *Package) constraintIgnoredGoFiles() []string {
|
|
||||||
if len(p.IgnoredGoFiles) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
files := make([]string, 0, len(p.IgnoredGoFiles))
|
|
||||||
for _, f := range p.IgnoredGoFiles {
|
|
||||||
if f != "" && f[0] != '.' && f[0] != '_' {
|
|
||||||
files = append(files, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return files
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// usesSwig reports whether the package needs to run SWIG.
|
// usesSwig reports whether the package needs to run SWIG.
|
||||||
|
|
|
||||||
|
|
@ -79,9 +79,8 @@ type Repo interface {
|
||||||
ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, err error)
|
ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, err error)
|
||||||
|
|
||||||
// RecentTag returns the most recent tag on rev or one of its predecessors
|
// RecentTag returns the most recent tag on rev or one of its predecessors
|
||||||
// with the given prefix and major version.
|
// with the given prefix. allowed may be used to filter out unwanted versions.
|
||||||
// An empty major string matches any major version.
|
RecentTag(rev, prefix string, allowed func(string) bool) (tag string, err error)
|
||||||
RecentTag(rev, prefix, major string) (tag string, err error)
|
|
||||||
|
|
||||||
// DescendsFrom reports whether rev or any of its ancestors has the given tag.
|
// DescendsFrom reports whether rev or any of its ancestors has the given tag.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -644,7 +644,7 @@ func (r *gitRepo) readFileRevs(tags []string, file string, fileMap map[string]*F
|
||||||
return missing, nil
|
return missing, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *gitRepo) RecentTag(rev, prefix, major string) (tag string, err error) {
|
func (r *gitRepo) RecentTag(rev, prefix string, allowed func(string) bool) (tag string, err error) {
|
||||||
info, err := r.Stat(rev)
|
info, err := r.Stat(rev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
@ -680,7 +680,10 @@ func (r *gitRepo) RecentTag(rev, prefix, major string) (tag string, err error) {
|
||||||
// NOTE: Do not replace the call to semver.Compare with semver.Max.
|
// NOTE: Do not replace the call to semver.Compare with semver.Max.
|
||||||
// We want to return the actual tag, not a canonicalized version of it,
|
// We want to return the actual tag, not a canonicalized version of it,
|
||||||
// and semver.Max currently canonicalizes (see golang.org/issue/32700).
|
// and semver.Max currently canonicalizes (see golang.org/issue/32700).
|
||||||
if c := semver.Canonical(semtag); c != "" && strings.HasPrefix(semtag, c) && (major == "" || semver.Major(c) == major) && semver.Compare(semtag, highest) > 0 {
|
if c := semver.Canonical(semtag); c == "" || !strings.HasPrefix(semtag, c) || !allowed(semtag) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if semver.Compare(semtag, highest) > 0 {
|
||||||
highest = semtag
|
highest = semtag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -395,7 +395,7 @@ func (r *vcsRepo) ReadFileRevs(revs []string, file string, maxSize int64) (map[s
|
||||||
return nil, vcsErrorf("ReadFileRevs not implemented")
|
return nil, vcsErrorf("ReadFileRevs not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *vcsRepo) RecentTag(rev, prefix, major string) (tag string, err error) {
|
func (r *vcsRepo) RecentTag(rev, prefix string, allowed func(string) bool) (tag string, err error) {
|
||||||
// We don't technically need to lock here since we're returning an error
|
// We don't technically need to lock here since we're returning an error
|
||||||
// uncondititonally, but doing so anyway will help to avoid baking in
|
// uncondititonally, but doing so anyway will help to avoid baking in
|
||||||
// lock-inversion bugs.
|
// lock-inversion bugs.
|
||||||
|
|
|
||||||
|
|
@ -419,9 +419,14 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
|
||||||
tagPrefix = r.codeDir + "/"
|
tagPrefix = r.codeDir + "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isRetracted, err := r.retractedVersions()
|
||||||
|
if err != nil {
|
||||||
|
isRetracted = func(string) bool { return false }
|
||||||
|
}
|
||||||
|
|
||||||
// tagToVersion returns the version obtained by trimming tagPrefix from tag.
|
// tagToVersion returns the version obtained by trimming tagPrefix from tag.
|
||||||
// If the tag is invalid or a pseudo-version, tagToVersion returns an empty
|
// If the tag is invalid, retracted, or a pseudo-version, tagToVersion returns
|
||||||
// version.
|
// an empty version.
|
||||||
tagToVersion := func(tag string) (v string, tagIsCanonical bool) {
|
tagToVersion := func(tag string) (v string, tagIsCanonical bool) {
|
||||||
if !strings.HasPrefix(tag, tagPrefix) {
|
if !strings.HasPrefix(tag, tagPrefix) {
|
||||||
return "", false
|
return "", false
|
||||||
|
|
@ -436,6 +441,9 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
|
||||||
if v == "" || !strings.HasPrefix(trimmed, v) {
|
if v == "" || !strings.HasPrefix(trimmed, v) {
|
||||||
return "", false // Invalid or incomplete version (just vX or vX.Y).
|
return "", false // Invalid or incomplete version (just vX or vX.Y).
|
||||||
}
|
}
|
||||||
|
if isRetracted(v) {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
if v == trimmed {
|
if v == trimmed {
|
||||||
tagIsCanonical = true
|
tagIsCanonical = true
|
||||||
}
|
}
|
||||||
|
|
@ -500,15 +508,24 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
|
||||||
return checkGoMod()
|
return checkGoMod()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find the highest tagged version in the revision's history, subject to
|
||||||
|
// major version and +incompatible constraints. Use that version as the
|
||||||
|
// pseudo-version base so that the pseudo-version sorts higher. Ignore
|
||||||
|
// retracted versions.
|
||||||
|
allowedMajor := func(major string) func(v string) bool {
|
||||||
|
return func(v string) bool {
|
||||||
|
return (major == "" || semver.Major(v) == major) && !isRetracted(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
if pseudoBase == "" {
|
if pseudoBase == "" {
|
||||||
var tag string
|
var tag string
|
||||||
if r.pseudoMajor != "" || canUseIncompatible() {
|
if r.pseudoMajor != "" || canUseIncompatible() {
|
||||||
tag, _ = r.code.RecentTag(info.Name, tagPrefix, r.pseudoMajor)
|
tag, _ = r.code.RecentTag(info.Name, tagPrefix, allowedMajor(r.pseudoMajor))
|
||||||
} else {
|
} else {
|
||||||
// Allow either v1 or v0, but not incompatible higher versions.
|
// Allow either v1 or v0, but not incompatible higher versions.
|
||||||
tag, _ = r.code.RecentTag(info.Name, tagPrefix, "v1")
|
tag, _ = r.code.RecentTag(info.Name, tagPrefix, allowedMajor("v1"))
|
||||||
if tag == "" {
|
if tag == "" {
|
||||||
tag, _ = r.code.RecentTag(info.Name, tagPrefix, "v0")
|
tag, _ = r.code.RecentTag(info.Name, tagPrefix, allowedMajor("v0"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pseudoBase, _ = tagToVersion(tag) // empty if the tag is invalid
|
pseudoBase, _ = tagToVersion(tag) // empty if the tag is invalid
|
||||||
|
|
@ -869,6 +886,57 @@ func (r *codeRepo) modPrefix(rev string) string {
|
||||||
return r.modPath + "@" + rev
|
return r.modPath + "@" + rev
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *codeRepo) retractedVersions() (func(string) bool, error) {
|
||||||
|
versions, err := r.Versions("")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v := range versions {
|
||||||
|
if strings.HasSuffix(v, "+incompatible") {
|
||||||
|
versions = versions[:i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(versions) == 0 {
|
||||||
|
return func(string) bool { return false }, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var highest string
|
||||||
|
for i := len(versions) - 1; i >= 0; i-- {
|
||||||
|
v := versions[i]
|
||||||
|
if semver.Prerelease(v) == "" {
|
||||||
|
highest = v
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if highest == "" {
|
||||||
|
highest = versions[len(versions)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := r.GoMod(highest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f, err := modfile.ParseLax("go.mod", data, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
retractions := make([]modfile.VersionInterval, len(f.Retract))
|
||||||
|
for _, r := range f.Retract {
|
||||||
|
retractions = append(retractions, r.VersionInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(v string) bool {
|
||||||
|
for _, r := range retractions {
|
||||||
|
if semver.Compare(r.Low, v) <= 0 && semver.Compare(v, r.High) <= 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *codeRepo) Zip(dst io.Writer, version string) error {
|
func (r *codeRepo) Zip(dst io.Writer, version string) error {
|
||||||
if version != module.CanonicalVersion(version) {
|
if version != module.CanonicalVersion(version) {
|
||||||
return fmt.Errorf("version %s is not canonical", version)
|
return fmt.Errorf("version %s is not canonical", version)
|
||||||
|
|
|
||||||
|
|
@ -221,10 +221,8 @@ func pkgImportPath(pkgpath string) *load.Package {
|
||||||
// See https://golang.org/issue/18878.
|
// See https://golang.org/issue/18878.
|
||||||
func TestRespectSetgidDir(t *testing.T) {
|
func TestRespectSetgidDir(t *testing.T) {
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "darwin", "ios":
|
case "ios":
|
||||||
if runtime.GOARCH == "arm64" {
|
t.Skip("can't set SetGID bit with chmod on iOS")
|
||||||
t.Skip("can't set SetGID bit with chmod on iOS")
|
|
||||||
}
|
|
||||||
case "windows", "plan9":
|
case "windows", "plan9":
|
||||||
t.Skip("chown/chmod setgid are not supported on Windows or Plan 9")
|
t.Skip("chown/chmod setgid are not supported on Windows or Plan 9")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -922,12 +922,13 @@ func (b *Builder) loadCachedSrcFiles(a *Action) error {
|
||||||
|
|
||||||
// vetConfig is the configuration passed to vet describing a single package.
|
// vetConfig is the configuration passed to vet describing a single package.
|
||||||
type vetConfig struct {
|
type vetConfig struct {
|
||||||
ID string // package ID (example: "fmt [fmt.test]")
|
ID string // package ID (example: "fmt [fmt.test]")
|
||||||
Compiler string // compiler name (gc, gccgo)
|
Compiler string // compiler name (gc, gccgo)
|
||||||
Dir string // directory containing package
|
Dir string // directory containing package
|
||||||
ImportPath string // canonical import path ("package path")
|
ImportPath string // canonical import path ("package path")
|
||||||
GoFiles []string // absolute paths to package source files
|
GoFiles []string // absolute paths to package source files
|
||||||
NonGoFiles []string // absolute paths to package non-Go files
|
NonGoFiles []string // absolute paths to package non-Go files
|
||||||
|
IgnoredFiles []string // absolute paths to ignored source files
|
||||||
|
|
||||||
ImportMap map[string]string // map import path in source code to package path
|
ImportMap map[string]string // map import path in source code to package path
|
||||||
PackageFile map[string]string // map package path to .a file with export data
|
PackageFile map[string]string // map package path to .a file with export data
|
||||||
|
|
@ -951,20 +952,23 @@ func buildVetConfig(a *Action, srcfiles []string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ignored := str.StringList(a.Package.IgnoredGoFiles, a.Package.IgnoredOtherFiles)
|
||||||
|
|
||||||
// Pass list of absolute paths to vet,
|
// Pass list of absolute paths to vet,
|
||||||
// so that vet's error messages will use absolute paths,
|
// so that vet's error messages will use absolute paths,
|
||||||
// so that we can reformat them relative to the directory
|
// so that we can reformat them relative to the directory
|
||||||
// in which the go command is invoked.
|
// in which the go command is invoked.
|
||||||
vcfg := &vetConfig{
|
vcfg := &vetConfig{
|
||||||
ID: a.Package.ImportPath,
|
ID: a.Package.ImportPath,
|
||||||
Compiler: cfg.BuildToolchainName,
|
Compiler: cfg.BuildToolchainName,
|
||||||
Dir: a.Package.Dir,
|
Dir: a.Package.Dir,
|
||||||
GoFiles: mkAbsFiles(a.Package.Dir, gofiles),
|
GoFiles: mkAbsFiles(a.Package.Dir, gofiles),
|
||||||
NonGoFiles: mkAbsFiles(a.Package.Dir, nongofiles),
|
NonGoFiles: mkAbsFiles(a.Package.Dir, nongofiles),
|
||||||
ImportPath: a.Package.ImportPath,
|
IgnoredFiles: mkAbsFiles(a.Package.Dir, ignored),
|
||||||
ImportMap: make(map[string]string),
|
ImportPath: a.Package.ImportPath,
|
||||||
PackageFile: make(map[string]string),
|
ImportMap: make(map[string]string),
|
||||||
Standard: make(map[string]bool),
|
PackageFile: make(map[string]string),
|
||||||
|
Standard: make(map[string]bool),
|
||||||
}
|
}
|
||||||
a.vetCfg = vcfg
|
a.vetCfg = vcfg
|
||||||
for i, raw := range a.Package.Internal.RawImports {
|
for i, raw := range a.Package.Internal.RawImports {
|
||||||
|
|
@ -1052,17 +1056,28 @@ func (b *Builder) vet(ctx context.Context, a *Action) error {
|
||||||
// This is OK as long as the packages that are farther down the
|
// This is OK as long as the packages that are farther down the
|
||||||
// dependency tree turn on *more* analysis, as here.
|
// dependency tree turn on *more* analysis, as here.
|
||||||
// (The unsafeptr check does not write any facts for use by
|
// (The unsafeptr check does not write any facts for use by
|
||||||
// later vet runs.)
|
// later vet runs, nor does unreachable.)
|
||||||
if a.Package.Goroot && !VetExplicit && VetTool == "" {
|
if a.Package.Goroot && !VetExplicit && VetTool == "" {
|
||||||
|
// Turn off -unsafeptr checks.
|
||||||
|
// There's too much unsafe.Pointer code
|
||||||
|
// that vet doesn't like in low-level packages
|
||||||
|
// like runtime, sync, and reflect.
|
||||||
// Note that $GOROOT/src/buildall.bash
|
// Note that $GOROOT/src/buildall.bash
|
||||||
// does the same for the misc-compile trybots
|
// does the same for the misc-compile trybots
|
||||||
// and should be updated if these flags are
|
// and should be updated if these flags are
|
||||||
// changed here.
|
// changed here.
|
||||||
//
|
|
||||||
// There's too much unsafe.Pointer code
|
|
||||||
// that vet doesn't like in low-level packages
|
|
||||||
// like runtime, sync, and reflect.
|
|
||||||
vetFlags = []string{"-unsafeptr=false"}
|
vetFlags = []string{"-unsafeptr=false"}
|
||||||
|
|
||||||
|
// Also turn off -unreachable checks during go test.
|
||||||
|
// During testing it is very common to make changes
|
||||||
|
// like hard-coded forced returns or panics that make
|
||||||
|
// code unreachable. It's unreasonable to insist on files
|
||||||
|
// not having any unreachable code during "go test".
|
||||||
|
// (buildall.bash still runs with -unreachable enabled
|
||||||
|
// for the overall whole-tree scan.)
|
||||||
|
if cfg.CmdName == "test" {
|
||||||
|
vetFlags = append(vetFlags, "-unreachable=false")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: We could decide that vet should compute export data for
|
// Note: We could decide that vet should compute export data for
|
||||||
|
|
|
||||||
26
src/cmd/go/testdata/script/env_write.txt
vendored
26
src/cmd/go/testdata/script/env_write.txt
vendored
|
|
@ -24,6 +24,12 @@ stdout GOARCH=
|
||||||
stdout GOOS=
|
stdout GOOS=
|
||||||
stdout GOROOT=
|
stdout GOROOT=
|
||||||
|
|
||||||
|
# checking errors
|
||||||
|
! go env -w
|
||||||
|
stderr 'go env -w: no KEY=VALUE arguments given'
|
||||||
|
! go env -u
|
||||||
|
stderr 'go env -u: no arguments given'
|
||||||
|
|
||||||
# go env -w changes default setting
|
# go env -w changes default setting
|
||||||
env root=
|
env root=
|
||||||
[windows] env root=c:
|
[windows] env root=c:
|
||||||
|
|
@ -97,6 +103,26 @@ stderr 'GOPATH entry cannot start with shell metacharacter'
|
||||||
! go env -w GOPATH=./go
|
! go env -w GOPATH=./go
|
||||||
stderr 'GOPATH entry is relative; must be absolute path'
|
stderr 'GOPATH entry is relative; must be absolute path'
|
||||||
|
|
||||||
|
# go env -w rejects invalid GOTMPDIR values
|
||||||
|
! go env -w GOTMPDIR=x
|
||||||
|
stderr 'go env -w: GOTMPDIR must be an absolute path'
|
||||||
|
|
||||||
|
# go env -w should accept absolute GOTMPDIR value
|
||||||
|
# and should not create it
|
||||||
|
[windows] go env -w GOTMPDIR=$WORK\x\y\z
|
||||||
|
[!windows] go env -w GOTMPDIR=$WORK/x/y/z
|
||||||
|
! exists $WORK/x/y/z
|
||||||
|
# we should be able to clear an env
|
||||||
|
go env -u GOTMPDIR
|
||||||
|
go env GOTMPDIR
|
||||||
|
stdout ^$
|
||||||
|
|
||||||
|
[windows] go env -w GOTMPDIR=$WORK\x\y\z
|
||||||
|
[!windows] go env -w GOTMPDIR=$WORK/x/y/z
|
||||||
|
go env -w GOTMPDIR=
|
||||||
|
go env GOTMPDIR
|
||||||
|
stdout ^$
|
||||||
|
|
||||||
# go env -w/-u checks validity of GOOS/ARCH combinations
|
# go env -w/-u checks validity of GOOS/ARCH combinations
|
||||||
env GOOS=
|
env GOOS=
|
||||||
env GOARCH=
|
env GOARCH=
|
||||||
|
|
|
||||||
62
src/cmd/go/testdata/script/mod_retract_pseudo_base.txt
vendored
Normal file
62
src/cmd/go/testdata/script/mod_retract_pseudo_base.txt
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
# When converting a commit to a pseudo-version, don't use a retracted version
|
||||||
|
# as the base.
|
||||||
|
# Verifies golang.org/issue/41700.
|
||||||
|
|
||||||
|
[!net] skip
|
||||||
|
[!exec:git] skip
|
||||||
|
env GOPROXY=direct
|
||||||
|
env GOSUMDB=off
|
||||||
|
go mod init m
|
||||||
|
|
||||||
|
# Control: check that v1.0.0 is the only version and is retracted.
|
||||||
|
go list -m -versions vcs-test.golang.org/git/retract-pseudo.git
|
||||||
|
stdout '^vcs-test.golang.org/git/retract-pseudo.git$'
|
||||||
|
go list -m -versions -retracted vcs-test.golang.org/git/retract-pseudo.git
|
||||||
|
stdout '^vcs-test.golang.org/git/retract-pseudo.git v1.0.0$'
|
||||||
|
|
||||||
|
# 713affd19d7b is a commit after v1.0.0. Don't use v1.0.0 as the base.
|
||||||
|
go list -m vcs-test.golang.org/git/retract-pseudo.git@713affd19d7b
|
||||||
|
stdout '^vcs-test.golang.org/git/retract-pseudo.git v0.0.0-20201009173747-713affd19d7b$'
|
||||||
|
|
||||||
|
# 64c061ed4371 is the commit v1.0.0 refers to. Don't convert to v1.0.0.
|
||||||
|
go list -m vcs-test.golang.org/git/retract-pseudo.git@64c061ed4371
|
||||||
|
stdout '^vcs-test.golang.org/git/retract-pseudo.git v0.0.0-20201009173747-64c061ed4371'
|
||||||
|
|
||||||
|
# A retracted version is a valid base. Retraction should not validate existing
|
||||||
|
# pseudo-versions, nor should it turn invalid pseudo-versions valid.
|
||||||
|
go get -d vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-713affd19d7b
|
||||||
|
go list -m vcs-test.golang.org/git/retract-pseudo.git
|
||||||
|
stdout '^vcs-test.golang.org/git/retract-pseudo.git v1.0.1-0.20201009173747-713affd19d7b$'
|
||||||
|
|
||||||
|
! go get -d vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-64c061ed4371
|
||||||
|
stderr '^go get vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-64c061ed4371: vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-64c061ed4371: invalid pseudo-version: tag \(v1.0.0\) found on revision 64c061ed4371 is already canonical, so should not be replaced with a pseudo-version derived from that tag$'
|
||||||
|
|
||||||
|
-- retract-pseudo.sh --
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# This is not part of the test.
|
||||||
|
# Run this to generate and update the repository on vcs-test.golang.org.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
rm -rf retract-pseudo
|
||||||
|
mkdir retract-pseudo
|
||||||
|
cd retract-pseudo
|
||||||
|
git init
|
||||||
|
|
||||||
|
# Create the module.
|
||||||
|
# Retract v1.0.0 and tag v1.0.0 at the same commit.
|
||||||
|
# The module has no unretracted release versions.
|
||||||
|
go mod init vcs-test.golang.org/git/retract-pseudo.git
|
||||||
|
go mod edit -retract v1.0.0
|
||||||
|
echo 'package p' >p.go
|
||||||
|
git add -A
|
||||||
|
git commit -m 'create module retract-pseudo'
|
||||||
|
git tag v1.0.0
|
||||||
|
|
||||||
|
# Commit a trivial change so the default branch does not point to v1.0.0.
|
||||||
|
git mv p.go q.go
|
||||||
|
git commit -m 'trivial change'
|
||||||
|
|
||||||
|
zip -r ../retract-pseudo.zip .
|
||||||
|
gsutil cp ../retract-pseudo.zip gs://vcs-test/git/retract-pseudo.zip
|
||||||
16
src/cmd/go/testdata/script/vet_flags.txt
vendored
16
src/cmd/go/testdata/script/vet_flags.txt
vendored
|
|
@ -2,21 +2,25 @@ env GO111MODULE=on
|
||||||
|
|
||||||
# Issue 35837: "go vet -<analyzer> <std package>" should use the requested
|
# Issue 35837: "go vet -<analyzer> <std package>" should use the requested
|
||||||
# analyzers, not the default analyzers for 'go test'.
|
# analyzers, not the default analyzers for 'go test'.
|
||||||
go vet -n -unreachable=false encoding/binary
|
go vet -n -buildtags=false runtime
|
||||||
stderr '-unreachable=false'
|
stderr '-buildtags=false'
|
||||||
! stderr '-unsafeptr=false'
|
! stderr '-unsafeptr=false'
|
||||||
|
|
||||||
# Issue 37030: "go vet <std package>" without other flags should disable the
|
# Issue 37030: "go vet <std package>" without other flags should disable the
|
||||||
# unsafeptr check by default.
|
# unsafeptr check by default.
|
||||||
go vet -n encoding/binary
|
go vet -n runtime
|
||||||
stderr '-unsafeptr=false'
|
stderr '-unsafeptr=false'
|
||||||
! stderr '-unreachable=false'
|
! stderr '-unreachable=false'
|
||||||
|
|
||||||
# However, it should be enabled if requested explicitly.
|
# However, it should be enabled if requested explicitly.
|
||||||
go vet -n -unsafeptr encoding/binary
|
go vet -n -unsafeptr runtime
|
||||||
stderr '-unsafeptr'
|
stderr '-unsafeptr'
|
||||||
! stderr '-unsafeptr=false'
|
! stderr '-unsafeptr=false'
|
||||||
|
|
||||||
|
# -unreachable is disabled during test but on during plain vet.
|
||||||
|
go test -n runtime
|
||||||
|
stderr '-unreachable=false'
|
||||||
|
|
||||||
# A flag terminator should be allowed before the package list.
|
# A flag terminator should be allowed before the package list.
|
||||||
go vet -n -- .
|
go vet -n -- .
|
||||||
|
|
||||||
|
|
@ -60,10 +64,10 @@ stderr '[/\\]vet'$GOEXE'["]? .* -errorsas .* ["]?\$WORK[/\\][^ ]*[/\\]vet\.cfg'
|
||||||
|
|
||||||
# "go test" on a standard package should by default disable an explicit list.
|
# "go test" on a standard package should by default disable an explicit list.
|
||||||
go test -x -run=none encoding/binary
|
go test -x -run=none encoding/binary
|
||||||
stderr '[/\\]vet'$GOEXE'["]? -unsafeptr=false ["]?\$WORK[/\\][^ ]*[/\\]vet\.cfg'
|
stderr '[/\\]vet'$GOEXE'["]? -unsafeptr=false -unreachable=false ["]?\$WORK[/\\][^ ]*[/\\]vet\.cfg'
|
||||||
|
|
||||||
go test -x -vet= -run=none encoding/binary
|
go test -x -vet= -run=none encoding/binary
|
||||||
stderr '[/\\]vet'$GOEXE'["]? -unsafeptr=false ["]?\$WORK[/\\][^ ]*[/\\]vet\.cfg'
|
stderr '[/\\]vet'$GOEXE'["]? -unsafeptr=false -unreachable=false ["]?\$WORK[/\\][^ ]*[/\\]vet\.cfg'
|
||||||
|
|
||||||
# Both should allow users to override via the -vet flag.
|
# Both should allow users to override via the -vet flag.
|
||||||
go test -x -vet=unreachable -run=none .
|
go test -x -vet=unreachable -run=none .
|
||||||
|
|
|
||||||
|
|
@ -958,9 +958,11 @@ const (
|
||||||
AVADDP
|
AVADDP
|
||||||
AVAND
|
AVAND
|
||||||
AVBIF
|
AVBIF
|
||||||
|
AVBCAX
|
||||||
AVCMEQ
|
AVCMEQ
|
||||||
AVCNT
|
AVCNT
|
||||||
AVEOR
|
AVEOR
|
||||||
|
AVEOR3
|
||||||
AVMOV
|
AVMOV
|
||||||
AVLD1
|
AVLD1
|
||||||
AVLD2
|
AVLD2
|
||||||
|
|
@ -989,6 +991,7 @@ const (
|
||||||
AVPMULL2
|
AVPMULL2
|
||||||
AVEXT
|
AVEXT
|
||||||
AVRBIT
|
AVRBIT
|
||||||
|
AVRAX1
|
||||||
AVUSHR
|
AVUSHR
|
||||||
AVUSHLL
|
AVUSHLL
|
||||||
AVUSHLL2
|
AVUSHLL2
|
||||||
|
|
@ -1001,6 +1004,7 @@ const (
|
||||||
AVBSL
|
AVBSL
|
||||||
AVBIT
|
AVBIT
|
||||||
AVTBL
|
AVTBL
|
||||||
|
AVXAR
|
||||||
AVZIP1
|
AVZIP1
|
||||||
AVZIP2
|
AVZIP2
|
||||||
AVCMTST
|
AVCMTST
|
||||||
|
|
|
||||||
|
|
@ -464,9 +464,11 @@ var Anames = []string{
|
||||||
"VADDP",
|
"VADDP",
|
||||||
"VAND",
|
"VAND",
|
||||||
"VBIF",
|
"VBIF",
|
||||||
|
"VBCAX",
|
||||||
"VCMEQ",
|
"VCMEQ",
|
||||||
"VCNT",
|
"VCNT",
|
||||||
"VEOR",
|
"VEOR",
|
||||||
|
"VEOR3",
|
||||||
"VMOV",
|
"VMOV",
|
||||||
"VLD1",
|
"VLD1",
|
||||||
"VLD2",
|
"VLD2",
|
||||||
|
|
@ -495,6 +497,7 @@ var Anames = []string{
|
||||||
"VPMULL2",
|
"VPMULL2",
|
||||||
"VEXT",
|
"VEXT",
|
||||||
"VRBIT",
|
"VRBIT",
|
||||||
|
"VRAX1",
|
||||||
"VUSHR",
|
"VUSHR",
|
||||||
"VUSHLL",
|
"VUSHLL",
|
||||||
"VUSHLL2",
|
"VUSHLL2",
|
||||||
|
|
@ -507,6 +510,7 @@ var Anames = []string{
|
||||||
"VBSL",
|
"VBSL",
|
||||||
"VBIT",
|
"VBIT",
|
||||||
"VTBL",
|
"VTBL",
|
||||||
|
"VXAR",
|
||||||
"VZIP1",
|
"VZIP1",
|
||||||
"VZIP2",
|
"VZIP2",
|
||||||
"VCMTST",
|
"VCMTST",
|
||||||
|
|
|
||||||
|
|
@ -843,6 +843,8 @@ var optab = []Optab{
|
||||||
{ASHA256H, C_ARNG, C_VREG, C_NONE, C_VREG, 1, 4, 0, 0, 0},
|
{ASHA256H, C_ARNG, C_VREG, C_NONE, C_VREG, 1, 4, 0, 0, 0},
|
||||||
{AVREV32, C_ARNG, C_NONE, C_NONE, C_ARNG, 83, 4, 0, 0, 0},
|
{AVREV32, C_ARNG, C_NONE, C_NONE, C_ARNG, 83, 4, 0, 0, 0},
|
||||||
{AVPMULL, C_ARNG, C_ARNG, C_NONE, C_ARNG, 93, 4, 0, 0, 0},
|
{AVPMULL, C_ARNG, C_ARNG, C_NONE, C_ARNG, 93, 4, 0, 0, 0},
|
||||||
|
{AVEOR3, C_ARNG, C_ARNG, C_ARNG, C_ARNG, 103, 4, 0, 0, 0},
|
||||||
|
{AVXAR, C_VCON, C_ARNG, C_ARNG, C_ARNG, 104, 4, 0, 0, 0},
|
||||||
|
|
||||||
{obj.AUNDEF, C_NONE, C_NONE, C_NONE, C_NONE, 90, 4, 0, 0, 0},
|
{obj.AUNDEF, C_NONE, C_NONE, C_NONE, C_NONE, 90, 4, 0, 0, 0},
|
||||||
{obj.APCDATA, C_VCON, C_NONE, C_NONE, C_VCON, 0, 0, 0, 0, 0},
|
{obj.APCDATA, C_VCON, C_NONE, C_NONE, C_VCON, 0, 0, 0, 0, 0},
|
||||||
|
|
@ -2769,6 +2771,7 @@ func buildop(ctxt *obj.Link) {
|
||||||
|
|
||||||
case AVADD:
|
case AVADD:
|
||||||
oprangeset(AVSUB, t)
|
oprangeset(AVSUB, t)
|
||||||
|
oprangeset(AVRAX1, t)
|
||||||
|
|
||||||
case AAESD:
|
case AAESD:
|
||||||
oprangeset(AAESE, t)
|
oprangeset(AAESE, t)
|
||||||
|
|
@ -2827,6 +2830,9 @@ func buildop(ctxt *obj.Link) {
|
||||||
oprangeset(AVLD4, t)
|
oprangeset(AVLD4, t)
|
||||||
oprangeset(AVLD4R, t)
|
oprangeset(AVLD4R, t)
|
||||||
|
|
||||||
|
case AVEOR3:
|
||||||
|
oprangeset(AVBCAX, t)
|
||||||
|
|
||||||
case ASHA1H,
|
case ASHA1H,
|
||||||
AVCNT,
|
AVCNT,
|
||||||
AVMOV,
|
AVMOV,
|
||||||
|
|
@ -2839,7 +2845,8 @@ func buildop(ctxt *obj.Link) {
|
||||||
AVDUP,
|
AVDUP,
|
||||||
AVMOVI,
|
AVMOVI,
|
||||||
APRFM,
|
APRFM,
|
||||||
AVEXT:
|
AVEXT,
|
||||||
|
AVXAR:
|
||||||
break
|
break
|
||||||
|
|
||||||
case obj.ANOP,
|
case obj.ANOP,
|
||||||
|
|
@ -3120,12 +3127,13 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
|
||||||
|
|
||||||
case 6: /* b ,O(R); bl ,O(R) */
|
case 6: /* b ,O(R); bl ,O(R) */
|
||||||
o1 = c.opbrr(p, p.As)
|
o1 = c.opbrr(p, p.As)
|
||||||
|
|
||||||
o1 |= uint32(p.To.Reg&31) << 5
|
o1 |= uint32(p.To.Reg&31) << 5
|
||||||
rel := obj.Addrel(c.cursym)
|
if p.As == obj.ACALL {
|
||||||
rel.Off = int32(c.pc)
|
rel := obj.Addrel(c.cursym)
|
||||||
rel.Siz = 0
|
rel.Off = int32(c.pc)
|
||||||
rel.Type = objabi.R_CALLIND
|
rel.Siz = 0
|
||||||
|
rel.Type = objabi.R_CALLIND
|
||||||
|
}
|
||||||
|
|
||||||
case 7: /* beq s */
|
case 7: /* beq s */
|
||||||
o1 = c.opbra(p, p.As)
|
o1 = c.opbra(p, p.As)
|
||||||
|
|
@ -4204,7 +4212,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
|
||||||
rel.Add = 0
|
rel.Add = 0
|
||||||
rel.Type = objabi.R_ARM64_GOTPCREL
|
rel.Type = objabi.R_ARM64_GOTPCREL
|
||||||
|
|
||||||
case 72: /* vaddp/vand/vcmeq/vorr/vadd/veor/vfmla/vfmls/vbit/vbsl/vcmtst/vsub/vbif/vuzip1/vuzip2 Vm.<T>, Vn.<T>, Vd.<T> */
|
case 72: /* vaddp/vand/vcmeq/vorr/vadd/veor/vfmla/vfmls/vbit/vbsl/vcmtst/vsub/vbif/vuzip1/vuzip2/vrax1 Vm.<T>, Vn.<T>, Vd.<T> */
|
||||||
af := int((p.From.Reg >> 5) & 15)
|
af := int((p.From.Reg >> 5) & 15)
|
||||||
af3 := int((p.Reg >> 5) & 15)
|
af3 := int((p.Reg >> 5) & 15)
|
||||||
at := int((p.To.Reg >> 5) & 15)
|
at := int((p.To.Reg >> 5) & 15)
|
||||||
|
|
@ -4268,6 +4276,12 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
|
||||||
} else {
|
} else {
|
||||||
size = 0
|
size = 0
|
||||||
}
|
}
|
||||||
|
case AVRAX1:
|
||||||
|
if af != ARNG_2D {
|
||||||
|
c.ctxt.Diag("invalid arrangement: %v", p)
|
||||||
|
}
|
||||||
|
size = 0
|
||||||
|
Q = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
o1 |= (uint32(Q&1) << 30) | (uint32(size&3) << 22) | (uint32(rf&31) << 16) | (uint32(r&31) << 5) | uint32(rt&31)
|
o1 |= (uint32(Q&1) << 30) | (uint32(size&3) << 22) | (uint32(rf&31) << 16) | (uint32(r&31) << 5) | uint32(rt&31)
|
||||||
|
|
@ -5185,6 +5199,51 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
|
||||||
c.ctxt.Diag("shift amount out of range: %v\n", p)
|
c.ctxt.Diag("shift amount out of range: %v\n", p)
|
||||||
}
|
}
|
||||||
o1 |= uint32(immh)<<19 | uint32(shift)<<16 | uint32(rf&31)<<5 | uint32(p.To.Reg&31)
|
o1 |= uint32(immh)<<19 | uint32(shift)<<16 | uint32(rf&31)<<5 | uint32(p.To.Reg&31)
|
||||||
|
case 103: /* VEOR3/VBCAX Va.B16, Vm.B16, Vn.B16, Vd.B16 */
|
||||||
|
ta := (p.From.Reg >> 5) & 15
|
||||||
|
tm := (p.Reg >> 5) & 15
|
||||||
|
td := (p.To.Reg >> 5) & 15
|
||||||
|
tn := ((p.GetFrom3().Reg) >> 5) & 15
|
||||||
|
|
||||||
|
if ta != tm || ta != tn || ta != td || ta != ARNG_16B {
|
||||||
|
c.ctxt.Diag("invalid arrangement: %v", p)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
o1 = c.oprrr(p, p.As)
|
||||||
|
ra := int(p.From.Reg)
|
||||||
|
rm := int(p.Reg)
|
||||||
|
rn := int(p.GetFrom3().Reg)
|
||||||
|
rd := int(p.To.Reg)
|
||||||
|
o1 |= uint32(rm&31)<<16 | uint32(ra&31)<<10 | uint32(rn&31)<<5 | uint32(rd)&31
|
||||||
|
|
||||||
|
case 104: /* vxar $imm4, Vm.<T>, Vn.<T>, Vd.<T> */
|
||||||
|
af := ((p.GetFrom3().Reg) >> 5) & 15
|
||||||
|
at := (p.To.Reg >> 5) & 15
|
||||||
|
a := (p.Reg >> 5) & 15
|
||||||
|
index := int(p.From.Offset)
|
||||||
|
|
||||||
|
if af != a || af != at {
|
||||||
|
c.ctxt.Diag("invalid arrangement: %v", p)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if af != ARNG_2D {
|
||||||
|
c.ctxt.Diag("invalid arrangement, should be D2: %v", p)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if index < 0 || index > 63 {
|
||||||
|
c.ctxt.Diag("illegal offset: %v", p)
|
||||||
|
}
|
||||||
|
|
||||||
|
o1 = c.opirr(p, p.As)
|
||||||
|
rf := (p.GetFrom3().Reg) & 31
|
||||||
|
rt := (p.To.Reg) & 31
|
||||||
|
r := (p.Reg) & 31
|
||||||
|
|
||||||
|
o1 |= (uint32(r&31) << 16) | (uint32(index&63) << 10) | (uint32(rf&31) << 5) | uint32(rt&31)
|
||||||
|
|
||||||
}
|
}
|
||||||
out[0] = o1
|
out[0] = o1
|
||||||
out[1] = o2
|
out[1] = o2
|
||||||
|
|
@ -5760,6 +5819,9 @@ func (c *ctxt7) oprrr(p *obj.Prog, a obj.As) uint32 {
|
||||||
case AVAND:
|
case AVAND:
|
||||||
return 7<<25 | 1<<21 | 7<<10
|
return 7<<25 | 1<<21 | 7<<10
|
||||||
|
|
||||||
|
case AVBCAX:
|
||||||
|
return 0xCE<<24 | 1<<21
|
||||||
|
|
||||||
case AVCMEQ:
|
case AVCMEQ:
|
||||||
return 1<<29 | 0x71<<21 | 0x23<<10
|
return 1<<29 | 0x71<<21 | 0x23<<10
|
||||||
|
|
||||||
|
|
@ -5775,12 +5837,18 @@ func (c *ctxt7) oprrr(p *obj.Prog, a obj.As) uint32 {
|
||||||
case AVEOR:
|
case AVEOR:
|
||||||
return 1<<29 | 0x71<<21 | 7<<10
|
return 1<<29 | 0x71<<21 | 7<<10
|
||||||
|
|
||||||
|
case AVEOR3:
|
||||||
|
return 0xCE << 24
|
||||||
|
|
||||||
case AVORR:
|
case AVORR:
|
||||||
return 7<<25 | 5<<21 | 7<<10
|
return 7<<25 | 5<<21 | 7<<10
|
||||||
|
|
||||||
case AVREV16:
|
case AVREV16:
|
||||||
return 3<<26 | 2<<24 | 1<<21 | 3<<11
|
return 3<<26 | 2<<24 | 1<<21 | 3<<11
|
||||||
|
|
||||||
|
case AVRAX1:
|
||||||
|
return 0xCE<<24 | 3<<21 | 1<<15 | 3<<10
|
||||||
|
|
||||||
case AVREV32:
|
case AVREV32:
|
||||||
return 11<<26 | 2<<24 | 1<<21 | 1<<11
|
return 11<<26 | 2<<24 | 1<<21 | 1<<11
|
||||||
|
|
||||||
|
|
@ -6038,6 +6106,8 @@ func (c *ctxt7) opirr(p *obj.Prog, a obj.As) uint32 {
|
||||||
|
|
||||||
case AVUSHLL2, AVUXTL2:
|
case AVUSHLL2, AVUXTL2:
|
||||||
return 3<<29 | 15<<24 | 0x29<<10
|
return 3<<29 | 15<<24 | 0x29<<10
|
||||||
|
case AVXAR:
|
||||||
|
return 0xCE<<24 | 1<<23
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ctxt.Diag("%v: bad irr %v", p, a)
|
c.ctxt.Diag("%v: bad irr %v", p, a)
|
||||||
|
|
|
||||||
|
|
@ -589,7 +589,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
||||||
q1.To.Reg = REGSP
|
q1.To.Reg = REGSP
|
||||||
q1.Spadj = c.autosize
|
q1.Spadj = c.autosize
|
||||||
|
|
||||||
if c.ctxt.Headtype == objabi.Hdarwin {
|
if objabi.GOOS == "ios" {
|
||||||
// iOS does not support SA_ONSTACK. We will run the signal handler
|
// iOS does not support SA_ONSTACK. We will run the signal handler
|
||||||
// on the G stack. If we write below SP, it may be clobbered by
|
// on the G stack. If we write below SP, it may be clobbered by
|
||||||
// the signal handler. So we save LR after decrementing SP.
|
// the signal handler. So we save LR after decrementing SP.
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ var (
|
||||||
GOROOT = envOr("GOROOT", defaultGOROOT)
|
GOROOT = envOr("GOROOT", defaultGOROOT)
|
||||||
GOARCH = envOr("GOARCH", defaultGOARCH)
|
GOARCH = envOr("GOARCH", defaultGOARCH)
|
||||||
GOOS = envOr("GOOS", defaultGOOS)
|
GOOS = envOr("GOOS", defaultGOOS)
|
||||||
|
GO386 = envOr("GO386", defaultGO386)
|
||||||
GOAMD64 = goamd64()
|
GOAMD64 = goamd64()
|
||||||
GOARM = goarm()
|
GOARM = goarm()
|
||||||
GOMIPS = gomips()
|
GOMIPS = gomips()
|
||||||
|
|
@ -135,14 +136,6 @@ func init() {
|
||||||
if GOARCH != "amd64" {
|
if GOARCH != "amd64" {
|
||||||
Regabi_enabled = 0
|
Regabi_enabled = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if v := os.Getenv("GO386"); v != "" && v != "sse2" {
|
|
||||||
msg := fmt.Sprintf("unsupported setting GO386=%s", v)
|
|
||||||
if v == "387" {
|
|
||||||
msg += ". 387 support was dropped in Go 1.16. Consider using gccgo instead."
|
|
||||||
}
|
|
||||||
log.Fatal(msg)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: must agree with runtime.framepointer_enabled.
|
// Note: must agree with runtime.framepointer_enabled.
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ func MSanSupported(goos, goarch string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustLinkExternal reports whether goos/goarch requires external linking.
|
// MustLinkExternal reports whether goos/goarch requires external linking.
|
||||||
|
// (This is the opposite of internal/testenv.CanInternalLink. Keep them in sync.)
|
||||||
func MustLinkExternal(goos, goarch string) bool {
|
func MustLinkExternal(goos, goarch string) bool {
|
||||||
switch goos {
|
switch goos {
|
||||||
case "android":
|
case "android":
|
||||||
|
|
@ -69,7 +70,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool {
|
||||||
case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/ppc64le", "linux/s390x",
|
case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/ppc64le", "linux/s390x",
|
||||||
"android/amd64", "android/arm", "android/arm64", "android/386",
|
"android/amd64", "android/arm", "android/arm64", "android/386",
|
||||||
"freebsd/amd64",
|
"freebsd/amd64",
|
||||||
"darwin/amd64",
|
"darwin/amd64", "darwin/arm64",
|
||||||
"windows/amd64", "windows/386":
|
"windows/amd64", "windows/386":
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -86,7 +87,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool {
|
||||||
case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x",
|
case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x",
|
||||||
"android/amd64", "android/arm", "android/arm64", "android/386",
|
"android/amd64", "android/arm", "android/arm64", "android/386",
|
||||||
"freebsd/amd64",
|
"freebsd/amd64",
|
||||||
"darwin/amd64",
|
"darwin/amd64", "darwin/arm64",
|
||||||
"aix/ppc64",
|
"aix/ppc64",
|
||||||
"windows/386", "windows/amd64", "windows/arm":
|
"windows/386", "windows/amd64", "windows/arm":
|
||||||
return true
|
return true
|
||||||
|
|
@ -104,7 +105,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool {
|
||||||
switch platform {
|
switch platform {
|
||||||
case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/s390x", "linux/ppc64le",
|
case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/s390x", "linux/ppc64le",
|
||||||
"android/amd64", "android/arm", "android/arm64", "android/386",
|
"android/amd64", "android/arm", "android/arm64", "android/386",
|
||||||
"darwin/amd64",
|
"darwin/amd64", "darwin/arm64",
|
||||||
"freebsd/amd64":
|
"freebsd/amd64":
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
src/cmd/internal/sys/supported_test.go
Normal file
18
src/cmd/internal/sys/supported_test.go
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright 2020 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 sys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"internal/testenv"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMustLinkExternalMatchesTestenv(t *testing.T) {
|
||||||
|
// MustLinkExternal and testenv.CanInternalLink are the exact opposite.
|
||||||
|
if b := MustLinkExternal(runtime.GOOS, runtime.GOARCH); b != !testenv.CanInternalLink() {
|
||||||
|
t.Fatalf("MustLinkExternal() == %v, testenv.CanInternalLink() == %v, don't match", b, testenv.CanInternalLink())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -371,7 +371,7 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sy
|
||||||
rt := r.Type
|
rt := r.Type
|
||||||
siz := r.Size
|
siz := r.Size
|
||||||
|
|
||||||
if ldr.SymType(rs) == sym.SHOSTOBJ || rt == objabi.R_CALLARM64 || rt == objabi.R_ADDRARM64 {
|
if ldr.SymType(rs) == sym.SHOSTOBJ || rt == objabi.R_CALLARM64 || rt == objabi.R_ADDRARM64 || rt == objabi.R_ARM64_GOTPCREL {
|
||||||
if ldr.SymDynid(rs) < 0 {
|
if ldr.SymDynid(rs) < 0 {
|
||||||
ldr.Errorf(s, "reloc %d (%s) to non-macho symbol %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymType(rs), ldr.SymType(rs))
|
ldr.Errorf(s, "reloc %d (%s) to non-macho symbol %s type=%d (%s)", rt, sym.RelocName(arch, rt), ldr.SymName(rs), ldr.SymType(rs), ldr.SymType(rs))
|
||||||
return false
|
return false
|
||||||
|
|
@ -415,6 +415,22 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sy
|
||||||
}
|
}
|
||||||
v |= 1 << 24 // pc-relative bit
|
v |= 1 << 24 // pc-relative bit
|
||||||
v |= ld.MACHO_ARM64_RELOC_PAGE21 << 28
|
v |= ld.MACHO_ARM64_RELOC_PAGE21 << 28
|
||||||
|
case objabi.R_ARM64_GOTPCREL:
|
||||||
|
siz = 4
|
||||||
|
// Two relocation entries: MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 MACHO_ARM64_RELOC_GOT_LOAD_PAGE21
|
||||||
|
// if r.Xadd is non-zero, add two MACHO_ARM64_RELOC_ADDEND.
|
||||||
|
if r.Xadd != 0 {
|
||||||
|
out.Write32(uint32(sectoff + 4))
|
||||||
|
out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(r.Xadd&0xffffff))
|
||||||
|
}
|
||||||
|
out.Write32(uint32(sectoff + 4))
|
||||||
|
out.Write32(v | (ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 << 28) | (2 << 25))
|
||||||
|
if r.Xadd != 0 {
|
||||||
|
out.Write32(uint32(sectoff))
|
||||||
|
out.Write32((ld.MACHO_ARM64_RELOC_ADDEND << 28) | (2 << 25) | uint32(r.Xadd&0xffffff))
|
||||||
|
}
|
||||||
|
v |= 1 << 24 // pc-relative bit
|
||||||
|
v |= ld.MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 << 28
|
||||||
}
|
}
|
||||||
|
|
||||||
switch siz {
|
switch siz {
|
||||||
|
|
@ -457,7 +473,7 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
|
||||||
}
|
}
|
||||||
|
|
||||||
nExtReloc = 2 // need two ELF/Mach-O relocations. see elfreloc1/machoreloc1
|
nExtReloc = 2 // need two ELF/Mach-O relocations. see elfreloc1/machoreloc1
|
||||||
if target.IsDarwin() && rt == objabi.R_ADDRARM64 && xadd != 0 {
|
if target.IsDarwin() && xadd != 0 {
|
||||||
nExtReloc = 4 // need another two relocations for non-zero addend
|
nExtReloc = 4 // need another two relocations for non-zero addend
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,13 @@ func (mode *BuildMode) Set(s string) error {
|
||||||
case "pie":
|
case "pie":
|
||||||
switch objabi.GOOS {
|
switch objabi.GOOS {
|
||||||
case "aix", "android", "linux", "windows":
|
case "aix", "android", "linux", "windows":
|
||||||
case "darwin", "freebsd":
|
case "darwin":
|
||||||
|
switch objabi.GOARCH {
|
||||||
|
case "amd64", "arm64":
|
||||||
|
default:
|
||||||
|
return badmode()
|
||||||
|
}
|
||||||
|
case "freebsd":
|
||||||
switch objabi.GOARCH {
|
switch objabi.GOARCH {
|
||||||
case "amd64":
|
case "amd64":
|
||||||
default:
|
default:
|
||||||
|
|
@ -95,7 +101,13 @@ func (mode *BuildMode) Set(s string) error {
|
||||||
default:
|
default:
|
||||||
return badmode()
|
return badmode()
|
||||||
}
|
}
|
||||||
case "darwin", "freebsd":
|
case "darwin":
|
||||||
|
switch objabi.GOARCH {
|
||||||
|
case "amd64", "arm64":
|
||||||
|
default:
|
||||||
|
return badmode()
|
||||||
|
}
|
||||||
|
case "freebsd":
|
||||||
switch objabi.GOARCH {
|
switch objabi.GOARCH {
|
||||||
case "amd64":
|
case "amd64":
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -238,6 +238,10 @@ func TestSizes(t *testing.T) {
|
||||||
if runtime.GOOS == "plan9" {
|
if runtime.GOOS == "plan9" {
|
||||||
t.Skip("skipping on plan9; no DWARF symbol table in executables")
|
t.Skip("skipping on plan9; no DWARF symbol table in executables")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// External linking may bring in C symbols with unknown size. Skip.
|
||||||
|
testenv.MustInternalLink(t)
|
||||||
|
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// DWARF sizes should never be -1.
|
// DWARF sizes should never be -1.
|
||||||
|
|
@ -919,6 +923,7 @@ func TestAbstractOriginSanityIssue26237(t *testing.T) {
|
||||||
|
|
||||||
func TestRuntimeTypeAttrInternal(t *testing.T) {
|
func TestRuntimeTypeAttrInternal(t *testing.T) {
|
||||||
testenv.MustHaveGoBuild(t)
|
testenv.MustHaveGoBuild(t)
|
||||||
|
testenv.MustInternalLink(t)
|
||||||
|
|
||||||
if runtime.GOOS == "plan9" {
|
if runtime.GOOS == "plan9" {
|
||||||
t.Skip("skipping on plan9; no DWARF symbol table in executables")
|
t.Skip("skipping on plan9; no DWARF symbol table in executables")
|
||||||
|
|
@ -1018,6 +1023,9 @@ func main() {
|
||||||
t.Fatalf("*main.X DIE had no runtime type attr. DIE: %v", dies[0])
|
t.Fatalf("*main.X DIE had no runtime type attr. DIE: %v", dies[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
|
||||||
|
return // everything is PIE on ARM64, addresses are relocated
|
||||||
|
}
|
||||||
if rtAttr.(uint64)+types.Addr != addr {
|
if rtAttr.(uint64)+types.Addr != addr {
|
||||||
t.Errorf("DWARF type offset was %#x+%#x, but test program said %#x", rtAttr.(uint64), types.Addr, addr)
|
t.Errorf("DWARF type offset was %#x+%#x, but test program said %#x", rtAttr.(uint64), types.Addr, addr)
|
||||||
}
|
}
|
||||||
|
|
@ -1203,6 +1211,15 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When external linking, we put all symbols in the symbol table (so the
|
||||||
|
// external linker can find them). Skip the symbol table check.
|
||||||
|
// TODO: maybe there is some way to tell the external linker not to put
|
||||||
|
// those symbols in the executable's symbol table? Prefix the symbol name
|
||||||
|
// with "." or "L" to pretend it is a label?
|
||||||
|
if !testenv.CanInternalLink() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
syms, err := f.Symbols()
|
syms, err := f.Symbols()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error reading symbols: %v", err)
|
t.Fatalf("error reading symbols: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUndefinedRelocErrors(t *testing.T) {
|
func TestUndefinedRelocErrors(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
testenv.MustHaveGoBuild(t)
|
testenv.MustHaveGoBuild(t)
|
||||||
|
|
||||||
|
// When external linking, symbols may be defined externally, so we allow
|
||||||
|
// undefined symbols and let external linker resolve. Skip the test.
|
||||||
|
testenv.MustInternalLink(t)
|
||||||
|
|
||||||
|
t.Parallel()
|
||||||
dir, err := ioutil.TempDir("", "go-build")
|
dir, err := ioutil.TempDir("", "go-build")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
||||||
|
|
@ -1254,7 +1254,9 @@ func (ctxt *Link) hostlink() {
|
||||||
// -headerpad is incompatible with -fembed-bitcode.
|
// -headerpad is incompatible with -fembed-bitcode.
|
||||||
argv = append(argv, "-Wl,-headerpad,1144")
|
argv = append(argv, "-Wl,-headerpad,1144")
|
||||||
}
|
}
|
||||||
if ctxt.DynlinkingGo() && !ctxt.Arch.InFamily(sys.ARM, sys.ARM64) {
|
if ctxt.DynlinkingGo() && objabi.GOOS != "ios" {
|
||||||
|
// -flat_namespace is deprecated on iOS.
|
||||||
|
// It is useful for supporting plugins. We don't support plugins on iOS.
|
||||||
argv = append(argv, "-Wl,-flat_namespace")
|
argv = append(argv, "-Wl,-flat_namespace")
|
||||||
}
|
}
|
||||||
if !combineDwarf {
|
if !combineDwarf {
|
||||||
|
|
@ -1327,9 +1329,6 @@ func (ctxt *Link) hostlink() {
|
||||||
case BuildModeCShared:
|
case BuildModeCShared:
|
||||||
if ctxt.HeadType == objabi.Hdarwin {
|
if ctxt.HeadType == objabi.Hdarwin {
|
||||||
argv = append(argv, "-dynamiclib")
|
argv = append(argv, "-dynamiclib")
|
||||||
if ctxt.Arch.Family != sys.AMD64 {
|
|
||||||
argv = append(argv, "-Wl,-read_only_relocs,suppress")
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// ELF.
|
// ELF.
|
||||||
argv = append(argv, "-Wl,-Bsymbolic")
|
argv = append(argv, "-Wl,-Bsymbolic")
|
||||||
|
|
|
||||||
|
|
@ -76,36 +76,38 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MACHO_CPU_AMD64 = 1<<24 | 7
|
MACHO_CPU_AMD64 = 1<<24 | 7
|
||||||
MACHO_CPU_386 = 7
|
MACHO_CPU_386 = 7
|
||||||
MACHO_SUBCPU_X86 = 3
|
MACHO_SUBCPU_X86 = 3
|
||||||
MACHO_CPU_ARM = 12
|
MACHO_CPU_ARM = 12
|
||||||
MACHO_SUBCPU_ARM = 0
|
MACHO_SUBCPU_ARM = 0
|
||||||
MACHO_SUBCPU_ARMV7 = 9
|
MACHO_SUBCPU_ARMV7 = 9
|
||||||
MACHO_CPU_ARM64 = 1<<24 | 12
|
MACHO_CPU_ARM64 = 1<<24 | 12
|
||||||
MACHO_SUBCPU_ARM64_ALL = 0
|
MACHO_SUBCPU_ARM64_ALL = 0
|
||||||
MACHO32SYMSIZE = 12
|
MACHO32SYMSIZE = 12
|
||||||
MACHO64SYMSIZE = 16
|
MACHO64SYMSIZE = 16
|
||||||
MACHO_X86_64_RELOC_UNSIGNED = 0
|
MACHO_X86_64_RELOC_UNSIGNED = 0
|
||||||
MACHO_X86_64_RELOC_SIGNED = 1
|
MACHO_X86_64_RELOC_SIGNED = 1
|
||||||
MACHO_X86_64_RELOC_BRANCH = 2
|
MACHO_X86_64_RELOC_BRANCH = 2
|
||||||
MACHO_X86_64_RELOC_GOT_LOAD = 3
|
MACHO_X86_64_RELOC_GOT_LOAD = 3
|
||||||
MACHO_X86_64_RELOC_GOT = 4
|
MACHO_X86_64_RELOC_GOT = 4
|
||||||
MACHO_X86_64_RELOC_SUBTRACTOR = 5
|
MACHO_X86_64_RELOC_SUBTRACTOR = 5
|
||||||
MACHO_X86_64_RELOC_SIGNED_1 = 6
|
MACHO_X86_64_RELOC_SIGNED_1 = 6
|
||||||
MACHO_X86_64_RELOC_SIGNED_2 = 7
|
MACHO_X86_64_RELOC_SIGNED_2 = 7
|
||||||
MACHO_X86_64_RELOC_SIGNED_4 = 8
|
MACHO_X86_64_RELOC_SIGNED_4 = 8
|
||||||
MACHO_ARM_RELOC_VANILLA = 0
|
MACHO_ARM_RELOC_VANILLA = 0
|
||||||
MACHO_ARM_RELOC_PAIR = 1
|
MACHO_ARM_RELOC_PAIR = 1
|
||||||
MACHO_ARM_RELOC_SECTDIFF = 2
|
MACHO_ARM_RELOC_SECTDIFF = 2
|
||||||
MACHO_ARM_RELOC_BR24 = 5
|
MACHO_ARM_RELOC_BR24 = 5
|
||||||
MACHO_ARM64_RELOC_UNSIGNED = 0
|
MACHO_ARM64_RELOC_UNSIGNED = 0
|
||||||
MACHO_ARM64_RELOC_BRANCH26 = 2
|
MACHO_ARM64_RELOC_BRANCH26 = 2
|
||||||
MACHO_ARM64_RELOC_PAGE21 = 3
|
MACHO_ARM64_RELOC_PAGE21 = 3
|
||||||
MACHO_ARM64_RELOC_PAGEOFF12 = 4
|
MACHO_ARM64_RELOC_PAGEOFF12 = 4
|
||||||
MACHO_ARM64_RELOC_ADDEND = 10
|
MACHO_ARM64_RELOC_GOT_LOAD_PAGE21 = 5
|
||||||
MACHO_GENERIC_RELOC_VANILLA = 0
|
MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12 = 6
|
||||||
MACHO_FAKE_GOTPCREL = 100
|
MACHO_ARM64_RELOC_ADDEND = 10
|
||||||
|
MACHO_GENERIC_RELOC_VANILLA = 0
|
||||||
|
MACHO_FAKE_GOTPCREL = 100
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -473,6 +475,18 @@ func (ctxt *Link) domacho() {
|
||||||
sb.SetReachable(true)
|
sb.SetReachable(true)
|
||||||
sb.AddUint8(0)
|
sb.AddUint8(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do not export C symbols dynamically in plugins, as runtime C symbols like crosscall2
|
||||||
|
// are in pclntab and end up pointing at the host binary, breaking unwinding.
|
||||||
|
// See Issue #18190.
|
||||||
|
if ctxt.BuildMode == BuildModePlugin {
|
||||||
|
for _, name := range []string{"_cgo_topofstack", "__cgo_topofstack", "_cgo_panic", "crosscall2"} {
|
||||||
|
s := ctxt.loader.Lookup(name, 0)
|
||||||
|
if s != 0 {
|
||||||
|
ctxt.loader.SetAttrCgoExportDynamic(s, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func machoadddynlib(lib string, linkmode LinkMode) {
|
func machoadddynlib(lib string, linkmode LinkMode) {
|
||||||
|
|
@ -897,19 +911,12 @@ func machosymtab(ctxt *Link) {
|
||||||
symtab.AddUint32(ctxt.Arch, uint32(symstr.Size()))
|
symtab.AddUint32(ctxt.Arch, uint32(symstr.Size()))
|
||||||
|
|
||||||
export := machoShouldExport(ctxt, ldr, s)
|
export := machoShouldExport(ctxt, ldr, s)
|
||||||
isGoSymbol := strings.Contains(ldr.SymExtname(s), ".")
|
|
||||||
|
|
||||||
// In normal buildmodes, only add _ to C symbols, as
|
// Prefix symbol names with "_" to match the system toolchain.
|
||||||
// Go symbols have dot in the name.
|
// (We used to only prefix C symbols, which is all required for the build.
|
||||||
//
|
// But some tools don't recognize Go symbols as symbols, so we prefix them
|
||||||
// Do not export C symbols in plugins, as runtime C
|
// as well.)
|
||||||
// symbols like crosscall2 are in pclntab and end up
|
symstr.AddUint8('_')
|
||||||
// pointing at the host binary, breaking unwinding.
|
|
||||||
// See Issue #18190.
|
|
||||||
cexport := !isGoSymbol && (ctxt.BuildMode != BuildModePlugin || onlycsymbol(ldr.SymName(s)))
|
|
||||||
if cexport || export || isGoSymbol {
|
|
||||||
symstr.AddUint8('_')
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace "·" as ".", because DTrace cannot handle it.
|
// replace "·" as ".", because DTrace cannot handle it.
|
||||||
symstr.Addstring(strings.Replace(ldr.SymExtname(s), "·", ".", -1))
|
symstr.Addstring(strings.Replace(ldr.SymExtname(s), "·", ".", -1))
|
||||||
|
|
@ -920,10 +927,13 @@ func machosymtab(ctxt *Link) {
|
||||||
symtab.AddUint16(ctxt.Arch, 0) // desc
|
symtab.AddUint16(ctxt.Arch, 0) // desc
|
||||||
symtab.AddUintXX(ctxt.Arch, 0, ctxt.Arch.PtrSize) // no value
|
symtab.AddUintXX(ctxt.Arch, 0, ctxt.Arch.PtrSize) // no value
|
||||||
} else {
|
} else {
|
||||||
if ldr.AttrCgoExport(s) || export {
|
if export || ldr.AttrCgoExportDynamic(s) {
|
||||||
symtab.AddUint8(0x0f)
|
symtab.AddUint8(0x0f) // N_SECT | N_EXT
|
||||||
|
} else if ldr.AttrCgoExportStatic(s) {
|
||||||
|
// Only export statically, not dynamically. (N_PEXT is like hidden visibility)
|
||||||
|
symtab.AddUint8(0x1f) // N_SECT | N_EXT | N_PEXT
|
||||||
} else {
|
} else {
|
||||||
symtab.AddUint8(0x0e)
|
symtab.AddUint8(0x0e) // N_SECT
|
||||||
}
|
}
|
||||||
o := s
|
o := s
|
||||||
if outer := ldr.OuterSym(o); outer != 0 {
|
if outer := ldr.OuterSym(o); outer != 0 {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// pclntab holds the state needed for pclntab generation.
|
// pclntab holds the state needed for pclntab generation.
|
||||||
|
|
@ -113,23 +112,7 @@ func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.Compilat
|
||||||
return state, compUnits, funcs
|
return state, compUnits, funcs
|
||||||
}
|
}
|
||||||
|
|
||||||
// onlycsymbol looks at a symbol's name to report whether this is a
|
|
||||||
// symbol that is referenced by C code
|
|
||||||
func onlycsymbol(sname string) bool {
|
|
||||||
switch sname {
|
|
||||||
case "_cgo_topofstack", "__cgo_topofstack", "_cgo_panic", "crosscall2":
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(sname, "_cgoexp_") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func emitPcln(ctxt *Link, s loader.Sym, container loader.Bitmap) bool {
|
func emitPcln(ctxt *Link, s loader.Sym, container loader.Bitmap) bool {
|
||||||
if ctxt.BuildMode == BuildModePlugin && ctxt.HeadType == objabi.Hdarwin && onlycsymbol(ctxt.loader.SymName(s)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// We want to generate func table entries only for the "lowest
|
// We want to generate func table entries only for the "lowest
|
||||||
// level" symbols, not containers of subsymbols.
|
// level" symbols, not containers of subsymbols.
|
||||||
return !container.Has(s)
|
return !container.Has(s)
|
||||||
|
|
|
||||||
|
|
@ -181,6 +181,7 @@ main.x: relocation target main.zero not defined
|
||||||
func TestIssue33979(t *testing.T) {
|
func TestIssue33979(t *testing.T) {
|
||||||
testenv.MustHaveGoBuild(t)
|
testenv.MustHaveGoBuild(t)
|
||||||
testenv.MustHaveCGO(t)
|
testenv.MustHaveCGO(t)
|
||||||
|
testenv.MustInternalLink(t)
|
||||||
|
|
||||||
// Skip test on platforms that do not support cgo internal linking.
|
// Skip test on platforms that do not support cgo internal linking.
|
||||||
switch runtime.GOARCH {
|
switch runtime.GOARCH {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,11 @@ func canInternalLink() bool {
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "aix":
|
case "aix":
|
||||||
return false
|
return false
|
||||||
|
case "darwin":
|
||||||
|
switch runtime.GOARCH {
|
||||||
|
case "arm64":
|
||||||
|
return false
|
||||||
|
}
|
||||||
case "dragonfly":
|
case "dragonfly":
|
||||||
return false
|
return false
|
||||||
case "freebsd":
|
case "freebsd":
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,9 @@ func testGoExec(t *testing.T, iscgo, isexternallinker bool) {
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
|
||||||
|
return true // On darwin/arm64 everything is PIE
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,10 @@ func (*objTool) Demangle(names []string) (map[string]string, error) {
|
||||||
return make(map[string]string), nil
|
return make(map[string]string), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *objTool) Disasm(file string, start, end uint64) ([]driver.Inst, error) {
|
func (t *objTool) Disasm(file string, start, end uint64, intelSyntax bool) ([]driver.Inst, error) {
|
||||||
|
if intelSyntax {
|
||||||
|
return nil, fmt.Errorf("printing assembly in Intel syntax is not supported")
|
||||||
|
}
|
||||||
d, err := t.cachedDisasm(file)
|
d, err := t.cachedDisasm(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
6
src/cmd/vendor/github.com/google/pprof/driver/driver.go
generated
vendored
6
src/cmd/vendor/github.com/google/pprof/driver/driver.go
generated
vendored
|
|
@ -142,7 +142,7 @@ type ObjTool interface {
|
||||||
|
|
||||||
// Disasm disassembles the named object file, starting at
|
// Disasm disassembles the named object file, starting at
|
||||||
// the start address and stopping at (before) the end address.
|
// the start address and stopping at (before) the end address.
|
||||||
Disasm(file string, start, end uint64) ([]Inst, error)
|
Disasm(file string, start, end uint64, intelSyntax bool) ([]Inst, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// An Inst is a single instruction in an assembly listing.
|
// An Inst is a single instruction in an assembly listing.
|
||||||
|
|
@ -269,8 +269,8 @@ func (f *internalObjFile) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym,
|
||||||
return pluginSyms, nil
|
return pluginSyms, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *internalObjTool) Disasm(file string, start, end uint64) ([]plugin.Inst, error) {
|
func (o *internalObjTool) Disasm(file string, start, end uint64, intelSyntax bool) ([]plugin.Inst, error) {
|
||||||
insts, err := o.ObjTool.Disasm(file, start, end)
|
insts, err := o.ObjTool.Disasm(file, start, end, intelSyntax)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
132
src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils.go
generated
vendored
132
src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils.go
generated
vendored
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
"debug/macho"
|
"debug/macho"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -26,6 +27,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
|
@ -39,6 +41,8 @@ type Binutils struct {
|
||||||
rep *binrep
|
rep *binrep
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var objdumpLLVMVerRE = regexp.MustCompile(`LLVM version (?:(\d*)\.(\d*)\.(\d*)|.*(trunk).*)`)
|
||||||
|
|
||||||
// binrep is an immutable representation for Binutils. It is atomically
|
// binrep is an immutable representation for Binutils. It is atomically
|
||||||
// replaced on every mutation to provide thread-safe access.
|
// replaced on every mutation to provide thread-safe access.
|
||||||
type binrep struct {
|
type binrep struct {
|
||||||
|
|
@ -51,6 +55,7 @@ type binrep struct {
|
||||||
nmFound bool
|
nmFound bool
|
||||||
objdump string
|
objdump string
|
||||||
objdumpFound bool
|
objdumpFound bool
|
||||||
|
isLLVMObjdump bool
|
||||||
|
|
||||||
// if fast, perform symbolization using nm (symbol names only),
|
// if fast, perform symbolization using nm (symbol names only),
|
||||||
// instead of file-line detail from the slower addr2line.
|
// instead of file-line detail from the slower addr2line.
|
||||||
|
|
@ -132,15 +137,103 @@ func initTools(b *binrep, config string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultPath := paths[""]
|
defaultPath := paths[""]
|
||||||
b.llvmSymbolizer, b.llvmSymbolizerFound = findExe("llvm-symbolizer", append(paths["llvm-symbolizer"], defaultPath...))
|
b.llvmSymbolizer, b.llvmSymbolizerFound = chooseExe([]string{"llvm-symbolizer"}, []string{}, append(paths["llvm-symbolizer"], defaultPath...))
|
||||||
b.addr2line, b.addr2lineFound = findExe("addr2line", append(paths["addr2line"], defaultPath...))
|
b.addr2line, b.addr2lineFound = chooseExe([]string{"addr2line"}, []string{"gaddr2line"}, append(paths["addr2line"], defaultPath...))
|
||||||
if !b.addr2lineFound {
|
// The "-n" option is supported by LLVM since 2011. The output of llvm-nm
|
||||||
// On MacOS, brew installs addr2line under gaddr2line name, so search for
|
// and GNU nm with "-n" option is interchangeable for our purposes, so we do
|
||||||
// that if the tool is not found by its default name.
|
// not need to differrentiate them.
|
||||||
b.addr2line, b.addr2lineFound = findExe("gaddr2line", append(paths["addr2line"], defaultPath...))
|
b.nm, b.nmFound = chooseExe([]string{"llvm-nm", "nm"}, []string{"gnm"}, append(paths["nm"], defaultPath...))
|
||||||
|
b.objdump, b.objdumpFound, b.isLLVMObjdump = findObjdump(append(paths["objdump"], defaultPath...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// findObjdump finds and returns path to preferred objdump binary.
|
||||||
|
// Order of preference is: llvm-objdump, objdump.
|
||||||
|
// On MacOS only, also looks for gobjdump with least preference.
|
||||||
|
// Accepts a list of paths and returns:
|
||||||
|
// a string with path to the preferred objdump binary if found,
|
||||||
|
// or an empty string if not found;
|
||||||
|
// a boolean if any acceptable objdump was found;
|
||||||
|
// a boolean indicating if it is an LLVM objdump.
|
||||||
|
func findObjdump(paths []string) (string, bool, bool) {
|
||||||
|
objdumpNames := []string{"llvm-objdump", "objdump"}
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
objdumpNames = append(objdumpNames, "gobjdump")
|
||||||
}
|
}
|
||||||
b.nm, b.nmFound = findExe("nm", append(paths["nm"], defaultPath...))
|
|
||||||
b.objdump, b.objdumpFound = findExe("objdump", append(paths["objdump"], defaultPath...))
|
for _, objdumpName := range objdumpNames {
|
||||||
|
if objdump, objdumpFound := findExe(objdumpName, paths); objdumpFound {
|
||||||
|
cmdOut, err := exec.Command(objdump, "--version").Output()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if isLLVMObjdump(string(cmdOut)) {
|
||||||
|
return objdump, true, true
|
||||||
|
}
|
||||||
|
if isBuObjdump(string(cmdOut)) {
|
||||||
|
return objdump, true, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// chooseExe finds and returns path to preferred binary. names is a list of
|
||||||
|
// names to search on both Linux and OSX. osxNames is a list of names specific
|
||||||
|
// to OSX. names always has a higher priority than osxNames. The order of
|
||||||
|
// the name within each list decides its priority (e.g. the first name has a
|
||||||
|
// higher priority than the second name in the list).
|
||||||
|
//
|
||||||
|
// It returns a string with path to the binary and a boolean indicating if any
|
||||||
|
// acceptable binary was found.
|
||||||
|
func chooseExe(names, osxNames []string, paths []string) (string, bool) {
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
names = append(names, osxNames...)
|
||||||
|
}
|
||||||
|
for _, name := range names {
|
||||||
|
if binary, found := findExe(name, paths); found {
|
||||||
|
return binary, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
// isLLVMObjdump accepts a string with path to an objdump binary,
|
||||||
|
// and returns a boolean indicating if the given binary is an LLVM
|
||||||
|
// objdump binary of an acceptable version.
|
||||||
|
func isLLVMObjdump(output string) bool {
|
||||||
|
fields := objdumpLLVMVerRE.FindStringSubmatch(output)
|
||||||
|
if len(fields) != 5 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if fields[4] == "trunk" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
verMajor, err := strconv.Atoi(fields[1])
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
verPatch, err := strconv.Atoi(fields[3])
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if runtime.GOOS == "linux" && verMajor >= 8 {
|
||||||
|
// Ensure LLVM objdump is at least version 8.0 on Linux.
|
||||||
|
// Some flags, like --demangle, and double dashes for options are
|
||||||
|
// not supported by previous versions.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
// Ensure LLVM objdump is at least version 10.0.1 on MacOS.
|
||||||
|
return verMajor > 10 || (verMajor == 10 && verPatch >= 1)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// isBuObjdump accepts a string with path to an objdump binary,
|
||||||
|
// and returns a boolean indicating if the given binary is a GNU
|
||||||
|
// binutils objdump binary. No version check is performed.
|
||||||
|
func isBuObjdump(output string) bool {
|
||||||
|
return strings.Contains(output, "GNU objdump")
|
||||||
}
|
}
|
||||||
|
|
||||||
// findExe looks for an executable command on a set of paths.
|
// findExe looks for an executable command on a set of paths.
|
||||||
|
|
@ -157,12 +250,25 @@ func findExe(cmd string, paths []string) (string, bool) {
|
||||||
|
|
||||||
// Disasm returns the assembly instructions for the specified address range
|
// Disasm returns the assembly instructions for the specified address range
|
||||||
// of a binary.
|
// of a binary.
|
||||||
func (bu *Binutils) Disasm(file string, start, end uint64) ([]plugin.Inst, error) {
|
func (bu *Binutils) Disasm(file string, start, end uint64, intelSyntax bool) ([]plugin.Inst, error) {
|
||||||
b := bu.get()
|
b := bu.get()
|
||||||
cmd := exec.Command(b.objdump, "-d", "-C", "--no-show-raw-insn", "-l",
|
if !b.objdumpFound {
|
||||||
fmt.Sprintf("--start-address=%#x", start),
|
return nil, errors.New("cannot disasm: no objdump tool available")
|
||||||
fmt.Sprintf("--stop-address=%#x", end),
|
}
|
||||||
file)
|
args := []string{"--disassemble-all", "--demangle", "--no-show-raw-insn",
|
||||||
|
"--line-numbers", fmt.Sprintf("--start-address=%#x", start),
|
||||||
|
fmt.Sprintf("--stop-address=%#x", end)}
|
||||||
|
|
||||||
|
if intelSyntax {
|
||||||
|
if b.isLLVMObjdump {
|
||||||
|
args = append(args, "--x86-asm-syntax=intel")
|
||||||
|
} else {
|
||||||
|
args = append(args, "-M", "intel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args, file)
|
||||||
|
cmd := exec.Command(b.objdump, args...)
|
||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%v: %v", cmd.Args, err)
|
return nil, fmt.Errorf("%v: %v", cmd.Args, err)
|
||||||
|
|
|
||||||
14
src/cmd/vendor/github.com/google/pprof/internal/binutils/disasm.go
generated
vendored
14
src/cmd/vendor/github.com/google/pprof/internal/binutils/disasm.go
generated
vendored
|
|
@ -25,10 +25,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
nmOutputRE = regexp.MustCompile(`^\s*([[:xdigit:]]+)\s+(.)\s+(.*)`)
|
nmOutputRE = regexp.MustCompile(`^\s*([[:xdigit:]]+)\s+(.)\s+(.*)`)
|
||||||
objdumpAsmOutputRE = regexp.MustCompile(`^\s*([[:xdigit:]]+):\s+(.*)`)
|
objdumpAsmOutputRE = regexp.MustCompile(`^\s*([[:xdigit:]]+):\s+(.*)`)
|
||||||
objdumpOutputFileLine = regexp.MustCompile(`^(.*):([0-9]+)`)
|
objdumpOutputFileLine = regexp.MustCompile(`^;?\s?(.*):([0-9]+)`)
|
||||||
objdumpOutputFunction = regexp.MustCompile(`^(\S.*)\(\):`)
|
objdumpOutputFunction = regexp.MustCompile(`^;?\s?(\S.*)\(\):`)
|
||||||
|
objdumpOutputFunctionLLVM = regexp.MustCompile(`^([[:xdigit:]]+)?\s?(.*):`)
|
||||||
)
|
)
|
||||||
|
|
||||||
func findSymbols(syms []byte, file string, r *regexp.Regexp, address uint64) ([]*plugin.Sym, error) {
|
func findSymbols(syms []byte, file string, r *regexp.Regexp, address uint64) ([]*plugin.Sym, error) {
|
||||||
|
|
@ -143,6 +144,11 @@ func disassemble(asm []byte) ([]plugin.Inst, error) {
|
||||||
if fields := objdumpOutputFunction.FindStringSubmatch(input); len(fields) == 2 {
|
if fields := objdumpOutputFunction.FindStringSubmatch(input); len(fields) == 2 {
|
||||||
function = fields[1]
|
function = fields[1]
|
||||||
continue
|
continue
|
||||||
|
} else {
|
||||||
|
if fields := objdumpOutputFunctionLLVM.FindStringSubmatch(input); len(fields) == 3 {
|
||||||
|
function = fields[2]
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Reset on unrecognized lines.
|
// Reset on unrecognized lines.
|
||||||
function, file, line = "", "", 0
|
function, file, line = "", "", 0
|
||||||
|
|
|
||||||
129
src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go
generated
vendored
129
src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go
generated
vendored
|
|
@ -69,8 +69,9 @@ func parseFlags(o *plugin.Options) (*source, []string, error) {
|
||||||
flagHTTP := flag.String("http", "", "Present interactive web UI at the specified http host:port")
|
flagHTTP := flag.String("http", "", "Present interactive web UI at the specified http host:port")
|
||||||
flagNoBrowser := flag.Bool("no_browser", false, "Skip opening a browswer for the interactive web UI")
|
flagNoBrowser := flag.Bool("no_browser", false, "Skip opening a browswer for the interactive web UI")
|
||||||
|
|
||||||
// Flags used during command processing
|
// Flags that set configuration properties.
|
||||||
installedFlags := installFlags(flag)
|
cfg := currentConfig()
|
||||||
|
configFlagSetter := installConfigFlags(flag, &cfg)
|
||||||
|
|
||||||
flagCommands := make(map[string]*bool)
|
flagCommands := make(map[string]*bool)
|
||||||
flagParamCommands := make(map[string]*string)
|
flagParamCommands := make(map[string]*string)
|
||||||
|
|
@ -107,8 +108,8 @@ func parseFlags(o *plugin.Options) (*source, []string, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report conflicting options
|
// Apply any specified flags to cfg.
|
||||||
if err := updateFlags(installedFlags); err != nil {
|
if err := configFlagSetter(); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -124,7 +125,7 @@ func parseFlags(o *plugin.Options) (*source, []string, error) {
|
||||||
return nil, nil, errors.New("-no_browser only makes sense with -http")
|
return nil, nil, errors.New("-no_browser only makes sense with -http")
|
||||||
}
|
}
|
||||||
|
|
||||||
si := pprofVariables["sample_index"].value
|
si := cfg.SampleIndex
|
||||||
si = sampleIndex(flagTotalDelay, si, "delay", "-total_delay", o.UI)
|
si = sampleIndex(flagTotalDelay, si, "delay", "-total_delay", o.UI)
|
||||||
si = sampleIndex(flagMeanDelay, si, "delay", "-mean_delay", o.UI)
|
si = sampleIndex(flagMeanDelay, si, "delay", "-mean_delay", o.UI)
|
||||||
si = sampleIndex(flagContentions, si, "contentions", "-contentions", o.UI)
|
si = sampleIndex(flagContentions, si, "contentions", "-contentions", o.UI)
|
||||||
|
|
@ -132,10 +133,10 @@ func parseFlags(o *plugin.Options) (*source, []string, error) {
|
||||||
si = sampleIndex(flagInUseObjects, si, "inuse_objects", "-inuse_objects", o.UI)
|
si = sampleIndex(flagInUseObjects, si, "inuse_objects", "-inuse_objects", o.UI)
|
||||||
si = sampleIndex(flagAllocSpace, si, "alloc_space", "-alloc_space", o.UI)
|
si = sampleIndex(flagAllocSpace, si, "alloc_space", "-alloc_space", o.UI)
|
||||||
si = sampleIndex(flagAllocObjects, si, "alloc_objects", "-alloc_objects", o.UI)
|
si = sampleIndex(flagAllocObjects, si, "alloc_objects", "-alloc_objects", o.UI)
|
||||||
pprofVariables.set("sample_index", si)
|
cfg.SampleIndex = si
|
||||||
|
|
||||||
if *flagMeanDelay {
|
if *flagMeanDelay {
|
||||||
pprofVariables.set("mean", "true")
|
cfg.Mean = true
|
||||||
}
|
}
|
||||||
|
|
||||||
source := &source{
|
source := &source{
|
||||||
|
|
@ -154,7 +155,7 @@ func parseFlags(o *plugin.Options) (*source, []string, error) {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
normalize := pprofVariables["normalize"].boolValue()
|
normalize := cfg.Normalize
|
||||||
if normalize && len(source.Base) == 0 {
|
if normalize && len(source.Base) == 0 {
|
||||||
return nil, nil, errors.New("must have base profile to normalize by")
|
return nil, nil, errors.New("must have base profile to normalize by")
|
||||||
}
|
}
|
||||||
|
|
@ -163,6 +164,8 @@ func parseFlags(o *plugin.Options) (*source, []string, error) {
|
||||||
if bu, ok := o.Obj.(*binutils.Binutils); ok {
|
if bu, ok := o.Obj.(*binutils.Binutils); ok {
|
||||||
bu.SetTools(*flagTools)
|
bu.SetTools(*flagTools)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setCurrentConfig(cfg)
|
||||||
return source, cmd, nil
|
return source, cmd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -194,66 +197,72 @@ func dropEmpty(list []*string) []string {
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
// installFlags creates command line flags for pprof variables.
|
// installConfigFlags creates command line flags for configuration
|
||||||
func installFlags(flag plugin.FlagSet) flagsInstalled {
|
// fields and returns a function which can be called after flags have
|
||||||
f := flagsInstalled{
|
// been parsed to copy any flags specified on the command line to
|
||||||
ints: make(map[string]*int),
|
// *cfg.
|
||||||
bools: make(map[string]*bool),
|
func installConfigFlags(flag plugin.FlagSet, cfg *config) func() error {
|
||||||
floats: make(map[string]*float64),
|
// List of functions for setting the different parts of a config.
|
||||||
strings: make(map[string]*string),
|
var setters []func()
|
||||||
}
|
var err error // Holds any errors encountered while running setters.
|
||||||
for n, v := range pprofVariables {
|
|
||||||
switch v.kind {
|
for _, field := range configFields {
|
||||||
case boolKind:
|
n := field.name
|
||||||
if v.group != "" {
|
help := configHelp[n]
|
||||||
// Set all radio variables to false to identify conflicts.
|
var setter func()
|
||||||
f.bools[n] = flag.Bool(n, false, v.help)
|
switch ptr := cfg.fieldPtr(field).(type) {
|
||||||
|
case *bool:
|
||||||
|
f := flag.Bool(n, *ptr, help)
|
||||||
|
setter = func() { *ptr = *f }
|
||||||
|
case *int:
|
||||||
|
f := flag.Int(n, *ptr, help)
|
||||||
|
setter = func() { *ptr = *f }
|
||||||
|
case *float64:
|
||||||
|
f := flag.Float64(n, *ptr, help)
|
||||||
|
setter = func() { *ptr = *f }
|
||||||
|
case *string:
|
||||||
|
if len(field.choices) == 0 {
|
||||||
|
f := flag.String(n, *ptr, help)
|
||||||
|
setter = func() { *ptr = *f }
|
||||||
} else {
|
} else {
|
||||||
f.bools[n] = flag.Bool(n, v.boolValue(), v.help)
|
// Make a separate flag per possible choice.
|
||||||
|
// Set all flags to initially false so we can
|
||||||
|
// identify conflicts.
|
||||||
|
bools := make(map[string]*bool)
|
||||||
|
for _, choice := range field.choices {
|
||||||
|
bools[choice] = flag.Bool(choice, false, configHelp[choice])
|
||||||
|
}
|
||||||
|
setter = func() {
|
||||||
|
var set []string
|
||||||
|
for k, v := range bools {
|
||||||
|
if *v {
|
||||||
|
set = append(set, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch len(set) {
|
||||||
|
case 0:
|
||||||
|
// Leave as default value.
|
||||||
|
case 1:
|
||||||
|
*ptr = set[0]
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("conflicting options set: %v", set)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case intKind:
|
|
||||||
f.ints[n] = flag.Int(n, v.intValue(), v.help)
|
|
||||||
case floatKind:
|
|
||||||
f.floats[n] = flag.Float64(n, v.floatValue(), v.help)
|
|
||||||
case stringKind:
|
|
||||||
f.strings[n] = flag.String(n, v.value, v.help)
|
|
||||||
}
|
}
|
||||||
|
setters = append(setters, setter)
|
||||||
}
|
}
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// updateFlags updates the pprof variables according to the flags
|
return func() error {
|
||||||
// parsed in the command line.
|
// Apply the setter for every flag.
|
||||||
func updateFlags(f flagsInstalled) error {
|
for _, setter := range setters {
|
||||||
vars := pprofVariables
|
setter()
|
||||||
groups := map[string]string{}
|
if err != nil {
|
||||||
for n, v := range f.bools {
|
return err
|
||||||
vars.set(n, fmt.Sprint(*v))
|
|
||||||
if *v {
|
|
||||||
g := vars[n].group
|
|
||||||
if g != "" && groups[g] != "" {
|
|
||||||
return fmt.Errorf("conflicting options %q and %q set", n, groups[g])
|
|
||||||
}
|
}
|
||||||
groups[g] = n
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
for n, v := range f.ints {
|
|
||||||
vars.set(n, fmt.Sprint(*v))
|
|
||||||
}
|
|
||||||
for n, v := range f.floats {
|
|
||||||
vars.set(n, fmt.Sprint(*v))
|
|
||||||
}
|
|
||||||
for n, v := range f.strings {
|
|
||||||
vars.set(n, *v)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type flagsInstalled struct {
|
|
||||||
ints map[string]*int
|
|
||||||
bools map[string]*bool
|
|
||||||
floats map[string]*float64
|
|
||||||
strings map[string]*string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// isBuildID determines if the profile may contain a build ID, by
|
// isBuildID determines if the profile may contain a build ID, by
|
||||||
|
|
|
||||||
281
src/cmd/vendor/github.com/google/pprof/internal/driver/commands.go
generated
vendored
281
src/cmd/vendor/github.com/google/pprof/internal/driver/commands.go
generated
vendored
|
|
@ -22,7 +22,6 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -70,9 +69,7 @@ func AddCommand(cmd string, format int, post PostProcessor, desc, usage string)
|
||||||
// SetVariableDefault sets the default value for a pprof
|
// SetVariableDefault sets the default value for a pprof
|
||||||
// variable. This enables extensions to set their own defaults.
|
// variable. This enables extensions to set their own defaults.
|
||||||
func SetVariableDefault(variable, value string) {
|
func SetVariableDefault(variable, value string) {
|
||||||
if v := pprofVariables[variable]; v != nil {
|
configure(variable, value)
|
||||||
v.value = value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostProcessor is a function that applies post-processing to the report output
|
// PostProcessor is a function that applies post-processing to the report output
|
||||||
|
|
@ -124,130 +121,132 @@ var pprofCommands = commands{
|
||||||
"weblist": {report.WebList, nil, invokeVisualizer("html", browsers()), true, "Display annotated source in a web browser", listHelp("weblist", false)},
|
"weblist": {report.WebList, nil, invokeVisualizer("html", browsers()), true, "Display annotated source in a web browser", listHelp("weblist", false)},
|
||||||
}
|
}
|
||||||
|
|
||||||
// pprofVariables are the configuration parameters that affect the
|
// configHelp contains help text per configuration parameter.
|
||||||
// reported generated by pprof.
|
var configHelp = map[string]string{
|
||||||
var pprofVariables = variables{
|
|
||||||
// Filename for file-based output formats, stdout by default.
|
// Filename for file-based output formats, stdout by default.
|
||||||
"output": &variable{stringKind, "", "", helpText("Output filename for file-based outputs")},
|
"output": helpText("Output filename for file-based outputs"),
|
||||||
|
|
||||||
// Comparisons.
|
// Comparisons.
|
||||||
"drop_negative": &variable{boolKind, "f", "", helpText(
|
"drop_negative": helpText(
|
||||||
"Ignore negative differences",
|
"Ignore negative differences",
|
||||||
"Do not show any locations with values <0.")},
|
"Do not show any locations with values <0."),
|
||||||
|
|
||||||
// Graph handling options.
|
// Graph handling options.
|
||||||
"call_tree": &variable{boolKind, "f", "", helpText(
|
"call_tree": helpText(
|
||||||
"Create a context-sensitive call tree",
|
"Create a context-sensitive call tree",
|
||||||
"Treat locations reached through different paths as separate.")},
|
"Treat locations reached through different paths as separate."),
|
||||||
|
|
||||||
// Display options.
|
// Display options.
|
||||||
"relative_percentages": &variable{boolKind, "f", "", helpText(
|
"relative_percentages": helpText(
|
||||||
"Show percentages relative to focused subgraph",
|
"Show percentages relative to focused subgraph",
|
||||||
"If unset, percentages are relative to full graph before focusing",
|
"If unset, percentages are relative to full graph before focusing",
|
||||||
"to facilitate comparison with original graph.")},
|
"to facilitate comparison with original graph."),
|
||||||
"unit": &variable{stringKind, "minimum", "", helpText(
|
"unit": helpText(
|
||||||
"Measurement units to display",
|
"Measurement units to display",
|
||||||
"Scale the sample values to this unit.",
|
"Scale the sample values to this unit.",
|
||||||
"For time-based profiles, use seconds, milliseconds, nanoseconds, etc.",
|
"For time-based profiles, use seconds, milliseconds, nanoseconds, etc.",
|
||||||
"For memory profiles, use megabytes, kilobytes, bytes, etc.",
|
"For memory profiles, use megabytes, kilobytes, bytes, etc.",
|
||||||
"Using auto will scale each value independently to the most natural unit.")},
|
"Using auto will scale each value independently to the most natural unit."),
|
||||||
"compact_labels": &variable{boolKind, "f", "", "Show minimal headers"},
|
"compact_labels": "Show minimal headers",
|
||||||
"source_path": &variable{stringKind, "", "", "Search path for source files"},
|
"source_path": "Search path for source files",
|
||||||
"trim_path": &variable{stringKind, "", "", "Path to trim from source paths before search"},
|
"trim_path": "Path to trim from source paths before search",
|
||||||
|
"intel_syntax": helpText(
|
||||||
|
"Show assembly in Intel syntax",
|
||||||
|
"Only applicable to commands `disasm` and `weblist`"),
|
||||||
|
|
||||||
// Filtering options
|
// Filtering options
|
||||||
"nodecount": &variable{intKind, "-1", "", helpText(
|
"nodecount": helpText(
|
||||||
"Max number of nodes to show",
|
"Max number of nodes to show",
|
||||||
"Uses heuristics to limit the number of locations to be displayed.",
|
"Uses heuristics to limit the number of locations to be displayed.",
|
||||||
"On graphs, dotted edges represent paths through nodes that have been removed.")},
|
"On graphs, dotted edges represent paths through nodes that have been removed."),
|
||||||
"nodefraction": &variable{floatKind, "0.005", "", "Hide nodes below <f>*total"},
|
"nodefraction": "Hide nodes below <f>*total",
|
||||||
"edgefraction": &variable{floatKind, "0.001", "", "Hide edges below <f>*total"},
|
"edgefraction": "Hide edges below <f>*total",
|
||||||
"trim": &variable{boolKind, "t", "", helpText(
|
"trim": helpText(
|
||||||
"Honor nodefraction/edgefraction/nodecount defaults",
|
"Honor nodefraction/edgefraction/nodecount defaults",
|
||||||
"Set to false to get the full profile, without any trimming.")},
|
"Set to false to get the full profile, without any trimming."),
|
||||||
"focus": &variable{stringKind, "", "", helpText(
|
"focus": helpText(
|
||||||
"Restricts to samples going through a node matching regexp",
|
"Restricts to samples going through a node matching regexp",
|
||||||
"Discard samples that do not include a node matching this regexp.",
|
"Discard samples that do not include a node matching this regexp.",
|
||||||
"Matching includes the function name, filename or object name.")},
|
"Matching includes the function name, filename or object name."),
|
||||||
"ignore": &variable{stringKind, "", "", helpText(
|
"ignore": helpText(
|
||||||
"Skips paths going through any nodes matching regexp",
|
"Skips paths going through any nodes matching regexp",
|
||||||
"If set, discard samples that include a node matching this regexp.",
|
"If set, discard samples that include a node matching this regexp.",
|
||||||
"Matching includes the function name, filename or object name.")},
|
"Matching includes the function name, filename or object name."),
|
||||||
"prune_from": &variable{stringKind, "", "", helpText(
|
"prune_from": helpText(
|
||||||
"Drops any functions below the matched frame.",
|
"Drops any functions below the matched frame.",
|
||||||
"If set, any frames matching the specified regexp and any frames",
|
"If set, any frames matching the specified regexp and any frames",
|
||||||
"below it will be dropped from each sample.")},
|
"below it will be dropped from each sample."),
|
||||||
"hide": &variable{stringKind, "", "", helpText(
|
"hide": helpText(
|
||||||
"Skips nodes matching regexp",
|
"Skips nodes matching regexp",
|
||||||
"Discard nodes that match this location.",
|
"Discard nodes that match this location.",
|
||||||
"Other nodes from samples that include this location will be shown.",
|
"Other nodes from samples that include this location will be shown.",
|
||||||
"Matching includes the function name, filename or object name.")},
|
"Matching includes the function name, filename or object name."),
|
||||||
"show": &variable{stringKind, "", "", helpText(
|
"show": helpText(
|
||||||
"Only show nodes matching regexp",
|
"Only show nodes matching regexp",
|
||||||
"If set, only show nodes that match this location.",
|
"If set, only show nodes that match this location.",
|
||||||
"Matching includes the function name, filename or object name.")},
|
"Matching includes the function name, filename or object name."),
|
||||||
"show_from": &variable{stringKind, "", "", helpText(
|
"show_from": helpText(
|
||||||
"Drops functions above the highest matched frame.",
|
"Drops functions above the highest matched frame.",
|
||||||
"If set, all frames above the highest match are dropped from every sample.",
|
"If set, all frames above the highest match are dropped from every sample.",
|
||||||
"Matching includes the function name, filename or object name.")},
|
"Matching includes the function name, filename or object name."),
|
||||||
"tagfocus": &variable{stringKind, "", "", helpText(
|
"tagfocus": helpText(
|
||||||
"Restricts to samples with tags in range or matched by regexp",
|
"Restricts to samples with tags in range or matched by regexp",
|
||||||
"Use name=value syntax to limit the matching to a specific tag.",
|
"Use name=value syntax to limit the matching to a specific tag.",
|
||||||
"Numeric tag filter examples: 1kb, 1kb:10kb, memory=32mb:",
|
"Numeric tag filter examples: 1kb, 1kb:10kb, memory=32mb:",
|
||||||
"String tag filter examples: foo, foo.*bar, mytag=foo.*bar")},
|
"String tag filter examples: foo, foo.*bar, mytag=foo.*bar"),
|
||||||
"tagignore": &variable{stringKind, "", "", helpText(
|
"tagignore": helpText(
|
||||||
"Discard samples with tags in range or matched by regexp",
|
"Discard samples with tags in range or matched by regexp",
|
||||||
"Use name=value syntax to limit the matching to a specific tag.",
|
"Use name=value syntax to limit the matching to a specific tag.",
|
||||||
"Numeric tag filter examples: 1kb, 1kb:10kb, memory=32mb:",
|
"Numeric tag filter examples: 1kb, 1kb:10kb, memory=32mb:",
|
||||||
"String tag filter examples: foo, foo.*bar, mytag=foo.*bar")},
|
"String tag filter examples: foo, foo.*bar, mytag=foo.*bar"),
|
||||||
"tagshow": &variable{stringKind, "", "", helpText(
|
"tagshow": helpText(
|
||||||
"Only consider tags matching this regexp",
|
"Only consider tags matching this regexp",
|
||||||
"Discard tags that do not match this regexp")},
|
"Discard tags that do not match this regexp"),
|
||||||
"taghide": &variable{stringKind, "", "", helpText(
|
"taghide": helpText(
|
||||||
"Skip tags matching this regexp",
|
"Skip tags matching this regexp",
|
||||||
"Discard tags that match this regexp")},
|
"Discard tags that match this regexp"),
|
||||||
// Heap profile options
|
// Heap profile options
|
||||||
"divide_by": &variable{floatKind, "1", "", helpText(
|
"divide_by": helpText(
|
||||||
"Ratio to divide all samples before visualization",
|
"Ratio to divide all samples before visualization",
|
||||||
"Divide all samples values by a constant, eg the number of processors or jobs.")},
|
"Divide all samples values by a constant, eg the number of processors or jobs."),
|
||||||
"mean": &variable{boolKind, "f", "", helpText(
|
"mean": helpText(
|
||||||
"Average sample value over first value (count)",
|
"Average sample value over first value (count)",
|
||||||
"For memory profiles, report average memory per allocation.",
|
"For memory profiles, report average memory per allocation.",
|
||||||
"For time-based profiles, report average time per event.")},
|
"For time-based profiles, report average time per event."),
|
||||||
"sample_index": &variable{stringKind, "", "", helpText(
|
"sample_index": helpText(
|
||||||
"Sample value to report (0-based index or name)",
|
"Sample value to report (0-based index or name)",
|
||||||
"Profiles contain multiple values per sample.",
|
"Profiles contain multiple values per sample.",
|
||||||
"Use sample_index=i to select the ith value (starting at 0).")},
|
"Use sample_index=i to select the ith value (starting at 0)."),
|
||||||
"normalize": &variable{boolKind, "f", "", helpText(
|
"normalize": helpText(
|
||||||
"Scales profile based on the base profile.")},
|
"Scales profile based on the base profile."),
|
||||||
|
|
||||||
// Data sorting criteria
|
// Data sorting criteria
|
||||||
"flat": &variable{boolKind, "t", "cumulative", helpText("Sort entries based on own weight")},
|
"flat": helpText("Sort entries based on own weight"),
|
||||||
"cum": &variable{boolKind, "f", "cumulative", helpText("Sort entries based on cumulative weight")},
|
"cum": helpText("Sort entries based on cumulative weight"),
|
||||||
|
|
||||||
// Output granularity
|
// Output granularity
|
||||||
"functions": &variable{boolKind, "t", "granularity", helpText(
|
"functions": helpText(
|
||||||
"Aggregate at the function level.",
|
"Aggregate at the function level.",
|
||||||
"Ignores the filename where the function was defined.")},
|
"Ignores the filename where the function was defined."),
|
||||||
"filefunctions": &variable{boolKind, "t", "granularity", helpText(
|
"filefunctions": helpText(
|
||||||
"Aggregate at the function level.",
|
"Aggregate at the function level.",
|
||||||
"Takes into account the filename where the function was defined.")},
|
"Takes into account the filename where the function was defined."),
|
||||||
"files": &variable{boolKind, "f", "granularity", "Aggregate at the file level."},
|
"files": "Aggregate at the file level.",
|
||||||
"lines": &variable{boolKind, "f", "granularity", "Aggregate at the source code line level."},
|
"lines": "Aggregate at the source code line level.",
|
||||||
"addresses": &variable{boolKind, "f", "granularity", helpText(
|
"addresses": helpText(
|
||||||
"Aggregate at the address level.",
|
"Aggregate at the address level.",
|
||||||
"Includes functions' addresses in the output.")},
|
"Includes functions' addresses in the output."),
|
||||||
"noinlines": &variable{boolKind, "f", "", helpText(
|
"noinlines": helpText(
|
||||||
"Ignore inlines.",
|
"Ignore inlines.",
|
||||||
"Attributes inlined functions to their first out-of-line caller.")},
|
"Attributes inlined functions to their first out-of-line caller."),
|
||||||
}
|
}
|
||||||
|
|
||||||
func helpText(s ...string) string {
|
func helpText(s ...string) string {
|
||||||
return strings.Join(s, "\n") + "\n"
|
return strings.Join(s, "\n") + "\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
// usage returns a string describing the pprof commands and variables.
|
// usage returns a string describing the pprof commands and configuration
|
||||||
// if commandLine is set, the output reflect cli usage.
|
// options. if commandLine is set, the output reflect cli usage.
|
||||||
func usage(commandLine bool) string {
|
func usage(commandLine bool) string {
|
||||||
var prefix string
|
var prefix string
|
||||||
if commandLine {
|
if commandLine {
|
||||||
|
|
@ -269,40 +268,33 @@ func usage(commandLine bool) string {
|
||||||
} else {
|
} else {
|
||||||
help = " Commands:\n"
|
help = " Commands:\n"
|
||||||
commands = append(commands, fmtHelp("o/options", "List options and their current values"))
|
commands = append(commands, fmtHelp("o/options", "List options and their current values"))
|
||||||
commands = append(commands, fmtHelp("quit/exit/^D", "Exit pprof"))
|
commands = append(commands, fmtHelp("q/quit/exit/^D", "Exit pprof"))
|
||||||
}
|
}
|
||||||
|
|
||||||
help = help + strings.Join(commands, "\n") + "\n\n" +
|
help = help + strings.Join(commands, "\n") + "\n\n" +
|
||||||
" Options:\n"
|
" Options:\n"
|
||||||
|
|
||||||
// Print help for variables after sorting them.
|
// Print help for configuration options after sorting them.
|
||||||
// Collect radio variables by their group name to print them together.
|
// Collect choices for multi-choice options print them together.
|
||||||
radioOptions := make(map[string][]string)
|
|
||||||
var variables []string
|
var variables []string
|
||||||
for name, vr := range pprofVariables {
|
var radioStrings []string
|
||||||
if vr.group != "" {
|
for _, f := range configFields {
|
||||||
radioOptions[vr.group] = append(radioOptions[vr.group], name)
|
if len(f.choices) == 0 {
|
||||||
|
variables = append(variables, fmtHelp(prefix+f.name, configHelp[f.name]))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
variables = append(variables, fmtHelp(prefix+name, vr.help))
|
// Format help for for this group.
|
||||||
}
|
s := []string{fmtHelp(f.name, "")}
|
||||||
sort.Strings(variables)
|
for _, choice := range f.choices {
|
||||||
|
s = append(s, " "+fmtHelp(prefix+choice, configHelp[choice]))
|
||||||
help = help + strings.Join(variables, "\n") + "\n\n" +
|
|
||||||
" Option groups (only set one per group):\n"
|
|
||||||
|
|
||||||
var radioStrings []string
|
|
||||||
for radio, ops := range radioOptions {
|
|
||||||
sort.Strings(ops)
|
|
||||||
s := []string{fmtHelp(radio, "")}
|
|
||||||
for _, op := range ops {
|
|
||||||
s = append(s, " "+fmtHelp(prefix+op, pprofVariables[op].help))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
radioStrings = append(radioStrings, strings.Join(s, "\n"))
|
radioStrings = append(radioStrings, strings.Join(s, "\n"))
|
||||||
}
|
}
|
||||||
|
sort.Strings(variables)
|
||||||
sort.Strings(radioStrings)
|
sort.Strings(radioStrings)
|
||||||
return help + strings.Join(radioStrings, "\n")
|
return help + strings.Join(variables, "\n") + "\n\n" +
|
||||||
|
" Option groups (only set one per group):\n" +
|
||||||
|
strings.Join(radioStrings, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func reportHelp(c string, cum, redirect bool) string {
|
func reportHelp(c string, cum, redirect bool) string {
|
||||||
|
|
@ -445,105 +437,8 @@ func invokeVisualizer(suffix string, visualizers []string) PostProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// variables describe the configuration parameters recognized by pprof.
|
// stringToBool is a custom parser for bools. We avoid using strconv.ParseBool
|
||||||
type variables map[string]*variable
|
// to remain compatible with old pprof behavior (e.g., treating "" as true).
|
||||||
|
|
||||||
// variable is a single configuration parameter.
|
|
||||||
type variable struct {
|
|
||||||
kind int // How to interpret the value, must be one of the enums below.
|
|
||||||
value string // Effective value. Only values appropriate for the Kind should be set.
|
|
||||||
group string // boolKind variables with the same Group != "" cannot be set simultaneously.
|
|
||||||
help string // Text describing the variable, in multiple lines separated by newline.
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// variable.kind must be one of these variables.
|
|
||||||
boolKind = iota
|
|
||||||
intKind
|
|
||||||
floatKind
|
|
||||||
stringKind
|
|
||||||
)
|
|
||||||
|
|
||||||
// set updates the value of a variable, checking that the value is
|
|
||||||
// suitable for the variable Kind.
|
|
||||||
func (vars variables) set(name, value string) error {
|
|
||||||
v := vars[name]
|
|
||||||
if v == nil {
|
|
||||||
return fmt.Errorf("no variable %s", name)
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
switch v.kind {
|
|
||||||
case boolKind:
|
|
||||||
var b bool
|
|
||||||
if b, err = stringToBool(value); err == nil {
|
|
||||||
if v.group != "" && !b {
|
|
||||||
err = fmt.Errorf("%q can only be set to true", name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case intKind:
|
|
||||||
_, err = strconv.Atoi(value)
|
|
||||||
case floatKind:
|
|
||||||
_, err = strconv.ParseFloat(value, 64)
|
|
||||||
case stringKind:
|
|
||||||
// Remove quotes, particularly useful for empty values.
|
|
||||||
if len(value) > 1 && strings.HasPrefix(value, `"`) && strings.HasSuffix(value, `"`) {
|
|
||||||
value = value[1 : len(value)-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
vars[name].value = value
|
|
||||||
if group := vars[name].group; group != "" {
|
|
||||||
for vname, vvar := range vars {
|
|
||||||
if vvar.group == group && vname != name {
|
|
||||||
vvar.value = "f"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// boolValue returns the value of a boolean variable.
|
|
||||||
func (v *variable) boolValue() bool {
|
|
||||||
b, err := stringToBool(v.value)
|
|
||||||
if err != nil {
|
|
||||||
panic("unexpected value " + v.value + " for bool ")
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// intValue returns the value of an intKind variable.
|
|
||||||
func (v *variable) intValue() int {
|
|
||||||
i, err := strconv.Atoi(v.value)
|
|
||||||
if err != nil {
|
|
||||||
panic("unexpected value " + v.value + " for int ")
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
// floatValue returns the value of a Float variable.
|
|
||||||
func (v *variable) floatValue() float64 {
|
|
||||||
f, err := strconv.ParseFloat(v.value, 64)
|
|
||||||
if err != nil {
|
|
||||||
panic("unexpected value " + v.value + " for float ")
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// stringValue returns a canonical representation for a variable.
|
|
||||||
func (v *variable) stringValue() string {
|
|
||||||
switch v.kind {
|
|
||||||
case boolKind:
|
|
||||||
return fmt.Sprint(v.boolValue())
|
|
||||||
case intKind:
|
|
||||||
return fmt.Sprint(v.intValue())
|
|
||||||
case floatKind:
|
|
||||||
return fmt.Sprint(v.floatValue())
|
|
||||||
}
|
|
||||||
return v.value
|
|
||||||
}
|
|
||||||
|
|
||||||
func stringToBool(s string) (bool, error) {
|
func stringToBool(s string) (bool, error) {
|
||||||
switch strings.ToLower(s) {
|
switch strings.ToLower(s) {
|
||||||
case "true", "t", "yes", "y", "1", "":
|
case "true", "t", "yes", "y", "1", "":
|
||||||
|
|
@ -554,13 +449,3 @@ func stringToBool(s string) (bool, error) {
|
||||||
return false, fmt.Errorf(`illegal value "%s" for bool variable`, s)
|
return false, fmt.Errorf(`illegal value "%s" for bool variable`, s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeCopy returns a duplicate of a set of shell variables.
|
|
||||||
func (vars variables) makeCopy() variables {
|
|
||||||
varscopy := make(variables, len(vars))
|
|
||||||
for n, v := range vars {
|
|
||||||
vcopy := *v
|
|
||||||
varscopy[n] = &vcopy
|
|
||||||
}
|
|
||||||
return varscopy
|
|
||||||
}
|
|
||||||
|
|
|
||||||
367
src/cmd/vendor/github.com/google/pprof/internal/driver/config.go
generated
vendored
Normal file
367
src/cmd/vendor/github.com/google/pprof/internal/driver/config.go
generated
vendored
Normal file
|
|
@ -0,0 +1,367 @@
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// config holds settings for a single named config.
|
||||||
|
// The JSON tag name for a field is used both for JSON encoding and as
|
||||||
|
// a named variable.
|
||||||
|
type config struct {
|
||||||
|
// Filename for file-based output formats, stdout by default.
|
||||||
|
Output string `json:"-"`
|
||||||
|
|
||||||
|
// Display options.
|
||||||
|
CallTree bool `json:"call_tree,omitempty"`
|
||||||
|
RelativePercentages bool `json:"relative_percentages,omitempty"`
|
||||||
|
Unit string `json:"unit,omitempty"`
|
||||||
|
CompactLabels bool `json:"compact_labels,omitempty"`
|
||||||
|
SourcePath string `json:"-"`
|
||||||
|
TrimPath string `json:"-"`
|
||||||
|
IntelSyntax bool `json:"intel_syntax,omitempty"`
|
||||||
|
Mean bool `json:"mean,omitempty"`
|
||||||
|
SampleIndex string `json:"-"`
|
||||||
|
DivideBy float64 `json:"-"`
|
||||||
|
Normalize bool `json:"normalize,omitempty"`
|
||||||
|
Sort string `json:"sort,omitempty"`
|
||||||
|
|
||||||
|
// Filtering options
|
||||||
|
DropNegative bool `json:"drop_negative,omitempty"`
|
||||||
|
NodeCount int `json:"nodecount,omitempty"`
|
||||||
|
NodeFraction float64 `json:"nodefraction,omitempty"`
|
||||||
|
EdgeFraction float64 `json:"edgefraction,omitempty"`
|
||||||
|
Trim bool `json:"trim,omitempty"`
|
||||||
|
Focus string `json:"focus,omitempty"`
|
||||||
|
Ignore string `json:"ignore,omitempty"`
|
||||||
|
PruneFrom string `json:"prune_from,omitempty"`
|
||||||
|
Hide string `json:"hide,omitempty"`
|
||||||
|
Show string `json:"show,omitempty"`
|
||||||
|
ShowFrom string `json:"show_from,omitempty"`
|
||||||
|
TagFocus string `json:"tagfocus,omitempty"`
|
||||||
|
TagIgnore string `json:"tagignore,omitempty"`
|
||||||
|
TagShow string `json:"tagshow,omitempty"`
|
||||||
|
TagHide string `json:"taghide,omitempty"`
|
||||||
|
NoInlines bool `json:"noinlines,omitempty"`
|
||||||
|
|
||||||
|
// Output granularity
|
||||||
|
Granularity string `json:"granularity,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultConfig returns the default configuration values; it is unaffected by
|
||||||
|
// flags and interactive assignments.
|
||||||
|
func defaultConfig() config {
|
||||||
|
return config{
|
||||||
|
Unit: "minimum",
|
||||||
|
NodeCount: -1,
|
||||||
|
NodeFraction: 0.005,
|
||||||
|
EdgeFraction: 0.001,
|
||||||
|
Trim: true,
|
||||||
|
DivideBy: 1.0,
|
||||||
|
Sort: "flat",
|
||||||
|
Granularity: "functions",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// currentConfig holds the current configuration values; it is affected by
|
||||||
|
// flags and interactive assignments.
|
||||||
|
var currentCfg = defaultConfig()
|
||||||
|
var currentMu sync.Mutex
|
||||||
|
|
||||||
|
func currentConfig() config {
|
||||||
|
currentMu.Lock()
|
||||||
|
defer currentMu.Unlock()
|
||||||
|
return currentCfg
|
||||||
|
}
|
||||||
|
|
||||||
|
func setCurrentConfig(cfg config) {
|
||||||
|
currentMu.Lock()
|
||||||
|
defer currentMu.Unlock()
|
||||||
|
currentCfg = cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
// configField contains metadata for a single configuration field.
|
||||||
|
type configField struct {
|
||||||
|
name string // JSON field name/key in variables
|
||||||
|
urlparam string // URL parameter name
|
||||||
|
saved bool // Is field saved in settings?
|
||||||
|
field reflect.StructField // Field in config
|
||||||
|
choices []string // Name Of variables in group
|
||||||
|
defaultValue string // Default value for this field.
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
configFields []configField // Precomputed metadata per config field
|
||||||
|
|
||||||
|
// configFieldMap holds an entry for every config field as well as an
|
||||||
|
// entry for every valid choice for a multi-choice field.
|
||||||
|
configFieldMap map[string]configField
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Config names for fields that are not saved in settings and therefore
|
||||||
|
// do not have a JSON name.
|
||||||
|
notSaved := map[string]string{
|
||||||
|
// Not saved in settings, but present in URLs.
|
||||||
|
"SampleIndex": "sample_index",
|
||||||
|
|
||||||
|
// Following fields are also not placed in URLs.
|
||||||
|
"Output": "output",
|
||||||
|
"SourcePath": "source_path",
|
||||||
|
"TrimPath": "trim_path",
|
||||||
|
"DivideBy": "divide_by",
|
||||||
|
}
|
||||||
|
|
||||||
|
// choices holds the list of allowed values for config fields that can
|
||||||
|
// take on one of a bounded set of values.
|
||||||
|
choices := map[string][]string{
|
||||||
|
"sort": {"cum", "flat"},
|
||||||
|
"granularity": {"functions", "filefunctions", "files", "lines", "addresses"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// urlparam holds the mapping from a config field name to the URL
|
||||||
|
// parameter used to hold that config field. If no entry is present for
|
||||||
|
// a name, the corresponding field is not saved in URLs.
|
||||||
|
urlparam := map[string]string{
|
||||||
|
"drop_negative": "dropneg",
|
||||||
|
"call_tree": "calltree",
|
||||||
|
"relative_percentages": "rel",
|
||||||
|
"unit": "unit",
|
||||||
|
"compact_labels": "compact",
|
||||||
|
"intel_syntax": "intel",
|
||||||
|
"nodecount": "n",
|
||||||
|
"nodefraction": "nf",
|
||||||
|
"edgefraction": "ef",
|
||||||
|
"trim": "trim",
|
||||||
|
"focus": "f",
|
||||||
|
"ignore": "i",
|
||||||
|
"prune_from": "prunefrom",
|
||||||
|
"hide": "h",
|
||||||
|
"show": "s",
|
||||||
|
"show_from": "sf",
|
||||||
|
"tagfocus": "tf",
|
||||||
|
"tagignore": "ti",
|
||||||
|
"tagshow": "ts",
|
||||||
|
"taghide": "th",
|
||||||
|
"mean": "mean",
|
||||||
|
"sample_index": "si",
|
||||||
|
"normalize": "norm",
|
||||||
|
"sort": "sort",
|
||||||
|
"granularity": "g",
|
||||||
|
"noinlines": "noinlines",
|
||||||
|
}
|
||||||
|
|
||||||
|
def := defaultConfig()
|
||||||
|
configFieldMap = map[string]configField{}
|
||||||
|
t := reflect.TypeOf(config{})
|
||||||
|
for i, n := 0, t.NumField(); i < n; i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
js := strings.Split(field.Tag.Get("json"), ",")
|
||||||
|
if len(js) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Get the configuration name for this field.
|
||||||
|
name := js[0]
|
||||||
|
if name == "-" {
|
||||||
|
name = notSaved[field.Name]
|
||||||
|
if name == "" {
|
||||||
|
// Not a configurable field.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f := configField{
|
||||||
|
name: name,
|
||||||
|
urlparam: urlparam[name],
|
||||||
|
saved: (name == js[0]),
|
||||||
|
field: field,
|
||||||
|
choices: choices[name],
|
||||||
|
}
|
||||||
|
f.defaultValue = def.get(f)
|
||||||
|
configFields = append(configFields, f)
|
||||||
|
configFieldMap[f.name] = f
|
||||||
|
for _, choice := range f.choices {
|
||||||
|
configFieldMap[choice] = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fieldPtr returns a pointer to the field identified by f in *cfg.
|
||||||
|
func (cfg *config) fieldPtr(f configField) interface{} {
|
||||||
|
// reflect.ValueOf: converts to reflect.Value
|
||||||
|
// Elem: dereferences cfg to make *cfg
|
||||||
|
// FieldByIndex: fetches the field
|
||||||
|
// Addr: takes address of field
|
||||||
|
// Interface: converts back from reflect.Value to a regular value
|
||||||
|
return reflect.ValueOf(cfg).Elem().FieldByIndex(f.field.Index).Addr().Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
// get returns the value of field f in cfg.
|
||||||
|
func (cfg *config) get(f configField) string {
|
||||||
|
switch ptr := cfg.fieldPtr(f).(type) {
|
||||||
|
case *string:
|
||||||
|
return *ptr
|
||||||
|
case *int:
|
||||||
|
return fmt.Sprint(*ptr)
|
||||||
|
case *float64:
|
||||||
|
return fmt.Sprint(*ptr)
|
||||||
|
case *bool:
|
||||||
|
return fmt.Sprint(*ptr)
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("unsupported config field type %v", f.field.Type))
|
||||||
|
}
|
||||||
|
|
||||||
|
// set sets the value of field f in cfg to value.
|
||||||
|
func (cfg *config) set(f configField, value string) error {
|
||||||
|
switch ptr := cfg.fieldPtr(f).(type) {
|
||||||
|
case *string:
|
||||||
|
if len(f.choices) > 0 {
|
||||||
|
// Verify that value is one of the allowed choices.
|
||||||
|
for _, choice := range f.choices {
|
||||||
|
if choice == value {
|
||||||
|
*ptr = value
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("invalid %q value %q", f.name, value)
|
||||||
|
}
|
||||||
|
*ptr = value
|
||||||
|
case *int:
|
||||||
|
v, err := strconv.Atoi(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*ptr = v
|
||||||
|
case *float64:
|
||||||
|
v, err := strconv.ParseFloat(value, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*ptr = v
|
||||||
|
case *bool:
|
||||||
|
v, err := stringToBool(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*ptr = v
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unsupported config field type %v", f.field.Type))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isConfigurable returns true if name is either the name of a config field, or
|
||||||
|
// a valid value for a multi-choice config field.
|
||||||
|
func isConfigurable(name string) bool {
|
||||||
|
_, ok := configFieldMap[name]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// isBoolConfig returns true if name is either name of a boolean config field,
|
||||||
|
// or a valid value for a multi-choice config field.
|
||||||
|
func isBoolConfig(name string) bool {
|
||||||
|
f, ok := configFieldMap[name]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if name != f.name {
|
||||||
|
return true // name must be one possible value for the field
|
||||||
|
}
|
||||||
|
var cfg config
|
||||||
|
_, ok = cfg.fieldPtr(f).(*bool)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// completeConfig returns the list of configurable names starting with prefix.
|
||||||
|
func completeConfig(prefix string) []string {
|
||||||
|
var result []string
|
||||||
|
for v := range configFieldMap {
|
||||||
|
if strings.HasPrefix(v, prefix) {
|
||||||
|
result = append(result, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// configure stores the name=value mapping into the current config, correctly
|
||||||
|
// handling the case when name identifies a particular choice in a field.
|
||||||
|
func configure(name, value string) error {
|
||||||
|
currentMu.Lock()
|
||||||
|
defer currentMu.Unlock()
|
||||||
|
f, ok := configFieldMap[name]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unknown config field %q", name)
|
||||||
|
}
|
||||||
|
if f.name == name {
|
||||||
|
return currentCfg.set(f, value)
|
||||||
|
}
|
||||||
|
// name must be one of the choices. If value is true, set field-value
|
||||||
|
// to name.
|
||||||
|
if v, err := strconv.ParseBool(value); v && err == nil {
|
||||||
|
return currentCfg.set(f, name)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("unknown config field %q", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// resetTransient sets all transient fields in *cfg to their currently
|
||||||
|
// configured values.
|
||||||
|
func (cfg *config) resetTransient() {
|
||||||
|
current := currentConfig()
|
||||||
|
cfg.Output = current.Output
|
||||||
|
cfg.SourcePath = current.SourcePath
|
||||||
|
cfg.TrimPath = current.TrimPath
|
||||||
|
cfg.DivideBy = current.DivideBy
|
||||||
|
cfg.SampleIndex = current.SampleIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
// applyURL updates *cfg based on params.
|
||||||
|
func (cfg *config) applyURL(params url.Values) error {
|
||||||
|
for _, f := range configFields {
|
||||||
|
var value string
|
||||||
|
if f.urlparam != "" {
|
||||||
|
value = params.Get(f.urlparam)
|
||||||
|
}
|
||||||
|
if value == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := cfg.set(f, value); err != nil {
|
||||||
|
return fmt.Errorf("error setting config field %s: %v", f.name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeURL returns a URL based on initialURL that contains the config contents
|
||||||
|
// as parameters. The second result is true iff a parameter value was changed.
|
||||||
|
func (cfg *config) makeURL(initialURL url.URL) (url.URL, bool) {
|
||||||
|
q := initialURL.Query()
|
||||||
|
changed := false
|
||||||
|
for _, f := range configFields {
|
||||||
|
if f.urlparam == "" || !f.saved {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
v := cfg.get(f)
|
||||||
|
if v == f.defaultValue {
|
||||||
|
v = "" // URL for of default value is the empty string.
|
||||||
|
} else if f.field.Type.Kind() == reflect.Bool {
|
||||||
|
// Shorten bool values to "f" or "t"
|
||||||
|
v = v[:1]
|
||||||
|
}
|
||||||
|
if q.Get(f.urlparam) == v {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
changed = true
|
||||||
|
if v == "" {
|
||||||
|
q.Del(f.urlparam)
|
||||||
|
} else {
|
||||||
|
q.Set(f.urlparam, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if changed {
|
||||||
|
initialURL.RawQuery = q.Encode()
|
||||||
|
}
|
||||||
|
return initialURL, changed
|
||||||
|
}
|
||||||
110
src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go
generated
vendored
110
src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go
generated
vendored
|
|
@ -50,7 +50,7 @@ func PProf(eo *plugin.Options) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd != nil {
|
if cmd != nil {
|
||||||
return generateReport(p, cmd, pprofVariables, o)
|
return generateReport(p, cmd, currentConfig(), o)
|
||||||
}
|
}
|
||||||
|
|
||||||
if src.HTTPHostport != "" {
|
if src.HTTPHostport != "" {
|
||||||
|
|
@ -59,7 +59,7 @@ func PProf(eo *plugin.Options) error {
|
||||||
return interactive(p, o)
|
return interactive(p, o)
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateRawReport(p *profile.Profile, cmd []string, vars variables, o *plugin.Options) (*command, *report.Report, error) {
|
func generateRawReport(p *profile.Profile, cmd []string, cfg config, o *plugin.Options) (*command, *report.Report, error) {
|
||||||
p = p.Copy() // Prevent modification to the incoming profile.
|
p = p.Copy() // Prevent modification to the incoming profile.
|
||||||
|
|
||||||
// Identify units of numeric tags in profile.
|
// Identify units of numeric tags in profile.
|
||||||
|
|
@ -71,16 +71,16 @@ func generateRawReport(p *profile.Profile, cmd []string, vars variables, o *plug
|
||||||
panic("unexpected nil command")
|
panic("unexpected nil command")
|
||||||
}
|
}
|
||||||
|
|
||||||
vars = applyCommandOverrides(cmd[0], c.format, vars)
|
cfg = applyCommandOverrides(cmd[0], c.format, cfg)
|
||||||
|
|
||||||
// Delay focus after configuring report to get percentages on all samples.
|
// Delay focus after configuring report to get percentages on all samples.
|
||||||
relative := vars["relative_percentages"].boolValue()
|
relative := cfg.RelativePercentages
|
||||||
if relative {
|
if relative {
|
||||||
if err := applyFocus(p, numLabelUnits, vars, o.UI); err != nil {
|
if err := applyFocus(p, numLabelUnits, cfg, o.UI); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ropt, err := reportOptions(p, numLabelUnits, vars)
|
ropt, err := reportOptions(p, numLabelUnits, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -95,19 +95,19 @@ func generateRawReport(p *profile.Profile, cmd []string, vars variables, o *plug
|
||||||
|
|
||||||
rpt := report.New(p, ropt)
|
rpt := report.New(p, ropt)
|
||||||
if !relative {
|
if !relative {
|
||||||
if err := applyFocus(p, numLabelUnits, vars, o.UI); err != nil {
|
if err := applyFocus(p, numLabelUnits, cfg, o.UI); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := aggregate(p, vars); err != nil {
|
if err := aggregate(p, cfg); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c, rpt, nil
|
return c, rpt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateReport(p *profile.Profile, cmd []string, vars variables, o *plugin.Options) error {
|
func generateReport(p *profile.Profile, cmd []string, cfg config, o *plugin.Options) error {
|
||||||
c, rpt, err := generateRawReport(p, cmd, vars, o)
|
c, rpt, err := generateRawReport(p, cmd, cfg, o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -129,7 +129,7 @@ func generateReport(p *profile.Profile, cmd []string, vars variables, o *plugin.
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no output is specified, use default visualizer.
|
// If no output is specified, use default visualizer.
|
||||||
output := vars["output"].value
|
output := cfg.Output
|
||||||
if output == "" {
|
if output == "" {
|
||||||
if c.visualizer != nil {
|
if c.visualizer != nil {
|
||||||
return c.visualizer(src, os.Stdout, o.UI)
|
return c.visualizer(src, os.Stdout, o.UI)
|
||||||
|
|
@ -151,7 +151,7 @@ func generateReport(p *profile.Profile, cmd []string, vars variables, o *plugin.
|
||||||
return out.Close()
|
return out.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyCommandOverrides(cmd string, outputFormat int, v variables) variables {
|
func applyCommandOverrides(cmd string, outputFormat int, cfg config) config {
|
||||||
// Some report types override the trim flag to false below. This is to make
|
// Some report types override the trim flag to false below. This is to make
|
||||||
// sure the default heuristics of excluding insignificant nodes and edges
|
// sure the default heuristics of excluding insignificant nodes and edges
|
||||||
// from the call graph do not apply. One example where it is important is
|
// from the call graph do not apply. One example where it is important is
|
||||||
|
|
@ -160,55 +160,55 @@ func applyCommandOverrides(cmd string, outputFormat int, v variables) variables
|
||||||
// data is selected. So, with trimming enabled, the report could end up
|
// data is selected. So, with trimming enabled, the report could end up
|
||||||
// showing no data if the specified function is "uninteresting" as far as the
|
// showing no data if the specified function is "uninteresting" as far as the
|
||||||
// trimming is concerned.
|
// trimming is concerned.
|
||||||
trim := v["trim"].boolValue()
|
trim := cfg.Trim
|
||||||
|
|
||||||
switch cmd {
|
switch cmd {
|
||||||
case "disasm", "weblist":
|
case "disasm", "weblist":
|
||||||
trim = false
|
trim = false
|
||||||
v.set("addresses", "t")
|
cfg.Granularity = "addresses"
|
||||||
// Force the 'noinlines' mode so that source locations for a given address
|
// Force the 'noinlines' mode so that source locations for a given address
|
||||||
// collapse and there is only one for the given address. Without this
|
// collapse and there is only one for the given address. Without this
|
||||||
// cumulative metrics would be double-counted when annotating the assembly.
|
// cumulative metrics would be double-counted when annotating the assembly.
|
||||||
// This is because the merge is done by address and in case of an inlined
|
// This is because the merge is done by address and in case of an inlined
|
||||||
// stack each of the inlined entries is a separate callgraph node.
|
// stack each of the inlined entries is a separate callgraph node.
|
||||||
v.set("noinlines", "t")
|
cfg.NoInlines = true
|
||||||
case "peek":
|
case "peek":
|
||||||
trim = false
|
trim = false
|
||||||
case "list":
|
case "list":
|
||||||
trim = false
|
trim = false
|
||||||
v.set("lines", "t")
|
cfg.Granularity = "lines"
|
||||||
// Do not force 'noinlines' to be false so that specifying
|
// Do not force 'noinlines' to be false so that specifying
|
||||||
// "-list foo -noinlines" is supported and works as expected.
|
// "-list foo -noinlines" is supported and works as expected.
|
||||||
case "text", "top", "topproto":
|
case "text", "top", "topproto":
|
||||||
if v["nodecount"].intValue() == -1 {
|
if cfg.NodeCount == -1 {
|
||||||
v.set("nodecount", "0")
|
cfg.NodeCount = 0
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if v["nodecount"].intValue() == -1 {
|
if cfg.NodeCount == -1 {
|
||||||
v.set("nodecount", "80")
|
cfg.NodeCount = 80
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch outputFormat {
|
switch outputFormat {
|
||||||
case report.Proto, report.Raw, report.Callgrind:
|
case report.Proto, report.Raw, report.Callgrind:
|
||||||
trim = false
|
trim = false
|
||||||
v.set("addresses", "t")
|
cfg.Granularity = "addresses"
|
||||||
v.set("noinlines", "f")
|
cfg.NoInlines = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if !trim {
|
if !trim {
|
||||||
v.set("nodecount", "0")
|
cfg.NodeCount = 0
|
||||||
v.set("nodefraction", "0")
|
cfg.NodeFraction = 0
|
||||||
v.set("edgefraction", "0")
|
cfg.EdgeFraction = 0
|
||||||
}
|
}
|
||||||
return v
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
func aggregate(prof *profile.Profile, v variables) error {
|
func aggregate(prof *profile.Profile, cfg config) error {
|
||||||
var function, filename, linenumber, address bool
|
var function, filename, linenumber, address bool
|
||||||
inlines := !v["noinlines"].boolValue()
|
inlines := !cfg.NoInlines
|
||||||
switch {
|
switch cfg.Granularity {
|
||||||
case v["addresses"].boolValue():
|
case "addresses":
|
||||||
if inlines {
|
if inlines {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -216,15 +216,15 @@ func aggregate(prof *profile.Profile, v variables) error {
|
||||||
filename = true
|
filename = true
|
||||||
linenumber = true
|
linenumber = true
|
||||||
address = true
|
address = true
|
||||||
case v["lines"].boolValue():
|
case "lines":
|
||||||
function = true
|
function = true
|
||||||
filename = true
|
filename = true
|
||||||
linenumber = true
|
linenumber = true
|
||||||
case v["files"].boolValue():
|
case "files":
|
||||||
filename = true
|
filename = true
|
||||||
case v["functions"].boolValue():
|
case "functions":
|
||||||
function = true
|
function = true
|
||||||
case v["filefunctions"].boolValue():
|
case "filefunctions":
|
||||||
function = true
|
function = true
|
||||||
filename = true
|
filename = true
|
||||||
default:
|
default:
|
||||||
|
|
@ -233,8 +233,8 @@ func aggregate(prof *profile.Profile, v variables) error {
|
||||||
return prof.Aggregate(inlines, function, filename, linenumber, address)
|
return prof.Aggregate(inlines, function, filename, linenumber, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
func reportOptions(p *profile.Profile, numLabelUnits map[string]string, vars variables) (*report.Options, error) {
|
func reportOptions(p *profile.Profile, numLabelUnits map[string]string, cfg config) (*report.Options, error) {
|
||||||
si, mean := vars["sample_index"].value, vars["mean"].boolValue()
|
si, mean := cfg.SampleIndex, cfg.Mean
|
||||||
value, meanDiv, sample, err := sampleFormat(p, si, mean)
|
value, meanDiv, sample, err := sampleFormat(p, si, mean)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -245,29 +245,37 @@ func reportOptions(p *profile.Profile, numLabelUnits map[string]string, vars var
|
||||||
stype = "mean_" + stype
|
stype = "mean_" + stype
|
||||||
}
|
}
|
||||||
|
|
||||||
if vars["divide_by"].floatValue() == 0 {
|
if cfg.DivideBy == 0 {
|
||||||
return nil, fmt.Errorf("zero divisor specified")
|
return nil, fmt.Errorf("zero divisor specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
var filters []string
|
var filters []string
|
||||||
for _, k := range []string{"focus", "ignore", "hide", "show", "show_from", "tagfocus", "tagignore", "tagshow", "taghide"} {
|
addFilter := func(k string, v string) {
|
||||||
v := vars[k].value
|
|
||||||
if v != "" {
|
if v != "" {
|
||||||
filters = append(filters, k+"="+v)
|
filters = append(filters, k+"="+v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
addFilter("focus", cfg.Focus)
|
||||||
|
addFilter("ignore", cfg.Ignore)
|
||||||
|
addFilter("hide", cfg.Hide)
|
||||||
|
addFilter("show", cfg.Show)
|
||||||
|
addFilter("show_from", cfg.ShowFrom)
|
||||||
|
addFilter("tagfocus", cfg.TagFocus)
|
||||||
|
addFilter("tagignore", cfg.TagIgnore)
|
||||||
|
addFilter("tagshow", cfg.TagShow)
|
||||||
|
addFilter("taghide", cfg.TagHide)
|
||||||
|
|
||||||
ropt := &report.Options{
|
ropt := &report.Options{
|
||||||
CumSort: vars["cum"].boolValue(),
|
CumSort: cfg.Sort == "cum",
|
||||||
CallTree: vars["call_tree"].boolValue(),
|
CallTree: cfg.CallTree,
|
||||||
DropNegative: vars["drop_negative"].boolValue(),
|
DropNegative: cfg.DropNegative,
|
||||||
|
|
||||||
CompactLabels: vars["compact_labels"].boolValue(),
|
CompactLabels: cfg.CompactLabels,
|
||||||
Ratio: 1 / vars["divide_by"].floatValue(),
|
Ratio: 1 / cfg.DivideBy,
|
||||||
|
|
||||||
NodeCount: vars["nodecount"].intValue(),
|
NodeCount: cfg.NodeCount,
|
||||||
NodeFraction: vars["nodefraction"].floatValue(),
|
NodeFraction: cfg.NodeFraction,
|
||||||
EdgeFraction: vars["edgefraction"].floatValue(),
|
EdgeFraction: cfg.EdgeFraction,
|
||||||
|
|
||||||
ActiveFilters: filters,
|
ActiveFilters: filters,
|
||||||
NumLabelUnits: numLabelUnits,
|
NumLabelUnits: numLabelUnits,
|
||||||
|
|
@ -277,10 +285,12 @@ func reportOptions(p *profile.Profile, numLabelUnits map[string]string, vars var
|
||||||
SampleType: stype,
|
SampleType: stype,
|
||||||
SampleUnit: sample.Unit,
|
SampleUnit: sample.Unit,
|
||||||
|
|
||||||
OutputUnit: vars["unit"].value,
|
OutputUnit: cfg.Unit,
|
||||||
|
|
||||||
SourcePath: vars["source_path"].stringValue(),
|
SourcePath: cfg.SourcePath,
|
||||||
TrimPath: vars["trim_path"].stringValue(),
|
TrimPath: cfg.TrimPath,
|
||||||
|
|
||||||
|
IntelSyntax: cfg.IntelSyntax,
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(p.Mapping) > 0 && p.Mapping[0].File != "" {
|
if len(p.Mapping) > 0 && p.Mapping[0].File != "" {
|
||||||
|
|
|
||||||
22
src/cmd/vendor/github.com/google/pprof/internal/driver/driver_focus.go
generated
vendored
22
src/cmd/vendor/github.com/google/pprof/internal/driver/driver_focus.go
generated
vendored
|
|
@ -28,15 +28,15 @@ import (
|
||||||
var tagFilterRangeRx = regexp.MustCompile("([+-]?[[:digit:]]+)([[:alpha:]]+)?")
|
var tagFilterRangeRx = regexp.MustCompile("([+-]?[[:digit:]]+)([[:alpha:]]+)?")
|
||||||
|
|
||||||
// applyFocus filters samples based on the focus/ignore options
|
// applyFocus filters samples based on the focus/ignore options
|
||||||
func applyFocus(prof *profile.Profile, numLabelUnits map[string]string, v variables, ui plugin.UI) error {
|
func applyFocus(prof *profile.Profile, numLabelUnits map[string]string, cfg config, ui plugin.UI) error {
|
||||||
focus, err := compileRegexOption("focus", v["focus"].value, nil)
|
focus, err := compileRegexOption("focus", cfg.Focus, nil)
|
||||||
ignore, err := compileRegexOption("ignore", v["ignore"].value, err)
|
ignore, err := compileRegexOption("ignore", cfg.Ignore, err)
|
||||||
hide, err := compileRegexOption("hide", v["hide"].value, err)
|
hide, err := compileRegexOption("hide", cfg.Hide, err)
|
||||||
show, err := compileRegexOption("show", v["show"].value, err)
|
show, err := compileRegexOption("show", cfg.Show, err)
|
||||||
showfrom, err := compileRegexOption("show_from", v["show_from"].value, err)
|
showfrom, err := compileRegexOption("show_from", cfg.ShowFrom, err)
|
||||||
tagfocus, err := compileTagFilter("tagfocus", v["tagfocus"].value, numLabelUnits, ui, err)
|
tagfocus, err := compileTagFilter("tagfocus", cfg.TagFocus, numLabelUnits, ui, err)
|
||||||
tagignore, err := compileTagFilter("tagignore", v["tagignore"].value, numLabelUnits, ui, err)
|
tagignore, err := compileTagFilter("tagignore", cfg.TagIgnore, numLabelUnits, ui, err)
|
||||||
prunefrom, err := compileRegexOption("prune_from", v["prune_from"].value, err)
|
prunefrom, err := compileRegexOption("prune_from", cfg.PruneFrom, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -54,8 +54,8 @@ func applyFocus(prof *profile.Profile, numLabelUnits map[string]string, v variab
|
||||||
warnNoMatches(tagfocus == nil || tfm, "TagFocus", ui)
|
warnNoMatches(tagfocus == nil || tfm, "TagFocus", ui)
|
||||||
warnNoMatches(tagignore == nil || tim, "TagIgnore", ui)
|
warnNoMatches(tagignore == nil || tim, "TagIgnore", ui)
|
||||||
|
|
||||||
tagshow, err := compileRegexOption("tagshow", v["tagshow"].value, err)
|
tagshow, err := compileRegexOption("tagshow", cfg.TagShow, err)
|
||||||
taghide, err := compileRegexOption("taghide", v["taghide"].value, err)
|
taghide, err := compileRegexOption("taghide", cfg.TagHide, err)
|
||||||
tns, tnh := prof.FilterTagsByName(tagshow, taghide)
|
tns, tnh := prof.FilterTagsByName(tagshow, taghide)
|
||||||
warnNoMatches(tagshow == nil || tns, "TagShow", ui)
|
warnNoMatches(tagshow == nil || tns, "TagShow", ui)
|
||||||
warnNoMatches(tagignore == nil || tnh, "TagHide", ui)
|
warnNoMatches(tagignore == nil || tnh, "TagHide", ui)
|
||||||
|
|
|
||||||
7
src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph.go
generated
vendored
7
src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph.go
generated
vendored
|
|
@ -38,7 +38,10 @@ type treeNode struct {
|
||||||
func (ui *webInterface) flamegraph(w http.ResponseWriter, req *http.Request) {
|
func (ui *webInterface) flamegraph(w http.ResponseWriter, req *http.Request) {
|
||||||
// Force the call tree so that the graph is a tree.
|
// Force the call tree so that the graph is a tree.
|
||||||
// Also do not trim the tree so that the flame graph contains all functions.
|
// Also do not trim the tree so that the flame graph contains all functions.
|
||||||
rpt, errList := ui.makeReport(w, req, []string{"svg"}, "call_tree", "true", "trim", "false")
|
rpt, errList := ui.makeReport(w, req, []string{"svg"}, func(cfg *config) {
|
||||||
|
cfg.CallTree = true
|
||||||
|
cfg.Trim = false
|
||||||
|
})
|
||||||
if rpt == nil {
|
if rpt == nil {
|
||||||
return // error already reported
|
return // error already reported
|
||||||
}
|
}
|
||||||
|
|
@ -96,7 +99,7 @@ func (ui *webInterface) flamegraph(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.render(w, "flamegraph", rpt, errList, config.Labels, webArgs{
|
ui.render(w, req, "flamegraph", rpt, errList, config.Labels, webArgs{
|
||||||
FlameGraph: template.JS(b),
|
FlameGraph: template.JS(b),
|
||||||
Nodes: nodeArr,
|
Nodes: nodeArr,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
177
src/cmd/vendor/github.com/google/pprof/internal/driver/interactive.go
generated
vendored
177
src/cmd/vendor/github.com/google/pprof/internal/driver/interactive.go
generated
vendored
|
|
@ -34,17 +34,14 @@ var tailDigitsRE = regexp.MustCompile("[0-9]+$")
|
||||||
func interactive(p *profile.Profile, o *plugin.Options) error {
|
func interactive(p *profile.Profile, o *plugin.Options) error {
|
||||||
// Enter command processing loop.
|
// Enter command processing loop.
|
||||||
o.UI.SetAutoComplete(newCompleter(functionNames(p)))
|
o.UI.SetAutoComplete(newCompleter(functionNames(p)))
|
||||||
pprofVariables.set("compact_labels", "true")
|
configure("compact_labels", "true")
|
||||||
pprofVariables["sample_index"].help += fmt.Sprintf("Or use sample_index=name, with name in %v.\n", sampleTypes(p))
|
configHelp["sample_index"] += fmt.Sprintf("Or use sample_index=name, with name in %v.\n", sampleTypes(p))
|
||||||
|
|
||||||
// Do not wait for the visualizer to complete, to allow multiple
|
// Do not wait for the visualizer to complete, to allow multiple
|
||||||
// graphs to be visualized simultaneously.
|
// graphs to be visualized simultaneously.
|
||||||
interactiveMode = true
|
interactiveMode = true
|
||||||
shortcuts := profileShortcuts(p)
|
shortcuts := profileShortcuts(p)
|
||||||
|
|
||||||
// Get all groups in pprofVariables to allow for clearer error messages.
|
|
||||||
groups := groupOptions(pprofVariables)
|
|
||||||
|
|
||||||
greetings(p, o.UI)
|
greetings(p, o.UI)
|
||||||
for {
|
for {
|
||||||
input, err := o.UI.ReadLine("(pprof) ")
|
input, err := o.UI.ReadLine("(pprof) ")
|
||||||
|
|
@ -69,7 +66,12 @@ func interactive(p *profile.Profile, o *plugin.Options) error {
|
||||||
}
|
}
|
||||||
value = strings.TrimSpace(value)
|
value = strings.TrimSpace(value)
|
||||||
}
|
}
|
||||||
if v := pprofVariables[name]; v != nil {
|
if isConfigurable(name) {
|
||||||
|
// All non-bool options require inputs
|
||||||
|
if len(s) == 1 && !isBoolConfig(name) {
|
||||||
|
o.UI.PrintErr(fmt.Errorf("please specify a value, e.g. %s=<val>", name))
|
||||||
|
continue
|
||||||
|
}
|
||||||
if name == "sample_index" {
|
if name == "sample_index" {
|
||||||
// Error check sample_index=xxx to ensure xxx is a valid sample type.
|
// Error check sample_index=xxx to ensure xxx is a valid sample type.
|
||||||
index, err := p.SampleIndexByName(value)
|
index, err := p.SampleIndexByName(value)
|
||||||
|
|
@ -77,23 +79,17 @@ func interactive(p *profile.Profile, o *plugin.Options) error {
|
||||||
o.UI.PrintErr(err)
|
o.UI.PrintErr(err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if index < 0 || index >= len(p.SampleType) {
|
||||||
|
o.UI.PrintErr(fmt.Errorf("invalid sample_index %q", value))
|
||||||
|
continue
|
||||||
|
}
|
||||||
value = p.SampleType[index].Type
|
value = p.SampleType[index].Type
|
||||||
}
|
}
|
||||||
if err := pprofVariables.set(name, value); err != nil {
|
if err := configure(name, value); err != nil {
|
||||||
o.UI.PrintErr(err)
|
o.UI.PrintErr(err)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Allow group=variable syntax by converting into variable="".
|
|
||||||
if v := pprofVariables[value]; v != nil && v.group == name {
|
|
||||||
if err := pprofVariables.set(value, ""); err != nil {
|
|
||||||
o.UI.PrintErr(err)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
} else if okValues := groups[name]; okValues != nil {
|
|
||||||
o.UI.PrintErr(fmt.Errorf("unrecognized value for %s: %q. Use one of %s", name, value, strings.Join(okValues, ", ")))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens := strings.Fields(input)
|
tokens := strings.Fields(input)
|
||||||
|
|
@ -105,16 +101,16 @@ func interactive(p *profile.Profile, o *plugin.Options) error {
|
||||||
case "o", "options":
|
case "o", "options":
|
||||||
printCurrentOptions(p, o.UI)
|
printCurrentOptions(p, o.UI)
|
||||||
continue
|
continue
|
||||||
case "exit", "quit":
|
case "exit", "quit", "q":
|
||||||
return nil
|
return nil
|
||||||
case "help":
|
case "help":
|
||||||
commandHelp(strings.Join(tokens[1:], " "), o.UI)
|
commandHelp(strings.Join(tokens[1:], " "), o.UI)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
args, vars, err := parseCommandLine(tokens)
|
args, cfg, err := parseCommandLine(tokens)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = generateReportWrapper(p, args, vars, o)
|
err = generateReportWrapper(p, args, cfg, o)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -124,30 +120,13 @@ func interactive(p *profile.Profile, o *plugin.Options) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// groupOptions returns a map containing all non-empty groups
|
|
||||||
// mapped to an array of the option names in that group in
|
|
||||||
// sorted order.
|
|
||||||
func groupOptions(vars variables) map[string][]string {
|
|
||||||
groups := make(map[string][]string)
|
|
||||||
for name, option := range vars {
|
|
||||||
group := option.group
|
|
||||||
if group != "" {
|
|
||||||
groups[group] = append(groups[group], name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, names := range groups {
|
|
||||||
sort.Strings(names)
|
|
||||||
}
|
|
||||||
return groups
|
|
||||||
}
|
|
||||||
|
|
||||||
var generateReportWrapper = generateReport // For testing purposes.
|
var generateReportWrapper = generateReport // For testing purposes.
|
||||||
|
|
||||||
// greetings prints a brief welcome and some overall profile
|
// greetings prints a brief welcome and some overall profile
|
||||||
// information before accepting interactive commands.
|
// information before accepting interactive commands.
|
||||||
func greetings(p *profile.Profile, ui plugin.UI) {
|
func greetings(p *profile.Profile, ui plugin.UI) {
|
||||||
numLabelUnits := identifyNumLabelUnits(p, ui)
|
numLabelUnits := identifyNumLabelUnits(p, ui)
|
||||||
ropt, err := reportOptions(p, numLabelUnits, pprofVariables)
|
ropt, err := reportOptions(p, numLabelUnits, currentConfig())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rpt := report.New(p, ropt)
|
rpt := report.New(p, ropt)
|
||||||
ui.Print(strings.Join(report.ProfileLabels(rpt), "\n"))
|
ui.Print(strings.Join(report.ProfileLabels(rpt), "\n"))
|
||||||
|
|
@ -200,27 +179,16 @@ func sampleTypes(p *profile.Profile) []string {
|
||||||
|
|
||||||
func printCurrentOptions(p *profile.Profile, ui plugin.UI) {
|
func printCurrentOptions(p *profile.Profile, ui plugin.UI) {
|
||||||
var args []string
|
var args []string
|
||||||
type groupInfo struct {
|
current := currentConfig()
|
||||||
set string
|
for _, f := range configFields {
|
||||||
values []string
|
n := f.name
|
||||||
}
|
v := current.get(f)
|
||||||
groups := make(map[string]*groupInfo)
|
|
||||||
for n, o := range pprofVariables {
|
|
||||||
v := o.stringValue()
|
|
||||||
comment := ""
|
comment := ""
|
||||||
if g := o.group; g != "" {
|
|
||||||
gi, ok := groups[g]
|
|
||||||
if !ok {
|
|
||||||
gi = &groupInfo{}
|
|
||||||
groups[g] = gi
|
|
||||||
}
|
|
||||||
if o.boolValue() {
|
|
||||||
gi.set = n
|
|
||||||
}
|
|
||||||
gi.values = append(gi.values, n)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
switch {
|
switch {
|
||||||
|
case len(f.choices) > 0:
|
||||||
|
values := append([]string{}, f.choices...)
|
||||||
|
sort.Strings(values)
|
||||||
|
comment = "[" + strings.Join(values, " | ") + "]"
|
||||||
case n == "sample_index":
|
case n == "sample_index":
|
||||||
st := sampleTypes(p)
|
st := sampleTypes(p)
|
||||||
if v == "" {
|
if v == "" {
|
||||||
|
|
@ -242,18 +210,13 @@ func printCurrentOptions(p *profile.Profile, ui plugin.UI) {
|
||||||
}
|
}
|
||||||
args = append(args, fmt.Sprintf(" %-25s = %-20s %s", n, v, comment))
|
args = append(args, fmt.Sprintf(" %-25s = %-20s %s", n, v, comment))
|
||||||
}
|
}
|
||||||
for g, vars := range groups {
|
|
||||||
sort.Strings(vars.values)
|
|
||||||
comment := commentStart + " [" + strings.Join(vars.values, " | ") + "]"
|
|
||||||
args = append(args, fmt.Sprintf(" %-25s = %-20s %s", g, vars.set, comment))
|
|
||||||
}
|
|
||||||
sort.Strings(args)
|
sort.Strings(args)
|
||||||
ui.Print(strings.Join(args, "\n"))
|
ui.Print(strings.Join(args, "\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseCommandLine parses a command and returns the pprof command to
|
// parseCommandLine parses a command and returns the pprof command to
|
||||||
// execute and a set of variables for the report.
|
// execute and the configuration to use for the report.
|
||||||
func parseCommandLine(input []string) ([]string, variables, error) {
|
func parseCommandLine(input []string) ([]string, config, error) {
|
||||||
cmd, args := input[:1], input[1:]
|
cmd, args := input[:1], input[1:]
|
||||||
name := cmd[0]
|
name := cmd[0]
|
||||||
|
|
||||||
|
|
@ -267,25 +230,32 @@ func parseCommandLine(input []string) ([]string, variables, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return nil, nil, fmt.Errorf("unrecognized command: %q", name)
|
if _, ok := configHelp[name]; ok {
|
||||||
|
value := "<val>"
|
||||||
|
if len(args) > 0 {
|
||||||
|
value = args[0]
|
||||||
|
}
|
||||||
|
return nil, config{}, fmt.Errorf("did you mean: %s=%s", name, value)
|
||||||
|
}
|
||||||
|
return nil, config{}, fmt.Errorf("unrecognized command: %q", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.hasParam {
|
if c.hasParam {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return nil, nil, fmt.Errorf("command %s requires an argument", name)
|
return nil, config{}, fmt.Errorf("command %s requires an argument", name)
|
||||||
}
|
}
|
||||||
cmd = append(cmd, args[0])
|
cmd = append(cmd, args[0])
|
||||||
args = args[1:]
|
args = args[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the variables as options set in the command line are not persistent.
|
// Copy config since options set in the command line should not persist.
|
||||||
vcopy := pprofVariables.makeCopy()
|
vcopy := currentConfig()
|
||||||
|
|
||||||
var focus, ignore string
|
var focus, ignore string
|
||||||
for i := 0; i < len(args); i++ {
|
for i := 0; i < len(args); i++ {
|
||||||
t := args[i]
|
t := args[i]
|
||||||
if _, err := strconv.ParseInt(t, 10, 32); err == nil {
|
if n, err := strconv.ParseInt(t, 10, 32); err == nil {
|
||||||
vcopy.set("nodecount", t)
|
vcopy.NodeCount = int(n)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
switch t[0] {
|
switch t[0] {
|
||||||
|
|
@ -294,14 +264,14 @@ func parseCommandLine(input []string) ([]string, variables, error) {
|
||||||
if outputFile == "" {
|
if outputFile == "" {
|
||||||
i++
|
i++
|
||||||
if i >= len(args) {
|
if i >= len(args) {
|
||||||
return nil, nil, fmt.Errorf("unexpected end of line after >")
|
return nil, config{}, fmt.Errorf("unexpected end of line after >")
|
||||||
}
|
}
|
||||||
outputFile = args[i]
|
outputFile = args[i]
|
||||||
}
|
}
|
||||||
vcopy.set("output", outputFile)
|
vcopy.Output = outputFile
|
||||||
case '-':
|
case '-':
|
||||||
if t == "--cum" || t == "-cum" {
|
if t == "--cum" || t == "-cum" {
|
||||||
vcopy.set("cum", "t")
|
vcopy.Sort = "cum"
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
ignore = catRegex(ignore, t[1:])
|
ignore = catRegex(ignore, t[1:])
|
||||||
|
|
@ -311,30 +281,27 @@ func parseCommandLine(input []string) ([]string, variables, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if name == "tags" {
|
if name == "tags" {
|
||||||
updateFocusIgnore(vcopy, "tag", focus, ignore)
|
if focus != "" {
|
||||||
|
vcopy.TagFocus = focus
|
||||||
|
}
|
||||||
|
if ignore != "" {
|
||||||
|
vcopy.TagIgnore = ignore
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
updateFocusIgnore(vcopy, "", focus, ignore)
|
if focus != "" {
|
||||||
|
vcopy.Focus = focus
|
||||||
|
}
|
||||||
|
if ignore != "" {
|
||||||
|
vcopy.Ignore = ignore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if vcopy.NodeCount == -1 && (name == "text" || name == "top") {
|
||||||
if vcopy["nodecount"].intValue() == -1 && (name == "text" || name == "top") {
|
vcopy.NodeCount = 10
|
||||||
vcopy.set("nodecount", "10")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cmd, vcopy, nil
|
return cmd, vcopy, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateFocusIgnore(v variables, prefix, f, i string) {
|
|
||||||
if f != "" {
|
|
||||||
focus := prefix + "focus"
|
|
||||||
v.set(focus, catRegex(v[focus].value, f))
|
|
||||||
}
|
|
||||||
|
|
||||||
if i != "" {
|
|
||||||
ignore := prefix + "ignore"
|
|
||||||
v.set(ignore, catRegex(v[ignore].value, i))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func catRegex(a, b string) string {
|
func catRegex(a, b string) string {
|
||||||
if a != "" && b != "" {
|
if a != "" && b != "" {
|
||||||
return a + "|" + b
|
return a + "|" + b
|
||||||
|
|
@ -362,8 +329,8 @@ func commandHelp(args string, ui plugin.UI) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if v := pprofVariables[args]; v != nil {
|
if help, ok := configHelp[args]; ok {
|
||||||
ui.Print(v.help + "\n")
|
ui.Print(help + "\n")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -373,18 +340,17 @@ func commandHelp(args string, ui plugin.UI) {
|
||||||
// newCompleter creates an autocompletion function for a set of commands.
|
// newCompleter creates an autocompletion function for a set of commands.
|
||||||
func newCompleter(fns []string) func(string) string {
|
func newCompleter(fns []string) func(string) string {
|
||||||
return func(line string) string {
|
return func(line string) string {
|
||||||
v := pprofVariables
|
|
||||||
switch tokens := strings.Fields(line); len(tokens) {
|
switch tokens := strings.Fields(line); len(tokens) {
|
||||||
case 0:
|
case 0:
|
||||||
// Nothing to complete
|
// Nothing to complete
|
||||||
case 1:
|
case 1:
|
||||||
// Single token -- complete command name
|
// Single token -- complete command name
|
||||||
if match := matchVariableOrCommand(v, tokens[0]); match != "" {
|
if match := matchVariableOrCommand(tokens[0]); match != "" {
|
||||||
return match
|
return match
|
||||||
}
|
}
|
||||||
case 2:
|
case 2:
|
||||||
if tokens[0] == "help" {
|
if tokens[0] == "help" {
|
||||||
if match := matchVariableOrCommand(v, tokens[1]); match != "" {
|
if match := matchVariableOrCommand(tokens[1]); match != "" {
|
||||||
return tokens[0] + " " + match
|
return tokens[0] + " " + match
|
||||||
}
|
}
|
||||||
return line
|
return line
|
||||||
|
|
@ -408,26 +374,19 @@ func newCompleter(fns []string) func(string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// matchVariableOrCommand attempts to match a string token to the prefix of a Command.
|
// matchVariableOrCommand attempts to match a string token to the prefix of a Command.
|
||||||
func matchVariableOrCommand(v variables, token string) string {
|
func matchVariableOrCommand(token string) string {
|
||||||
token = strings.ToLower(token)
|
token = strings.ToLower(token)
|
||||||
found := ""
|
var matches []string
|
||||||
for cmd := range pprofCommands {
|
for cmd := range pprofCommands {
|
||||||
if strings.HasPrefix(cmd, token) {
|
if strings.HasPrefix(cmd, token) {
|
||||||
if found != "" {
|
matches = append(matches, cmd)
|
||||||
return ""
|
|
||||||
}
|
|
||||||
found = cmd
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for variable := range v {
|
matches = append(matches, completeConfig(token)...)
|
||||||
if strings.HasPrefix(variable, token) {
|
if len(matches) == 1 {
|
||||||
if found != "" {
|
return matches[0]
|
||||||
return ""
|
|
||||||
}
|
|
||||||
found = variable
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return found
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// functionCompleter replaces provided substring with a function
|
// functionCompleter replaces provided substring with a function
|
||||||
|
|
|
||||||
157
src/cmd/vendor/github.com/google/pprof/internal/driver/settings.go
generated
vendored
Normal file
157
src/cmd/vendor/github.com/google/pprof/internal/driver/settings.go
generated
vendored
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// settings holds pprof settings.
|
||||||
|
type settings struct {
|
||||||
|
// Configs holds a list of named UI configurations.
|
||||||
|
Configs []namedConfig `json:"configs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// namedConfig associates a name with a config.
|
||||||
|
type namedConfig struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
// settingsFileName returns the name of the file where settings should be saved.
|
||||||
|
func settingsFileName() (string, error) {
|
||||||
|
// Return "pprof/settings.json" under os.UserConfigDir().
|
||||||
|
dir, err := os.UserConfigDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return filepath.Join(dir, "pprof", "settings.json"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readSettings reads settings from fname.
|
||||||
|
func readSettings(fname string) (*settings, error) {
|
||||||
|
data, err := ioutil.ReadFile(fname)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return &settings{}, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("could not read settings: %w", err)
|
||||||
|
}
|
||||||
|
settings := &settings{}
|
||||||
|
if err := json.Unmarshal(data, settings); err != nil {
|
||||||
|
return nil, fmt.Errorf("could not parse settings: %w", err)
|
||||||
|
}
|
||||||
|
for i := range settings.Configs {
|
||||||
|
settings.Configs[i].resetTransient()
|
||||||
|
}
|
||||||
|
return settings, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeSettings saves settings to fname.
|
||||||
|
func writeSettings(fname string, settings *settings) error {
|
||||||
|
data, err := json.MarshalIndent(settings, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not encode settings: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the settings directory if it does not exist
|
||||||
|
// XDG specifies permissions 0700 when creating settings dirs:
|
||||||
|
// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||||
|
if err := os.MkdirAll(filepath.Dir(fname), 0700); err != nil {
|
||||||
|
return fmt.Errorf("failed to create settings directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ioutil.WriteFile(fname, data, 0644); err != nil {
|
||||||
|
return fmt.Errorf("failed to write settings: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// configMenuEntry holds information for a single config menu entry.
|
||||||
|
type configMenuEntry struct {
|
||||||
|
Name string
|
||||||
|
URL string
|
||||||
|
Current bool // Is this the currently selected config?
|
||||||
|
UserConfig bool // Is this a user-provided config?
|
||||||
|
}
|
||||||
|
|
||||||
|
// configMenu returns a list of items to add to a menu in the web UI.
|
||||||
|
func configMenu(fname string, url url.URL) []configMenuEntry {
|
||||||
|
// Start with system configs.
|
||||||
|
configs := []namedConfig{{Name: "Default", config: defaultConfig()}}
|
||||||
|
if settings, err := readSettings(fname); err == nil {
|
||||||
|
// Add user configs.
|
||||||
|
configs = append(configs, settings.Configs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to menu entries.
|
||||||
|
result := make([]configMenuEntry, len(configs))
|
||||||
|
lastMatch := -1
|
||||||
|
for i, cfg := range configs {
|
||||||
|
dst, changed := cfg.config.makeURL(url)
|
||||||
|
if !changed {
|
||||||
|
lastMatch = i
|
||||||
|
}
|
||||||
|
result[i] = configMenuEntry{
|
||||||
|
Name: cfg.Name,
|
||||||
|
URL: dst.String(),
|
||||||
|
UserConfig: (i != 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Mark the last matching config as currennt
|
||||||
|
if lastMatch >= 0 {
|
||||||
|
result[lastMatch].Current = true
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// editSettings edits settings by applying fn to them.
|
||||||
|
func editSettings(fname string, fn func(s *settings) error) error {
|
||||||
|
settings, err := readSettings(fname)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := fn(settings); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return writeSettings(fname, settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setConfig saves the config specified in request to fname.
|
||||||
|
func setConfig(fname string, request url.URL) error {
|
||||||
|
q := request.Query()
|
||||||
|
name := q.Get("config")
|
||||||
|
if name == "" {
|
||||||
|
return fmt.Errorf("invalid config name")
|
||||||
|
}
|
||||||
|
cfg := currentConfig()
|
||||||
|
if err := cfg.applyURL(q); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return editSettings(fname, func(s *settings) error {
|
||||||
|
for i, c := range s.Configs {
|
||||||
|
if c.Name == name {
|
||||||
|
s.Configs[i].config = cfg
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.Configs = append(s.Configs, namedConfig{Name: name, config: cfg})
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeConfig removes config from fname.
|
||||||
|
func removeConfig(fname, config string) error {
|
||||||
|
return editSettings(fname, func(s *settings) error {
|
||||||
|
for i, c := range s.Configs {
|
||||||
|
if c.Name == config {
|
||||||
|
s.Configs = append(s.Configs[:i], s.Configs[i+1:]...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("config %s not found", config)
|
||||||
|
})
|
||||||
|
}
|
||||||
238
src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go
generated
vendored
238
src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go
generated
vendored
|
|
@ -166,6 +166,73 @@ a {
|
||||||
color: gray;
|
color: gray;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
.menu-check-mark {
|
||||||
|
position: absolute;
|
||||||
|
left: 2px;
|
||||||
|
}
|
||||||
|
.menu-delete-btn {
|
||||||
|
position: absolute;
|
||||||
|
right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
{{/* Used to disable events when a modal dialog is displayed */}}
|
||||||
|
#dialog-overlay {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
left: 0px;
|
||||||
|
top: 0px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(1,1,1,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog {
|
||||||
|
{{/* Displayed centered horizontally near the top */}}
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
margin: 0px;
|
||||||
|
top: 60px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
|
||||||
|
z-index: 3;
|
||||||
|
font-size: 125%;
|
||||||
|
background-color: #ffffff;
|
||||||
|
box-shadow: 0 1px 5px rgba(0,0,0,.3);
|
||||||
|
}
|
||||||
|
.dialog-header {
|
||||||
|
font-size: 120%;
|
||||||
|
border-bottom: 1px solid #CCCCCC;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
background: #EEEEEE;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.dialog-footer {
|
||||||
|
border-top: 1px solid #CCCCCC;
|
||||||
|
width: 100%;
|
||||||
|
text-align: right;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.dialog-error {
|
||||||
|
margin: 10px;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
.dialog input {
|
||||||
|
margin: 10px;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
.dialog button {
|
||||||
|
margin-left: 10px;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
#save-dialog, #delete-dialog {
|
||||||
|
width: 50%;
|
||||||
|
max-width: 20em;
|
||||||
|
}
|
||||||
|
#delete-prompt {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
#content {
|
#content {
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
|
@ -200,6 +267,8 @@ table thead {
|
||||||
font-family: 'Roboto Medium', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
font-family: 'Roboto Medium', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
||||||
}
|
}
|
||||||
table tr th {
|
table tr th {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
background-color: #ddd;
|
background-color: #ddd;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
padding: .3em .5em;
|
padding: .3em .5em;
|
||||||
|
|
@ -282,6 +351,24 @@ table tr td {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="config" class="menu-item">
|
||||||
|
<div class="menu-name">
|
||||||
|
Config
|
||||||
|
<i class="downArrow"></i>
|
||||||
|
</div>
|
||||||
|
<div class="submenu">
|
||||||
|
<a title="{{.Help.save_config}}" id="save-config">Save as ...</a>
|
||||||
|
<hr>
|
||||||
|
{{range .Configs}}
|
||||||
|
<a href="{{.URL}}">
|
||||||
|
{{if .Current}}<span class="menu-check-mark">✓</span>{{end}}
|
||||||
|
{{.Name}}
|
||||||
|
{{if .UserConfig}}<span class="menu-delete-btn" data-config={{.Name}}>🗙</span>{{end}}
|
||||||
|
</a>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<input id="search" type="text" placeholder="Search regexp" autocomplete="off" autocapitalize="none" size=40>
|
<input id="search" type="text" placeholder="Search regexp" autocomplete="off" autocapitalize="none" size=40>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -294,6 +381,31 @@ table tr td {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="dialog-overlay"></div>
|
||||||
|
|
||||||
|
<div class="dialog" id="save-dialog">
|
||||||
|
<div class="dialog-header">Save options as</div>
|
||||||
|
<datalist id="config-list">
|
||||||
|
{{range .Configs}}{{if .UserConfig}}<option value="{{.Name}}" />{{end}}{{end}}
|
||||||
|
</datalist>
|
||||||
|
<input id="save-name" type="text" list="config-list" placeholder="New config" />
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<span class="dialog-error" id="save-error"></span>
|
||||||
|
<button id="save-cancel">Cancel</button>
|
||||||
|
<button id="save-confirm">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dialog" id="delete-dialog">
|
||||||
|
<div class="dialog-header" id="delete-dialog-title">Delete config</div>
|
||||||
|
<div id="delete-prompt"></div>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<span class="dialog-error" id="delete-error"></span>
|
||||||
|
<button id="delete-cancel">Cancel</button>
|
||||||
|
<button id="delete-confirm">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="errors">{{range .Errors}}<div>{{.}}</div>{{end}}</div>
|
<div id="errors">{{range .Errors}}<div>{{.}}</div>{{end}}</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
|
@ -583,6 +695,131 @@ function initMenus() {
|
||||||
}, { passive: true, capture: true });
|
}, { passive: true, capture: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sendURL(method, url, done) {
|
||||||
|
fetch(url.toString(), {method: method})
|
||||||
|
.then((response) => { done(response.ok); })
|
||||||
|
.catch((error) => { done(false); });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize handlers for saving/loading configurations.
|
||||||
|
function initConfigManager() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Initialize various elements.
|
||||||
|
function elem(id) {
|
||||||
|
const result = document.getElementById(id);
|
||||||
|
if (!result) console.warn('element ' + id + ' not found');
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
const overlay = elem('dialog-overlay');
|
||||||
|
const saveDialog = elem('save-dialog');
|
||||||
|
const saveInput = elem('save-name');
|
||||||
|
const saveError = elem('save-error');
|
||||||
|
const delDialog = elem('delete-dialog');
|
||||||
|
const delPrompt = elem('delete-prompt');
|
||||||
|
const delError = elem('delete-error');
|
||||||
|
|
||||||
|
let currentDialog = null;
|
||||||
|
let currentDeleteTarget = null;
|
||||||
|
|
||||||
|
function showDialog(dialog) {
|
||||||
|
if (currentDialog != null) {
|
||||||
|
overlay.style.display = 'none';
|
||||||
|
currentDialog.style.display = 'none';
|
||||||
|
}
|
||||||
|
currentDialog = dialog;
|
||||||
|
if (dialog != null) {
|
||||||
|
overlay.style.display = 'block';
|
||||||
|
dialog.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelDialog(e) {
|
||||||
|
showDialog(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show dialog for saving the current config.
|
||||||
|
function showSaveDialog(e) {
|
||||||
|
saveError.innerText = '';
|
||||||
|
showDialog(saveDialog);
|
||||||
|
saveInput.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit save config.
|
||||||
|
function commitSave(e) {
|
||||||
|
const name = saveInput.value;
|
||||||
|
const url = new URL(document.URL);
|
||||||
|
// Set path relative to existing path.
|
||||||
|
url.pathname = new URL('./saveconfig', document.URL).pathname;
|
||||||
|
url.searchParams.set('config', name);
|
||||||
|
saveError.innerText = '';
|
||||||
|
sendURL('POST', url, (ok) => {
|
||||||
|
if (!ok) {
|
||||||
|
saveError.innerText = 'Save failed';
|
||||||
|
} else {
|
||||||
|
showDialog(null);
|
||||||
|
location.reload(); // Reload to show updated config menu
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSaveInputKey(e) {
|
||||||
|
if (e.key === 'Enter') commitSave(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteConfig(e, elem) {
|
||||||
|
e.preventDefault();
|
||||||
|
const config = elem.dataset.config;
|
||||||
|
delPrompt.innerText = 'Delete ' + config + '?';
|
||||||
|
currentDeleteTarget = elem;
|
||||||
|
showDialog(delDialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitDelete(e, elem) {
|
||||||
|
if (!currentDeleteTarget) return;
|
||||||
|
const config = currentDeleteTarget.dataset.config;
|
||||||
|
const url = new URL('./deleteconfig', document.URL);
|
||||||
|
url.searchParams.set('config', config);
|
||||||
|
delError.innerText = '';
|
||||||
|
sendURL('DELETE', url, (ok) => {
|
||||||
|
if (!ok) {
|
||||||
|
delError.innerText = 'Delete failed';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showDialog(null);
|
||||||
|
// Remove menu entry for this config.
|
||||||
|
if (currentDeleteTarget && currentDeleteTarget.parentElement) {
|
||||||
|
currentDeleteTarget.parentElement.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind event on elem to fn.
|
||||||
|
function bind(event, elem, fn) {
|
||||||
|
if (elem == null) return;
|
||||||
|
elem.addEventListener(event, fn);
|
||||||
|
if (event == 'click') {
|
||||||
|
// Also enable via touch.
|
||||||
|
elem.addEventListener('touchstart', fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bind('click', elem('save-config'), showSaveDialog);
|
||||||
|
bind('click', elem('save-cancel'), cancelDialog);
|
||||||
|
bind('click', elem('save-confirm'), commitSave);
|
||||||
|
bind('keydown', saveInput, handleSaveInputKey);
|
||||||
|
|
||||||
|
bind('click', elem('delete-cancel'), cancelDialog);
|
||||||
|
bind('click', elem('delete-confirm'), commitDelete);
|
||||||
|
|
||||||
|
// Activate deletion button for all config entries in menu.
|
||||||
|
for (const del of Array.from(document.getElementsByClassName('menu-delete-btn'))) {
|
||||||
|
bind('click', del, (e) => {
|
||||||
|
deleteConfig(e, del);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function viewer(baseUrl, nodes) {
|
function viewer(baseUrl, nodes) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
|
@ -875,6 +1112,7 @@ function viewer(baseUrl, nodes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
addAction('details', handleDetails);
|
addAction('details', handleDetails);
|
||||||
|
initConfigManager();
|
||||||
|
|
||||||
search.addEventListener('input', handleSearch);
|
search.addEventListener('input', handleSearch);
|
||||||
search.addEventListener('keydown', handleKey);
|
search.addEventListener('keydown', handleKey);
|
||||||
|
|
|
||||||
143
src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go
generated
vendored
143
src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go
generated
vendored
|
|
@ -35,22 +35,28 @@ import (
|
||||||
|
|
||||||
// webInterface holds the state needed for serving a browser based interface.
|
// webInterface holds the state needed for serving a browser based interface.
|
||||||
type webInterface struct {
|
type webInterface struct {
|
||||||
prof *profile.Profile
|
prof *profile.Profile
|
||||||
options *plugin.Options
|
options *plugin.Options
|
||||||
help map[string]string
|
help map[string]string
|
||||||
templates *template.Template
|
templates *template.Template
|
||||||
|
settingsFile string
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeWebInterface(p *profile.Profile, opt *plugin.Options) *webInterface {
|
func makeWebInterface(p *profile.Profile, opt *plugin.Options) (*webInterface, error) {
|
||||||
|
settingsFile, err := settingsFileName()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
templates := template.New("templategroup")
|
templates := template.New("templategroup")
|
||||||
addTemplates(templates)
|
addTemplates(templates)
|
||||||
report.AddSourceTemplates(templates)
|
report.AddSourceTemplates(templates)
|
||||||
return &webInterface{
|
return &webInterface{
|
||||||
prof: p,
|
prof: p,
|
||||||
options: opt,
|
options: opt,
|
||||||
help: make(map[string]string),
|
help: make(map[string]string),
|
||||||
templates: templates,
|
templates: templates,
|
||||||
}
|
settingsFile: settingsFile,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// maxEntries is the maximum number of entries to print for text interfaces.
|
// maxEntries is the maximum number of entries to print for text interfaces.
|
||||||
|
|
@ -80,6 +86,7 @@ type webArgs struct {
|
||||||
TextBody string
|
TextBody string
|
||||||
Top []report.TextItem
|
Top []report.TextItem
|
||||||
FlameGraph template.JS
|
FlameGraph template.JS
|
||||||
|
Configs []configMenuEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveWebInterface(hostport string, p *profile.Profile, o *plugin.Options, disableBrowser bool) error {
|
func serveWebInterface(hostport string, p *profile.Profile, o *plugin.Options, disableBrowser bool) error {
|
||||||
|
|
@ -88,16 +95,20 @@ func serveWebInterface(hostport string, p *profile.Profile, o *plugin.Options, d
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
interactiveMode = true
|
interactiveMode = true
|
||||||
ui := makeWebInterface(p, o)
|
ui, err := makeWebInterface(p, o)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
for n, c := range pprofCommands {
|
for n, c := range pprofCommands {
|
||||||
ui.help[n] = c.description
|
ui.help[n] = c.description
|
||||||
}
|
}
|
||||||
for n, v := range pprofVariables {
|
for n, help := range configHelp {
|
||||||
ui.help[n] = v.help
|
ui.help[n] = help
|
||||||
}
|
}
|
||||||
ui.help["details"] = "Show information about the profile and this view"
|
ui.help["details"] = "Show information about the profile and this view"
|
||||||
ui.help["graph"] = "Display profile as a directed graph"
|
ui.help["graph"] = "Display profile as a directed graph"
|
||||||
ui.help["reset"] = "Show the entire profile"
|
ui.help["reset"] = "Show the entire profile"
|
||||||
|
ui.help["save_config"] = "Save current settings"
|
||||||
|
|
||||||
server := o.HTTPServer
|
server := o.HTTPServer
|
||||||
if server == nil {
|
if server == nil {
|
||||||
|
|
@ -108,12 +119,14 @@ func serveWebInterface(hostport string, p *profile.Profile, o *plugin.Options, d
|
||||||
Host: host,
|
Host: host,
|
||||||
Port: port,
|
Port: port,
|
||||||
Handlers: map[string]http.Handler{
|
Handlers: map[string]http.Handler{
|
||||||
"/": http.HandlerFunc(ui.dot),
|
"/": http.HandlerFunc(ui.dot),
|
||||||
"/top": http.HandlerFunc(ui.top),
|
"/top": http.HandlerFunc(ui.top),
|
||||||
"/disasm": http.HandlerFunc(ui.disasm),
|
"/disasm": http.HandlerFunc(ui.disasm),
|
||||||
"/source": http.HandlerFunc(ui.source),
|
"/source": http.HandlerFunc(ui.source),
|
||||||
"/peek": http.HandlerFunc(ui.peek),
|
"/peek": http.HandlerFunc(ui.peek),
|
||||||
"/flamegraph": http.HandlerFunc(ui.flamegraph),
|
"/flamegraph": http.HandlerFunc(ui.flamegraph),
|
||||||
|
"/saveconfig": http.HandlerFunc(ui.saveConfig),
|
||||||
|
"/deleteconfig": http.HandlerFunc(ui.deleteConfig),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,21 +219,9 @@ func isLocalhost(host string) bool {
|
||||||
|
|
||||||
func openBrowser(url string, o *plugin.Options) {
|
func openBrowser(url string, o *plugin.Options) {
|
||||||
// Construct URL.
|
// Construct URL.
|
||||||
u, _ := gourl.Parse(url)
|
baseURL, _ := gourl.Parse(url)
|
||||||
q := u.Query()
|
current := currentConfig()
|
||||||
for _, p := range []struct{ param, key string }{
|
u, _ := current.makeURL(*baseURL)
|
||||||
{"f", "focus"},
|
|
||||||
{"s", "show"},
|
|
||||||
{"sf", "show_from"},
|
|
||||||
{"i", "ignore"},
|
|
||||||
{"h", "hide"},
|
|
||||||
{"si", "sample_index"},
|
|
||||||
} {
|
|
||||||
if v := pprofVariables[p.key].value; v != "" {
|
|
||||||
q.Set(p.param, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
u.RawQuery = q.Encode()
|
|
||||||
|
|
||||||
// Give server a little time to get ready.
|
// Give server a little time to get ready.
|
||||||
time.Sleep(time.Millisecond * 500)
|
time.Sleep(time.Millisecond * 500)
|
||||||
|
|
@ -240,28 +241,23 @@ func openBrowser(url string, o *plugin.Options) {
|
||||||
o.UI.PrintErr(u.String())
|
o.UI.PrintErr(u.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func varsFromURL(u *gourl.URL) variables {
|
|
||||||
vars := pprofVariables.makeCopy()
|
|
||||||
vars["focus"].value = u.Query().Get("f")
|
|
||||||
vars["show"].value = u.Query().Get("s")
|
|
||||||
vars["show_from"].value = u.Query().Get("sf")
|
|
||||||
vars["ignore"].value = u.Query().Get("i")
|
|
||||||
vars["hide"].value = u.Query().Get("h")
|
|
||||||
vars["sample_index"].value = u.Query().Get("si")
|
|
||||||
return vars
|
|
||||||
}
|
|
||||||
|
|
||||||
// makeReport generates a report for the specified command.
|
// makeReport generates a report for the specified command.
|
||||||
|
// If configEditor is not null, it is used to edit the config used for the report.
|
||||||
func (ui *webInterface) makeReport(w http.ResponseWriter, req *http.Request,
|
func (ui *webInterface) makeReport(w http.ResponseWriter, req *http.Request,
|
||||||
cmd []string, vars ...string) (*report.Report, []string) {
|
cmd []string, configEditor func(*config)) (*report.Report, []string) {
|
||||||
v := varsFromURL(req.URL)
|
cfg := currentConfig()
|
||||||
for i := 0; i+1 < len(vars); i += 2 {
|
if err := cfg.applyURL(req.URL.Query()); err != nil {
|
||||||
v[vars[i]].value = vars[i+1]
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
ui.options.UI.PrintErr(err)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if configEditor != nil {
|
||||||
|
configEditor(&cfg)
|
||||||
}
|
}
|
||||||
catcher := &errorCatcher{UI: ui.options.UI}
|
catcher := &errorCatcher{UI: ui.options.UI}
|
||||||
options := *ui.options
|
options := *ui.options
|
||||||
options.UI = catcher
|
options.UI = catcher
|
||||||
_, rpt, err := generateRawReport(ui.prof, cmd, v, &options)
|
_, rpt, err := generateRawReport(ui.prof, cmd, cfg, &options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
ui.options.UI.PrintErr(err)
|
ui.options.UI.PrintErr(err)
|
||||||
|
|
@ -271,7 +267,7 @@ func (ui *webInterface) makeReport(w http.ResponseWriter, req *http.Request,
|
||||||
}
|
}
|
||||||
|
|
||||||
// render generates html using the named template based on the contents of data.
|
// render generates html using the named template based on the contents of data.
|
||||||
func (ui *webInterface) render(w http.ResponseWriter, tmpl string,
|
func (ui *webInterface) render(w http.ResponseWriter, req *http.Request, tmpl string,
|
||||||
rpt *report.Report, errList, legend []string, data webArgs) {
|
rpt *report.Report, errList, legend []string, data webArgs) {
|
||||||
file := getFromLegend(legend, "File: ", "unknown")
|
file := getFromLegend(legend, "File: ", "unknown")
|
||||||
profile := getFromLegend(legend, "Type: ", "unknown")
|
profile := getFromLegend(legend, "Type: ", "unknown")
|
||||||
|
|
@ -281,6 +277,8 @@ func (ui *webInterface) render(w http.ResponseWriter, tmpl string,
|
||||||
data.SampleTypes = sampleTypes(ui.prof)
|
data.SampleTypes = sampleTypes(ui.prof)
|
||||||
data.Legend = legend
|
data.Legend = legend
|
||||||
data.Help = ui.help
|
data.Help = ui.help
|
||||||
|
data.Configs = configMenu(ui.settingsFile, *req.URL)
|
||||||
|
|
||||||
html := &bytes.Buffer{}
|
html := &bytes.Buffer{}
|
||||||
if err := ui.templates.ExecuteTemplate(html, tmpl, data); err != nil {
|
if err := ui.templates.ExecuteTemplate(html, tmpl, data); err != nil {
|
||||||
http.Error(w, "internal template error", http.StatusInternalServerError)
|
http.Error(w, "internal template error", http.StatusInternalServerError)
|
||||||
|
|
@ -293,7 +291,7 @@ func (ui *webInterface) render(w http.ResponseWriter, tmpl string,
|
||||||
|
|
||||||
// dot generates a web page containing an svg diagram.
|
// dot generates a web page containing an svg diagram.
|
||||||
func (ui *webInterface) dot(w http.ResponseWriter, req *http.Request) {
|
func (ui *webInterface) dot(w http.ResponseWriter, req *http.Request) {
|
||||||
rpt, errList := ui.makeReport(w, req, []string{"svg"})
|
rpt, errList := ui.makeReport(w, req, []string{"svg"}, nil)
|
||||||
if rpt == nil {
|
if rpt == nil {
|
||||||
return // error already reported
|
return // error already reported
|
||||||
}
|
}
|
||||||
|
|
@ -320,7 +318,7 @@ func (ui *webInterface) dot(w http.ResponseWriter, req *http.Request) {
|
||||||
nodes = append(nodes, n.Info.Name)
|
nodes = append(nodes, n.Info.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.render(w, "graph", rpt, errList, legend, webArgs{
|
ui.render(w, req, "graph", rpt, errList, legend, webArgs{
|
||||||
HTMLBody: template.HTML(string(svg)),
|
HTMLBody: template.HTML(string(svg)),
|
||||||
Nodes: nodes,
|
Nodes: nodes,
|
||||||
})
|
})
|
||||||
|
|
@ -345,7 +343,9 @@ func dotToSvg(dot []byte) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *webInterface) top(w http.ResponseWriter, req *http.Request) {
|
func (ui *webInterface) top(w http.ResponseWriter, req *http.Request) {
|
||||||
rpt, errList := ui.makeReport(w, req, []string{"top"}, "nodecount", "500")
|
rpt, errList := ui.makeReport(w, req, []string{"top"}, func(cfg *config) {
|
||||||
|
cfg.NodeCount = 500
|
||||||
|
})
|
||||||
if rpt == nil {
|
if rpt == nil {
|
||||||
return // error already reported
|
return // error already reported
|
||||||
}
|
}
|
||||||
|
|
@ -355,7 +355,7 @@ func (ui *webInterface) top(w http.ResponseWriter, req *http.Request) {
|
||||||
nodes = append(nodes, item.Name)
|
nodes = append(nodes, item.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.render(w, "top", rpt, errList, legend, webArgs{
|
ui.render(w, req, "top", rpt, errList, legend, webArgs{
|
||||||
Top: top,
|
Top: top,
|
||||||
Nodes: nodes,
|
Nodes: nodes,
|
||||||
})
|
})
|
||||||
|
|
@ -364,7 +364,7 @@ func (ui *webInterface) top(w http.ResponseWriter, req *http.Request) {
|
||||||
// disasm generates a web page containing disassembly.
|
// disasm generates a web page containing disassembly.
|
||||||
func (ui *webInterface) disasm(w http.ResponseWriter, req *http.Request) {
|
func (ui *webInterface) disasm(w http.ResponseWriter, req *http.Request) {
|
||||||
args := []string{"disasm", req.URL.Query().Get("f")}
|
args := []string{"disasm", req.URL.Query().Get("f")}
|
||||||
rpt, errList := ui.makeReport(w, req, args)
|
rpt, errList := ui.makeReport(w, req, args, nil)
|
||||||
if rpt == nil {
|
if rpt == nil {
|
||||||
return // error already reported
|
return // error already reported
|
||||||
}
|
}
|
||||||
|
|
@ -377,7 +377,7 @@ func (ui *webInterface) disasm(w http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
legend := report.ProfileLabels(rpt)
|
legend := report.ProfileLabels(rpt)
|
||||||
ui.render(w, "plaintext", rpt, errList, legend, webArgs{
|
ui.render(w, req, "plaintext", rpt, errList, legend, webArgs{
|
||||||
TextBody: out.String(),
|
TextBody: out.String(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -387,7 +387,7 @@ func (ui *webInterface) disasm(w http.ResponseWriter, req *http.Request) {
|
||||||
// data.
|
// data.
|
||||||
func (ui *webInterface) source(w http.ResponseWriter, req *http.Request) {
|
func (ui *webInterface) source(w http.ResponseWriter, req *http.Request) {
|
||||||
args := []string{"weblist", req.URL.Query().Get("f")}
|
args := []string{"weblist", req.URL.Query().Get("f")}
|
||||||
rpt, errList := ui.makeReport(w, req, args)
|
rpt, errList := ui.makeReport(w, req, args, nil)
|
||||||
if rpt == nil {
|
if rpt == nil {
|
||||||
return // error already reported
|
return // error already reported
|
||||||
}
|
}
|
||||||
|
|
@ -401,7 +401,7 @@ func (ui *webInterface) source(w http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
legend := report.ProfileLabels(rpt)
|
legend := report.ProfileLabels(rpt)
|
||||||
ui.render(w, "sourcelisting", rpt, errList, legend, webArgs{
|
ui.render(w, req, "sourcelisting", rpt, errList, legend, webArgs{
|
||||||
HTMLBody: template.HTML(body.String()),
|
HTMLBody: template.HTML(body.String()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -409,7 +409,9 @@ func (ui *webInterface) source(w http.ResponseWriter, req *http.Request) {
|
||||||
// peek generates a web page listing callers/callers.
|
// peek generates a web page listing callers/callers.
|
||||||
func (ui *webInterface) peek(w http.ResponseWriter, req *http.Request) {
|
func (ui *webInterface) peek(w http.ResponseWriter, req *http.Request) {
|
||||||
args := []string{"peek", req.URL.Query().Get("f")}
|
args := []string{"peek", req.URL.Query().Get("f")}
|
||||||
rpt, errList := ui.makeReport(w, req, args, "lines", "t")
|
rpt, errList := ui.makeReport(w, req, args, func(cfg *config) {
|
||||||
|
cfg.Granularity = "lines"
|
||||||
|
})
|
||||||
if rpt == nil {
|
if rpt == nil {
|
||||||
return // error already reported
|
return // error already reported
|
||||||
}
|
}
|
||||||
|
|
@ -422,11 +424,30 @@ func (ui *webInterface) peek(w http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
legend := report.ProfileLabels(rpt)
|
legend := report.ProfileLabels(rpt)
|
||||||
ui.render(w, "plaintext", rpt, errList, legend, webArgs{
|
ui.render(w, req, "plaintext", rpt, errList, legend, webArgs{
|
||||||
TextBody: out.String(),
|
TextBody: out.String(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// saveConfig saves URL configuration.
|
||||||
|
func (ui *webInterface) saveConfig(w http.ResponseWriter, req *http.Request) {
|
||||||
|
if err := setConfig(ui.settingsFile, *req.URL); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
ui.options.UI.PrintErr(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteConfig deletes a configuration.
|
||||||
|
func (ui *webInterface) deleteConfig(w http.ResponseWriter, req *http.Request) {
|
||||||
|
name := req.URL.Query().Get("config")
|
||||||
|
if err := removeConfig(ui.settingsFile, name); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
ui.options.UI.PrintErr(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// getFromLegend returns the suffix of an entry in legend that starts
|
// getFromLegend returns the suffix of an entry in legend that starts
|
||||||
// with param. It returns def if no such entry is found.
|
// with param. It returns def if no such entry is found.
|
||||||
func getFromLegend(legend []string, param, def string) string {
|
func getFromLegend(legend []string, param, def string) string {
|
||||||
|
|
|
||||||
2
src/cmd/vendor/github.com/google/pprof/internal/plugin/plugin.go
generated
vendored
2
src/cmd/vendor/github.com/google/pprof/internal/plugin/plugin.go
generated
vendored
|
|
@ -114,7 +114,7 @@ type ObjTool interface {
|
||||||
|
|
||||||
// Disasm disassembles the named object file, starting at
|
// Disasm disassembles the named object file, starting at
|
||||||
// the start address and stopping at (before) the end address.
|
// the start address and stopping at (before) the end address.
|
||||||
Disasm(file string, start, end uint64) ([]Inst, error)
|
Disasm(file string, start, end uint64, intelSyntax bool) ([]Inst, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// An Inst is a single instruction in an assembly listing.
|
// An Inst is a single instruction in an assembly listing.
|
||||||
|
|
|
||||||
11
src/cmd/vendor/github.com/google/pprof/internal/report/report.go
generated
vendored
11
src/cmd/vendor/github.com/google/pprof/internal/report/report.go
generated
vendored
|
|
@ -79,6 +79,8 @@ type Options struct {
|
||||||
Symbol *regexp.Regexp // Symbols to include on disassembly report.
|
Symbol *regexp.Regexp // Symbols to include on disassembly report.
|
||||||
SourcePath string // Search path for source files.
|
SourcePath string // Search path for source files.
|
||||||
TrimPath string // Paths to trim from source file paths.
|
TrimPath string // Paths to trim from source file paths.
|
||||||
|
|
||||||
|
IntelSyntax bool // Whether or not to print assembly in Intel syntax.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate generates a report as directed by the Report.
|
// Generate generates a report as directed by the Report.
|
||||||
|
|
@ -438,7 +440,7 @@ func PrintAssembly(w io.Writer, rpt *Report, obj plugin.ObjTool, maxFuncs int) e
|
||||||
flatSum, cumSum := sns.Sum()
|
flatSum, cumSum := sns.Sum()
|
||||||
|
|
||||||
// Get the function assembly.
|
// Get the function assembly.
|
||||||
insts, err := obj.Disasm(s.sym.File, s.sym.Start, s.sym.End)
|
insts, err := obj.Disasm(s.sym.File, s.sym.Start, s.sym.End, o.IntelSyntax)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -1201,6 +1203,13 @@ func reportLabels(rpt *Report, g *graph.Graph, origCount, droppedNodes, droppedE
|
||||||
nodeCount, origCount))
|
nodeCount, origCount))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Help new users understand the graph.
|
||||||
|
// A new line is intentionally added here to better show this message.
|
||||||
|
if fullHeaders {
|
||||||
|
label = append(label, "\\lSee https://git.io/JfYMW for how to read the graph")
|
||||||
|
}
|
||||||
|
|
||||||
return label
|
return label
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
6
src/cmd/vendor/github.com/google/pprof/internal/report/source.go
generated
vendored
6
src/cmd/vendor/github.com/google/pprof/internal/report/source.go
generated
vendored
|
|
@ -205,7 +205,7 @@ func PrintWebList(w io.Writer, rpt *Report, obj plugin.ObjTool, maxFiles int) er
|
||||||
ff := fileFunction{n.Info.File, n.Info.Name}
|
ff := fileFunction{n.Info.File, n.Info.Name}
|
||||||
fns := fileNodes[ff]
|
fns := fileNodes[ff]
|
||||||
|
|
||||||
asm := assemblyPerSourceLine(symbols, fns, ff.fileName, obj)
|
asm := assemblyPerSourceLine(symbols, fns, ff.fileName, obj, o.IntelSyntax)
|
||||||
start, end := sourceCoordinates(asm)
|
start, end := sourceCoordinates(asm)
|
||||||
|
|
||||||
fnodes, path, err := getSourceFromFile(ff.fileName, reader, fns, start, end)
|
fnodes, path, err := getSourceFromFile(ff.fileName, reader, fns, start, end)
|
||||||
|
|
@ -239,7 +239,7 @@ func sourceCoordinates(asm map[int][]assemblyInstruction) (start, end int) {
|
||||||
// assemblyPerSourceLine disassembles the binary containing a symbol
|
// assemblyPerSourceLine disassembles the binary containing a symbol
|
||||||
// and classifies the assembly instructions according to its
|
// and classifies the assembly instructions according to its
|
||||||
// corresponding source line, annotating them with a set of samples.
|
// corresponding source line, annotating them with a set of samples.
|
||||||
func assemblyPerSourceLine(objSyms []*objSymbol, rs graph.Nodes, src string, obj plugin.ObjTool) map[int][]assemblyInstruction {
|
func assemblyPerSourceLine(objSyms []*objSymbol, rs graph.Nodes, src string, obj plugin.ObjTool, intelSyntax bool) map[int][]assemblyInstruction {
|
||||||
assembly := make(map[int][]assemblyInstruction)
|
assembly := make(map[int][]assemblyInstruction)
|
||||||
// Identify symbol to use for this collection of samples.
|
// Identify symbol to use for this collection of samples.
|
||||||
o := findMatchingSymbol(objSyms, rs)
|
o := findMatchingSymbol(objSyms, rs)
|
||||||
|
|
@ -248,7 +248,7 @@ func assemblyPerSourceLine(objSyms []*objSymbol, rs graph.Nodes, src string, obj
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract assembly for matched symbol
|
// Extract assembly for matched symbol
|
||||||
insts, err := obj.Disasm(o.sym.File, o.sym.Start, o.sym.End)
|
insts, err := obj.Disasm(o.sym.File, o.sym.Start, o.sym.End, intelSyntax)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return assembly
|
return assembly
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
src/cmd/vendor/github.com/google/pprof/profile/profile.go
generated
vendored
10
src/cmd/vendor/github.com/google/pprof/profile/profile.go
generated
vendored
|
|
@ -398,10 +398,12 @@ func (p *Profile) CheckValid() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, ln := range l.Line {
|
for _, ln := range l.Line {
|
||||||
if f := ln.Function; f != nil {
|
f := ln.Function
|
||||||
if f.ID == 0 || functions[f.ID] != f {
|
if f == nil {
|
||||||
return fmt.Errorf("inconsistent function %p: %d", f, f.ID)
|
return fmt.Errorf("location id: %d has a line with nil function", l.ID)
|
||||||
}
|
}
|
||||||
|
if f.ID == 0 || functions[f.ID] != f {
|
||||||
|
return fmt.Errorf("inconsistent function %p: %d", f, f.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
src/cmd/vendor/modules.txt
vendored
2
src/cmd/vendor/modules.txt
vendored
|
|
@ -1,4 +1,4 @@
|
||||||
# github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3
|
# github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99
|
||||||
## explicit
|
## explicit
|
||||||
github.com/google/pprof/driver
|
github.com/google/pprof/driver
|
||||||
github.com/google/pprof/internal/binutils
|
github.com/google/pprof/internal/binutils
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build darwin,amd64
|
// +build darwin,!ios
|
||||||
|
|
||||||
// Package macOS provides cgo-less wrappers for Core Foundation and
|
// Package macOS provides cgo-less wrappers for Core Foundation and
|
||||||
// Security.framework, similarly to how package syscall provides access to
|
// Security.framework, similarly to how package syscall provides access to
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build darwin,amd64
|
// +build darwin,!ios
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build darwin,amd64
|
// +build darwin,!ios
|
||||||
|
|
||||||
package macOS
|
package macOS
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build darwin,amd64
|
// +build darwin,!ios
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
package x509
|
package x509
|
||||||
|
|
||||||
//go:generate go run root_darwin_ios_gen.go -version 55161.80.1
|
//go:generate go run root_ios_gen.go -version 55161.80.1
|
||||||
|
|
||||||
import "sync"
|
import "sync"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by root_darwin_ios_gen.go -version 55161.80.1; DO NOT EDIT.
|
// Code generated by root_ios_gen.go -version 55161.80.1; DO NOT EDIT.
|
||||||
// Update the version in root.go and regenerate with "go generate".
|
// Update the version in root.go and regenerate with "go generate".
|
||||||
|
|
||||||
// +build darwin,arm64 darwin,amd64,ios
|
// +build ios
|
||||||
// +build !x509omitbundledroots
|
// +build !x509omitbundledroots
|
||||||
|
|
||||||
package x509
|
package x509
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
// +build ignore
|
// +build ignore
|
||||||
|
|
||||||
// Generates root_darwin_iosx.go.
|
// Generates root_ios.go.
|
||||||
//
|
//
|
||||||
// As of iOS 13, there is no API for querying the system trusted X.509 root
|
// As of iOS 13, there is no API for querying the system trusted X.509 root
|
||||||
// certificates.
|
// certificates.
|
||||||
|
|
@ -37,10 +37,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Temporarily name the file _iosx.go, to avoid restricting it to GOOS=ios,
|
var output = flag.String("output", "root_ios.go", "file name to write")
|
||||||
// as this is also used for darwin/arm64 (macOS).
|
|
||||||
// TODO: maybe use darwin/amd64 implementation on macOS arm64?
|
|
||||||
var output = flag.String("output", "root_darwin_iosx.go", "file name to write")
|
|
||||||
var version = flag.String("version", "", "security_certificates version")
|
var version = flag.String("version", "", "security_certificates version")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
if *version == "" {
|
if *version == "" {
|
||||||
|
|
@ -159,10 +156,10 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const header = `// Code generated by root_darwin_ios_gen.go -version %s; DO NOT EDIT.
|
const header = `// Code generated by root_ios_gen.go -version %s; DO NOT EDIT.
|
||||||
// Update the version in root.go and regenerate with "go generate".
|
// Update the version in root.go and regenerate with "go generate".
|
||||||
|
|
||||||
// +build darwin,arm64 darwin,amd64,ios
|
// +build ios
|
||||||
// +build !x509omitbundledroots
|
// +build !x509omitbundledroots
|
||||||
|
|
||||||
package x509
|
package x509
|
||||||
|
|
@ -409,19 +409,20 @@ type Package struct {
|
||||||
BinaryOnly bool // cannot be rebuilt from source (has //go:binary-only-package comment)
|
BinaryOnly bool // cannot be rebuilt from source (has //go:binary-only-package comment)
|
||||||
|
|
||||||
// Source files
|
// Source files
|
||||||
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
|
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
|
||||||
CgoFiles []string // .go source files that import "C"
|
CgoFiles []string // .go source files that import "C"
|
||||||
IgnoredGoFiles []string // .go source files ignored for this build
|
IgnoredGoFiles []string // .go source files ignored for this build (including ignored _test.go files)
|
||||||
InvalidGoFiles []string // .go source files with detected problems (parse error, wrong package name, and so on)
|
InvalidGoFiles []string // .go source files with detected problems (parse error, wrong package name, and so on)
|
||||||
CFiles []string // .c source files
|
IgnoredOtherFiles []string // non-.go source files ignored for this build
|
||||||
CXXFiles []string // .cc, .cpp and .cxx source files
|
CFiles []string // .c source files
|
||||||
MFiles []string // .m (Objective-C) source files
|
CXXFiles []string // .cc, .cpp and .cxx source files
|
||||||
HFiles []string // .h, .hh, .hpp and .hxx source files
|
MFiles []string // .m (Objective-C) source files
|
||||||
FFiles []string // .f, .F, .for and .f90 Fortran source files
|
HFiles []string // .h, .hh, .hpp and .hxx source files
|
||||||
SFiles []string // .s source files
|
FFiles []string // .f, .F, .for and .f90 Fortran source files
|
||||||
SwigFiles []string // .swig files
|
SFiles []string // .s source files
|
||||||
SwigCXXFiles []string // .swigcxx files
|
SwigFiles []string // .swig files
|
||||||
SysoFiles []string // .syso system object files to add to archive
|
SwigCXXFiles []string // .swigcxx files
|
||||||
|
SysoFiles []string // .syso system object files to add to archive
|
||||||
|
|
||||||
// Cgo directives
|
// Cgo directives
|
||||||
CgoCFLAGS []string // Cgo CFLAGS directives
|
CgoCFLAGS []string // Cgo CFLAGS directives
|
||||||
|
|
@ -816,46 +817,28 @@ Found:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !match {
|
if !match {
|
||||||
if ext == ".go" {
|
if strings.HasPrefix(name, "_") || strings.HasPrefix(name, ".") {
|
||||||
|
// not due to build constraints - don't report
|
||||||
|
} else if ext == ".go" {
|
||||||
p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
|
p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
|
||||||
|
} else if fileListForExt(p, ext) != nil {
|
||||||
|
p.IgnoredOtherFiles = append(p.IgnoredOtherFiles, name)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Going to save the file. For non-Go files, can stop here.
|
// Going to save the file. For non-Go files, can stop here.
|
||||||
switch ext {
|
switch ext {
|
||||||
case ".c":
|
case ".go":
|
||||||
p.CFiles = append(p.CFiles, name)
|
// keep going
|
||||||
continue
|
|
||||||
case ".cc", ".cpp", ".cxx":
|
|
||||||
p.CXXFiles = append(p.CXXFiles, name)
|
|
||||||
continue
|
|
||||||
case ".m":
|
|
||||||
p.MFiles = append(p.MFiles, name)
|
|
||||||
continue
|
|
||||||
case ".h", ".hh", ".hpp", ".hxx":
|
|
||||||
p.HFiles = append(p.HFiles, name)
|
|
||||||
continue
|
|
||||||
case ".f", ".F", ".for", ".f90":
|
|
||||||
p.FFiles = append(p.FFiles, name)
|
|
||||||
continue
|
|
||||||
case ".s":
|
|
||||||
p.SFiles = append(p.SFiles, name)
|
|
||||||
continue
|
|
||||||
case ".S", ".sx":
|
case ".S", ".sx":
|
||||||
|
// special case for cgo, handled at end
|
||||||
Sfiles = append(Sfiles, name)
|
Sfiles = append(Sfiles, name)
|
||||||
continue
|
continue
|
||||||
case ".swig":
|
default:
|
||||||
p.SwigFiles = append(p.SwigFiles, name)
|
if list := fileListForExt(p, ext); list != nil {
|
||||||
continue
|
*list = append(*list, name)
|
||||||
case ".swigcxx":
|
}
|
||||||
p.SwigCXXFiles = append(p.SwigCXXFiles, name)
|
|
||||||
continue
|
|
||||||
case ".syso":
|
|
||||||
// binary objects to add to package archive
|
|
||||||
// Likely of the form foo_windows.syso, but
|
|
||||||
// the name was vetted above with goodOSArchFile.
|
|
||||||
p.SysoFiles = append(p.SysoFiles, name)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -996,6 +979,9 @@ Found:
|
||||||
if len(p.CgoFiles) > 0 {
|
if len(p.CgoFiles) > 0 {
|
||||||
p.SFiles = append(p.SFiles, Sfiles...)
|
p.SFiles = append(p.SFiles, Sfiles...)
|
||||||
sort.Strings(p.SFiles)
|
sort.Strings(p.SFiles)
|
||||||
|
} else {
|
||||||
|
p.IgnoredOtherFiles = append(p.IgnoredOtherFiles, Sfiles...)
|
||||||
|
sort.Strings(p.IgnoredOtherFiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
if badGoError != nil {
|
if badGoError != nil {
|
||||||
|
|
@ -1007,6 +993,30 @@ Found:
|
||||||
return p, pkgerr
|
return p, pkgerr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fileListForExt(p *Package, ext string) *[]string {
|
||||||
|
switch ext {
|
||||||
|
case ".c":
|
||||||
|
return &p.CFiles
|
||||||
|
case ".cc", ".cpp", ".cxx":
|
||||||
|
return &p.CXXFiles
|
||||||
|
case ".m":
|
||||||
|
return &p.MFiles
|
||||||
|
case ".h", ".hh", ".hpp", ".hxx":
|
||||||
|
return &p.HFiles
|
||||||
|
case ".f", ".F", ".for", ".f90":
|
||||||
|
return &p.FFiles
|
||||||
|
case ".s", ".S", ".sx":
|
||||||
|
return &p.SFiles
|
||||||
|
case ".swig":
|
||||||
|
return &p.SwigFiles
|
||||||
|
case ".swigcxx":
|
||||||
|
return &p.SwigCXXFiles
|
||||||
|
case ".syso":
|
||||||
|
return &p.SysoFiles
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var errNoModules = errors.New("not using modules")
|
var errNoModules = errors.New("not using modules")
|
||||||
|
|
||||||
// importGo checks whether it can use the go command to find the directory for path.
|
// importGo checks whether it can use the go command to find the directory for path.
|
||||||
|
|
@ -1302,6 +1312,8 @@ func (ctxt *Context) MatchFile(dir, name string) (match bool, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var dummyPkg Package
|
||||||
|
|
||||||
// matchFile determines whether the file with the given name in the given directory
|
// matchFile determines whether the file with the given name in the given directory
|
||||||
// should be included in the package being constructed.
|
// should be included in the package being constructed.
|
||||||
// It returns the data read from the file.
|
// It returns the data read from the file.
|
||||||
|
|
@ -1326,16 +1338,15 @@ func (ctxt *Context) matchFile(dir, name string, allTags map[string]bool, binary
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ext {
|
if ext != ".go" && fileListForExt(&dummyPkg, ext) == nil {
|
||||||
case ".go", ".c", ".cc", ".cxx", ".cpp", ".m", ".s", ".h", ".hh", ".hpp", ".hxx", ".f", ".F", ".f90", ".S", ".sx", ".swig", ".swigcxx":
|
// skip
|
||||||
// tentatively okay - read to make sure
|
return
|
||||||
case ".syso":
|
}
|
||||||
|
|
||||||
|
if ext == ".syso" {
|
||||||
// binary, no reading
|
// binary, no reading
|
||||||
match = true
|
match = true
|
||||||
return
|
return
|
||||||
default:
|
|
||||||
// skip
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
filename = ctxt.joinPath(dir, name)
|
filename = ctxt.joinPath(dir, name)
|
||||||
|
|
@ -1360,8 +1371,8 @@ func (ctxt *Context) matchFile(dir, name string, allTags map[string]bool, binary
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look for +build comments to accept or reject the file.
|
// Look for +build comments to accept or reject the file.
|
||||||
var sawBinaryOnly bool
|
ok, sawBinaryOnly := ctxt.shouldBuild(data, allTags)
|
||||||
if !ctxt.shouldBuild(data, allTags, &sawBinaryOnly) && !ctxt.UseAllFiles {
|
if !ok && !ctxt.UseAllFiles {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1411,10 +1422,11 @@ var binaryOnlyComment = []byte("//go:binary-only-package")
|
||||||
//
|
//
|
||||||
// marks the file as applicable only on Windows and Linux.
|
// marks the file as applicable only on Windows and Linux.
|
||||||
//
|
//
|
||||||
// If shouldBuild finds a //go:binary-only-package comment in the file,
|
// For each build tag it consults, shouldBuild sets allTags[tag] = true.
|
||||||
// it sets *binaryOnly to true. Otherwise it does not change *binaryOnly.
|
|
||||||
//
|
//
|
||||||
func (ctxt *Context) shouldBuild(content []byte, allTags map[string]bool, binaryOnly *bool) bool {
|
// shouldBuild reports whether the file should be built
|
||||||
|
// and whether a //go:binary-only-package comment was found.
|
||||||
|
func (ctxt *Context) shouldBuild(content []byte, allTags map[string]bool) (shouldBuild bool, binaryOnly bool) {
|
||||||
sawBinaryOnly := false
|
sawBinaryOnly := false
|
||||||
|
|
||||||
// Pass 1. Identify leading run of // comments and blank lines,
|
// Pass 1. Identify leading run of // comments and blank lines,
|
||||||
|
|
@ -1441,7 +1453,7 @@ func (ctxt *Context) shouldBuild(content []byte, allTags map[string]bool, binary
|
||||||
|
|
||||||
// Pass 2. Process each line in the run.
|
// Pass 2. Process each line in the run.
|
||||||
p = content
|
p = content
|
||||||
allok := true
|
shouldBuild = true
|
||||||
for len(p) > 0 {
|
for len(p) > 0 {
|
||||||
line := p
|
line := p
|
||||||
if i := bytes.IndexByte(line, '\n'); i >= 0 {
|
if i := bytes.IndexByte(line, '\n'); i >= 0 {
|
||||||
|
|
@ -1468,17 +1480,13 @@ func (ctxt *Context) shouldBuild(content []byte, allTags map[string]bool, binary
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
allok = false
|
shouldBuild = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if binaryOnly != nil && sawBinaryOnly {
|
return shouldBuild, sawBinaryOnly
|
||||||
*binaryOnly = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return allok
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// saveCgo saves the information from the #cgo lines in the import "C" comment.
|
// saveCgo saves the information from the #cgo lines in the import "C" comment.
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ package build
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
"internal/testenv"
|
"internal/testenv"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
@ -120,7 +121,7 @@ func TestMultiplePackageImport(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLocalDirectory(t *testing.T) {
|
func TestLocalDirectory(t *testing.T) {
|
||||||
if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" {
|
if runtime.GOOS == "ios" {
|
||||||
t.Skipf("skipping on %s/%s, no valid GOROOT", runtime.GOOS, runtime.GOARCH)
|
t.Skipf("skipping on %s/%s, no valid GOROOT", runtime.GOOS, runtime.GOARCH)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -138,48 +139,78 @@ func TestLocalDirectory(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var shouldBuildTests = []struct {
|
||||||
|
content string
|
||||||
|
tags map[string]bool
|
||||||
|
binaryOnly bool
|
||||||
|
shouldBuild bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
content: "// +build yes\n\n" +
|
||||||
|
"package main\n",
|
||||||
|
tags: map[string]bool{"yes": true},
|
||||||
|
shouldBuild: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: "// +build no yes\n\n" +
|
||||||
|
"package main\n",
|
||||||
|
tags: map[string]bool{"yes": true, "no": true},
|
||||||
|
shouldBuild: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: "// +build no,yes no\n\n" +
|
||||||
|
"package main\n",
|
||||||
|
tags: map[string]bool{"yes": true, "no": true},
|
||||||
|
shouldBuild: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: "// +build cgo\n\n" +
|
||||||
|
"// Copyright The Go Authors.\n\n" +
|
||||||
|
"// This package implements parsing of tags like\n" +
|
||||||
|
"// +build tag1\n" +
|
||||||
|
"package build",
|
||||||
|
tags: map[string]bool{"cgo": true},
|
||||||
|
shouldBuild: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: "// Copyright The Go Authors.\n\n" +
|
||||||
|
"package build\n\n" +
|
||||||
|
"// shouldBuild checks tags given by lines of the form\n" +
|
||||||
|
"// +build tag\n" +
|
||||||
|
"func shouldBuild(content []byte)\n",
|
||||||
|
tags: map[string]bool{},
|
||||||
|
shouldBuild: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// too close to package line
|
||||||
|
content: "// +build yes\n" +
|
||||||
|
"package main\n",
|
||||||
|
tags: map[string]bool{},
|
||||||
|
shouldBuild: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// too close to package line
|
||||||
|
content: "// +build no\n" +
|
||||||
|
"package main\n",
|
||||||
|
tags: map[string]bool{},
|
||||||
|
shouldBuild: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
func TestShouldBuild(t *testing.T) {
|
func TestShouldBuild(t *testing.T) {
|
||||||
const file1 = "// +build tag1\n\n" +
|
for i, tt := range shouldBuildTests {
|
||||||
"package main\n"
|
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
||||||
want1 := map[string]bool{"tag1": true}
|
ctx := &Context{BuildTags: []string{"yes"}}
|
||||||
|
tags := map[string]bool{}
|
||||||
const file2 = "// +build cgo\n\n" +
|
shouldBuild, binaryOnly := ctx.shouldBuild([]byte(tt.content), tags)
|
||||||
"// This package implements parsing of tags like\n" +
|
if shouldBuild != tt.shouldBuild || binaryOnly != tt.binaryOnly || !reflect.DeepEqual(tags, tt.tags) {
|
||||||
"// +build tag1\n" +
|
t.Errorf("mismatch:\n"+
|
||||||
"package build"
|
"have shouldBuild=%v, binaryOnly=%v, tags=%v\n"+
|
||||||
want2 := map[string]bool{"cgo": true}
|
"want shouldBuild=%v, binaryOnly=%v, tags=%v",
|
||||||
|
shouldBuild, binaryOnly, tags,
|
||||||
const file3 = "// Copyright The Go Authors.\n\n" +
|
tt.shouldBuild, tt.binaryOnly, tt.tags)
|
||||||
"package build\n\n" +
|
}
|
||||||
"// shouldBuild checks tags given by lines of the form\n" +
|
})
|
||||||
"// +build tag\n" +
|
|
||||||
"func shouldBuild(content []byte)\n"
|
|
||||||
want3 := map[string]bool{}
|
|
||||||
|
|
||||||
ctx := &Context{BuildTags: []string{"tag1"}}
|
|
||||||
m := map[string]bool{}
|
|
||||||
if !ctx.shouldBuild([]byte(file1), m, nil) {
|
|
||||||
t.Errorf("shouldBuild(file1) = false, want true")
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(m, want1) {
|
|
||||||
t.Errorf("shouldBuild(file1) tags = %v, want %v", m, want1)
|
|
||||||
}
|
|
||||||
|
|
||||||
m = map[string]bool{}
|
|
||||||
if ctx.shouldBuild([]byte(file2), m, nil) {
|
|
||||||
t.Errorf("shouldBuild(file2) = true, want false")
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(m, want2) {
|
|
||||||
t.Errorf("shouldBuild(file2) tags = %v, want %v", m, want2)
|
|
||||||
}
|
|
||||||
|
|
||||||
m = map[string]bool{}
|
|
||||||
ctx = &Context{BuildTags: nil}
|
|
||||||
if !ctx.shouldBuild([]byte(file3), m, nil) {
|
|
||||||
t.Errorf("shouldBuild(file3) = false, want true")
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(m, want3) {
|
|
||||||
t.Errorf("shouldBuild(file3) tags = %v, want %v", m, want3)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -250,7 +281,7 @@ func TestMatchFile(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestImportCmd(t *testing.T) {
|
func TestImportCmd(t *testing.T) {
|
||||||
if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" {
|
if runtime.GOOS == "ios" {
|
||||||
t.Skipf("skipping on %s/%s, no valid GOROOT", runtime.GOOS, runtime.GOARCH)
|
t.Skipf("skipping on %s/%s, no valid GOROOT", runtime.GOOS, runtime.GOARCH)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -318,7 +318,6 @@ var depsRules = `
|
||||||
# so large dependencies must be kept out.
|
# so large dependencies must be kept out.
|
||||||
# This is a long-looking list but most of these
|
# This is a long-looking list but most of these
|
||||||
# are small with few dependencies.
|
# are small with few dependencies.
|
||||||
# math/rand should probably be removed at some point.
|
|
||||||
CGO,
|
CGO,
|
||||||
golang.org/x/net/dns/dnsmessage,
|
golang.org/x/net/dns/dnsmessage,
|
||||||
golang.org/x/net/lif,
|
golang.org/x/net/lif,
|
||||||
|
|
@ -327,11 +326,11 @@ var depsRules = `
|
||||||
internal/poll,
|
internal/poll,
|
||||||
internal/singleflight,
|
internal/singleflight,
|
||||||
internal/race,
|
internal/race,
|
||||||
math/rand,
|
|
||||||
os
|
os
|
||||||
< net;
|
< net;
|
||||||
|
|
||||||
fmt, unicode !< net;
|
fmt, unicode !< net;
|
||||||
|
math/rand !< net; # net uses runtime instead
|
||||||
|
|
||||||
# NET is net plus net-helper packages.
|
# NET is net plus net-helper packages.
|
||||||
FMT, net
|
FMT, net
|
||||||
|
|
@ -449,7 +448,7 @@ var depsRules = `
|
||||||
OS, compress/gzip, regexp
|
OS, compress/gzip, regexp
|
||||||
< internal/profile;
|
< internal/profile;
|
||||||
|
|
||||||
html/template, internal/profile, net/http, runtime/pprof, runtime/trace
|
html, internal/profile, net/http, runtime/pprof, runtime/trace
|
||||||
< net/http/pprof;
|
< net/http/pprof;
|
||||||
|
|
||||||
# RPC
|
# RPC
|
||||||
|
|
@ -479,7 +478,7 @@ var depsRules = `
|
||||||
CGO, OS, fmt
|
CGO, OS, fmt
|
||||||
< os/signal/internal/pty;
|
< os/signal/internal/pty;
|
||||||
|
|
||||||
NET, testing
|
NET, testing, math/rand
|
||||||
< golang.org/x/net/nettest;
|
< golang.org/x/net/nettest;
|
||||||
|
|
||||||
FMT, container/heap, math/rand
|
FMT, container/heap, math/rand
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,11 @@
|
||||||
// These hash functions are intended to be used to implement hash tables or
|
// These hash functions are intended to be used to implement hash tables or
|
||||||
// other data structures that need to map arbitrary strings or byte
|
// other data structures that need to map arbitrary strings or byte
|
||||||
// sequences to a uniform distribution on unsigned 64-bit integers.
|
// sequences to a uniform distribution on unsigned 64-bit integers.
|
||||||
|
// Each different instance of a hash table or data structure should use its own Seed.
|
||||||
//
|
//
|
||||||
// The hash functions are collision-resistant but not cryptographically secure.
|
// The hash functions are not cryptographically secure.
|
||||||
// (See crypto/sha256 and crypto/sha512 for cryptographic use.)
|
// (See crypto/sha256 and crypto/sha512 for cryptographic use.)
|
||||||
//
|
//
|
||||||
// The hash value of a given byte sequence is consistent within a
|
|
||||||
// single process, but will be different in different processes.
|
|
||||||
package maphash
|
package maphash
|
||||||
|
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
|
||||||
|
|
@ -1302,7 +1302,7 @@ func TestUnterminatedStringError(t *testing.T) {
|
||||||
t.Fatal("expected error")
|
t.Fatal("expected error")
|
||||||
}
|
}
|
||||||
str := err.Error()
|
str := err.Error()
|
||||||
if !strings.Contains(str, "X:3: unexpected unterminated raw quoted string") {
|
if !strings.Contains(str, "X:3: unterminated raw quoted string") {
|
||||||
t.Fatalf("unexpected error: %s", str)
|
t.Fatalf("unexpected error: %s", str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue