This splits the heap bitmap into separate chunks for every 64MB of the
heap and introduces an index mapping from virtual address to metadata.
It modifies the heapBits abstraction to use this two-level structure.
Finally, it modifies heapBitsSetType to unroll the bitmap into the
object itself and then copy it out if the bitmap would span
discontiguous bitmap chunks.
This is a step toward supporting general sparse heaps, which will
eliminate address space conflict failures as well as the limit on the
heap size.
It's also advantageous for 32-bit. 32-bit already supports
discontiguous heaps by always starting the arena at address 0.
However, as a result, with a contiguous bitmap, if the kernel chooses
a high address (near 2GB) for a heap mapping, the runtime is forced to
map up to 128MB of heap bitmap. Now the runtime can map sections of
the bitmap for just the parts of the address space used by the heap.
Updates #10460.
This slightly slows down the x/garbage and compilebench benchmarks.
However, I think the slowdown is acceptably small.
name old time/op new time/op delta
Template 178ms ± 1% 180ms ± 1% +0.78% (p=0.029 n=10+10)
Unicode 85.7ms ± 2% 86.5ms ± 2% ~ (p=0.089 n=10+10)
GoTypes 594ms ± 0% 599ms ± 1% +0.70% (p=0.000 n=9+9)
Compiler 2.86s ± 0% 2.87s ± 0% +0.40% (p=0.001 n=9+9)
SSA 7.23s ± 2% 7.29s ± 2% +0.94% (p=0.029 n=10+10)
Flate 116ms ± 1% 117ms ± 1% +0.99% (p=0.000 n=9+9)
GoParser 146ms ± 1% 146ms ± 0% ~ (p=0.193 n=10+7)
Reflect 399ms ± 0% 403ms ± 1% +0.89% (p=0.001 n=10+10)
Tar 173ms ± 1% 174ms ± 1% +0.91% (p=0.013 n=10+9)
XML 208ms ± 1% 210ms ± 1% +0.93% (p=0.000 n=10+10)
[Geo mean] 368ms 371ms +0.79%
name old time/op new time/op delta
Garbage/benchmem-MB=64-12 2.17ms ± 1% 2.21ms ± 1% +2.15% (p=0.000 n=20+20)
Change-Id: I037fd283221976f4f61249119d6b97b100bcbc66
Reviewed-on: https://go-review.googlesource.com/85883
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rick Hudson <rlh@golang.org>
There are various places that assume the heap bitmap is contiguous and
scan it sequentially. We're about to split up the heap bitmap. This
commit modifies all of these except heapBitsSetType to use the
heapBits abstractions so they can transparently switch to a
discontiguous bitmap.
Updates #10460. This is a step toward supporting sparse heaps.
Change-Id: I2f3994a5785e4dccb66602fb3950bbd290d9392c
Reviewed-on: https://go-review.googlesource.com/85882
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rick Hudson <rlh@golang.org>
Currently the heap bitamp is laid in reverse order in memory relative
to the heap itself. This was originally done out of "excessive
cleverness" so that computing a bitmap pointer could load only the
arena_start field and so that heaps could be more contiguous by
growing the arena and the bitmap out from a common center point.
However, this appears to have no actual performance benefit, it
complicates nearly every use of the bitmap, and it makes already
confusing code more confusing. Furthermore, it's still possible to use
a single field (the new bitmap_delta) for the bitmap pointer
computation by employing slightly different excessive cleverness.
Hence, this CL puts the bitmap into forward order.
This is a (very) updated version of CL 9404.
Change-Id: I743587cc626c4ecd81e660658bad85b54584108c
Reviewed-on: https://go-review.googlesource.com/85881
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rick Hudson <rlh@golang.org>
The logic in the spanOf* functions is open-coded in a lot of places
right now. Replace these with calls to the spanOf* functions.
Change-Id: I3cc996aceb9a529b60fea7ec6fef22008c012978
Reviewed-on: https://go-review.googlesource.com/85880
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rick Hudson <rlh@golang.org>
I think we'd forgotten about the mheap.lookup APIs when we introduced
spanOf*, but, at any rate, the spanOf* functions are used far more
widely at this point, so this CL eliminates the mheap.lookup*
functions in favor of spanOf*.
Change-Id: I15facd0856e238bb75d990e838a092b5bef5bdfc
Reviewed-on: https://go-review.googlesource.com/85879
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rick Hudson <rlh@golang.org>
heapBitsForObject does two things: it finds the base of the object and
it creates the heapBits for the base of the object. There are several
places where we just care about the base of the object. Furthermore,
greyobject only needs the heapBits in the checkmark path and can
easily compute them only when needed. Once we eliminate passing the
heap bits to grayobject, almost all uses of heapBitsForObject don't
need the heap bits.
Hence, this splits heapBitsForObject into findObject and
heapBitsForAddr (the latter already exists), removes the hbits
argument to grayobject, and replaces all heapBitsForObject calls with
calls to findObject.
In addition to making things cleaner overall, heapBitsForAddr is going
to get more expensive shortly, so it's important that we don't do it
needlessly.
Note that there's an interesting performance pitfall here. I had
originally moved findObject to mheap.go, since it made more sense
there. However, that leads to a ~2% slow down and a whopping 11%
increase in L1 icache misses on both the x/garbage and compilebench
benchmarks. This suggests we may want to be more principled about
this, but, for now, let's just leave findObject in mbitmap.go.
(I tried to make findObject small enough to inline by splitting out
the error case, but, sadly, wasn't quite able to get it under the
inlining budget.)
Change-Id: I7bcb92f383ade565d22a9f2494e4c66fd513fb10
Reviewed-on: https://go-review.googlesource.com/85878
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rick Hudson <rlh@golang.org>
These functions all serve essentially the same purpose. mlookup is
used in only one place and findObject in only three. Use
heapBitsForObject instead, which is the most optimized implementation.
(This may seem slightly silly because none of these uses care about
the heap bits, but we're about to split up the functionality of
heapBitsForObject anyway. At that point, findObject will rise from the
ashes.)
Change-Id: I906468c972be095dd23cf2404a7d4434e802f250
Reviewed-on: https://go-review.googlesource.com/85877
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rick Hudson <rlh@golang.org>
I was spelunking Linux's address space code and found that some of the
information about maximum virtual addresses in lfstack's comments was
out of date. This expands and updates the comment.
Change-Id: I9f54b23e6b266b3c5cc20259a849231fb751f6e7
Reviewed-on: https://go-review.googlesource.com/85875
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rick Hudson <rlh@golang.org>
During DWARF debug generation, the DW_AT_decl_line / DW_AT_decl_file
attributes for variable DIEs were being computed without taking into
account the possibility of "//line" directives. Fix things up to use
the correct src.Pos methods to pick up this info.
Fixes#23704.
Change-Id: I88c21a0e0a9602392be229252d856a6d665868e2
Reviewed-on: https://go-review.googlesource.com/92255
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Also add testdata for version 1.11 including UserTaskSpan test trace.
Change-Id: I673fb29bb3aee96a14fadc0ab860d4f5832143f5
Reviewed-on: https://go-review.googlesource.com/93795
Reviewed-by: Heschi Kreinick <heschi@google.com>
No test as the only system I know that uses 64-bit DWARF is AIX.
Change-Id: I24e225253075be188845656b6778993c2d24ebf5
Reviewed-on: https://go-review.googlesource.com/84379
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
This implements the annotation API proposed in golang.org/cl/63274.
traceString is updated to protect the string map with trace.stringsLock
because the assumption that traceString is called by a single goroutine
(either at the beginning of tracing and at the end of tracing when
dumping all the symbols and function names) is no longer true.
traceString is used by the annotation apis (NewContext, StartSpan, Log)
to register frequently appearing strings (task and span names, and log
keys) after this change.
NewContext -> one or two records (EvString, EvUserTaskCreate)
end function -> one record (EvUserTaskEnd)
StartSpan -> one or two records (EvString, EvUserSpan)
span end function -> one or two records (EvString, EvUserSpan)
Log -> one or two records (EvString, EvUserLog)
EvUserLog record is of the typical record format written by traceEvent
except that it is followed by bytes that represents the value string.
In addition to runtime/trace change, this change includes
corresponding changes in internal/trace to parse the new record types.
Future work to improve efficiency:
More efficient unique task id generation instead of atomic. (per-P
counter).
Instead of a centralized trace.stringsLock, consider using per-P
string cache or something more efficient.
R=go1.11
Change-Id: Iec9276c6c51e5be441ccd52dec270f1e3b153970
Reviewed-on: https://go-review.googlesource.com/71690
Reviewed-by: Austin Clements <austin@google.com>
This CL presents the proposed user annotation API skeleton.
This CL bumps up the trace version to 1.11.
Design doc https://goo.gl/iqJfJ3
Implementation CLs are followed.
The API introduces three basic building blocks. Log, Span, and Task.
Log is for basic logging. When called, the message will be recorded
to the trace along with timestamp, goroutine id, and stack info.
trace.Log(ctx, messageType message)
Span can be thought as an extension of log to record interesting
time interval during a goroutine's execution. A span is local to a
goroutine by definition.
trace.WithSpan(ctx, "doVeryExpensiveOp", func(ctx context) {
/* do something very expensive */
})
Task is higher-level concept that aids tracing of complex operations
that encompass multiple goroutines or are asynchronous.
For example, an RPC request, a HTTP request, a file write, or a
batch job can be traced with a Task.
Note we chose to design the API around context.Context so it allows
easier integration with other tracing tools, often designed around
context.Context as well. Log and WithSpan APIs recognize the task
information embedded in the context and record it in the trace as
well. That allows the Go execution tracer to associate and group
the spans and log messages based on the task information.
In order to create a Task,
ctx, end := trace.NewContext(ctx, "myTask")
defer end()
The Go execution tracer measures the time between the task created
and the task ended for the task latency.
More discussion history in golang.org/cl/59572.
Update #16619
R=go1.11
Change-Id: I59a937048294dafd23a75cf1723c6db461b193cd
Reviewed-on: https://go-review.googlesource.com/63274
Reviewed-by: Austin Clements <austin@google.com>
This change adds ADD/AND/OR/XOR Immediate Shifted instructions for
ppc64x so they are usable in Go asm code. These instructions were
originally present in asm9.go, but they were only usable in that
file (as -AADD, -AANDCC, -AOR, -AXOR). These old mnemonics are now
removed.
Updates #23845
Change-Id: Ifa2fac685e8bc628cb241dd446adfc3068181826
Reviewed-on: https://go-review.googlesource.com/94115
Reviewed-by: Lynn Boger <laboger@linux.vnet.ibm.com>
Per the notice in the Go 1.10 release notes, this change drops the
support for Windows Vista or below (including Windows XP) and
simplifies the code for the sake of maintenance.
There is one exception to the above. The code related to DLL and
system calls still remains in the runtime package. The remaining code
will be refined and used for supporting upcoming Windows versions in
future.
Updates #17245Fixes#23072
Change-Id: I9e2821721f25ef9b83dfbf85be2b7ee5d9023aa5
Reviewed-on: https://go-review.googlesource.com/94255
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
NetBSD supports the SOCK_CLOEXEC and SOCK_NONBLOCK flags to the socket
syscall since version 6.0. The same version also introduced the paccept
syscall which can be used to implement syscall.Accept4.
Follows CL 40895
Change-Id: I9e4e1829b0382744c7799f4e58929a53b4e193f7
Reviewed-on: https://go-review.googlesource.com/94295
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Benny Siegert <bsiegert@gmail.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
The scanner assumed that ~ really meant ^, which may be helpful when
coming from C. But ~ is not a valid Go token, and pretending that it
should be ^ can lead to confusing error messages. Better to be upfront
about it and complain about the invalid character in the first place.
This was code "inherited" from the original yacc parser which was
derived from a C compiler. It's 10 years later and we can probably
assume that people are less confused about C and Go.
Fixes#23587.
Change-Id: I8d8f9b55b0dff009b75c1530d729bf9092c5aea6
Reviewed-on: https://go-review.googlesource.com/94160
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Updating to commit 0e0e5b7254e076a62326ab7305ba49e8515f0c91
from github.com/google/pprof
Recent modifications to the vendored pprof, such as skipping
TestWebInterface to avoid starting a web browser, have all been fixed
upstream.
Change-Id: I72e11108c438e1573bf2f9216e76d157378e8d45
Reviewed-on: https://go-review.googlesource.com/93375
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Move the ELF32 and ELF64 structure definitions into their own files so
they can be reused when vDSO support is added for other architectures.
Change-Id: Id0171b4e5cea4add8635743c881e3bf3469597af
Reviewed-on: https://go-review.googlesource.com/93995
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
If copying from a slice to itself, skip the write barriers
and actual memory copies.
This happens in practice in code like this snippet from
the trim pass in the compiler, when k ends up being 0:
copy(s.Values[k:], s.Values[:m])
Change-Id: Ie6924acfd56151f874d87f1d7f1f74320b4c4f10
Reviewed-on: https://go-review.googlesource.com/94023
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
The runtime.hmap type is known at compile time.
Using new(hmap) avoids loading the hmap type from the maptype
supplied as an argument to makemap which is only known at runtime.
This change makes makemap consistent with makemap_small
by using new(hmap) instead of newobject in both functions.
Change-Id: Ia47acfda527e8a71d15a1a7a4c2b54fb923515eb
Reviewed-on: https://go-review.googlesource.com/91775
Run-TryBot: Martin Möhrmann <moehrmann@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
The runtime builtin functions that are tested in append_test.go
are defined in slice.go. Renaming the test file to slice_test.go
makes this relation explicit with a common file name prefix.
Change-Id: I2f89ec23a6077fe6b80d2161efc760df828c8cd4
Reviewed-on: https://go-review.googlesource.com/90655
Run-TryBot: Martin Möhrmann <moehrmann@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Also permit passing flags to pkg-config, as we used to.
Also change the error message to refer to https://golang.org/s/invalidflag.
Fixes#23749
Change-Id: I3fbeb4c346610e6fd55e8720e720b0a40e352ab5
Reviewed-on: https://go-review.googlesource.com/93836
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
1) Fix the doc string for syntax.Parse: The returned AST is
always nil if there was an error and an error handler is missing.
2) Adjust the syntax Print and Dump tests such that they print and
dump the AST even in the presence of errors.
Change-Id: If658eabdcc83f578d815070bc65d1a5f6cfaddfc
Reviewed-on: https://go-review.googlesource.com/94157
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Assume that an expression that is not a function call in a defer/go
statement is indeed a function that is just missing its invocation.
Report the error but continue with a sane syntax tree.
Fixes#23586.
Change-Id: Ib45ebac57c83b3e39ae4a1b137ffa291dec5b50d
Reviewed-on: https://go-review.googlesource.com/94156
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Previously, if we typechecked a statement like
var x bool = p1.f == p2.f && p1.g == p2.g
we would correctly update the '&&' node's type from 'untyped bool' to
'bool', but the '==' nodes would stay 'untyped bool'. This is
inconsistent, and caused consistency checks during walk to fail.
This CL doesn't pass toolstash because it seems to slightly affect the
register allocator's heuristics. (Presumably 'untyped bool's were
previously making it all the way through SSA?)
Fixes#23414.
Change-Id: Ia85f8cfc69b5ba35dfeb157f4edf57612ecc3285
Reviewed-on: https://go-review.googlesource.com/94022
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
Also, remove parser.error method (in favor of parser.errorAt) as it's only
used twice.
This is a purely cosmetic change.
Change-Id: Idb3b8b50f1c2e4d10de2ffb1c1184ceba8f7de8a
Reviewed-on: https://go-review.googlesource.com/94030
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
It's always useful to distinguish "bool" and "string" from "untyped
bool" and "untyped string", so change typefmt to do this
unconditionally.
Also, while here, replace a bare 0 with its named constant FErr.
Fixes#23833.
Change-Id: I3fcb8d7204686937439caaaf8b3973fc236d0387
Reviewed-on: https://go-review.googlesource.com/94021
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
The tag was overwritten by the code for special handling unnamed
parameters.
Fixes#23045.
Change-Id: Ie2e1db3e902a07a2bbbc2a3424cea300f0a42cc3
Reviewed-on: https://go-review.googlesource.com/82775
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Rather than only ignoring runtime.Error panics, which are a very
narrow set of possible panic values, switch it such that the json
package only captures panic values that have been properly wrapped
in a jsonError struct. This ensures that only intentional panics
originating from the json package are captured.
Fixes#23012
Change-Id: I5e85200259edd2abb1b0512ce6cc288849151a6d
Reviewed-on: https://go-review.googlesource.com/94019
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This avoids odd behavior where sometimes a lot of useful
errors are not reported simply because of a small syntax
error.
Tested manually with non-existing files. (We cannot easily
add an automatic test because this is a stand-alone binary
in this directory that must be built manually.)
Fixes#23593.
Change-Id: Iff90f95413bed7d1023fa0a5c9eb0414144428a9
Reviewed-on: https://go-review.googlesource.com/93815
Reviewed-by: Alan Donovan <adonovan@google.com>
Constant fold Not of boolean constants.
Noticed while working on #23504.
Change-Id: I965705154ee7348a1a159fad4e029b922d3171b3
Reviewed-on: https://go-review.googlesource.com/88956
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Martin Möhrmann <moehrmann@google.com>
Per the language spec clarification in https://golang.org/cl/14727.
Updates #12576
Updates #12621
Change-Id: I1e459c3c11a571bd29582761faacaa9ca3178ba6
Reviewed-on: https://go-review.googlesource.com/91895
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Transform (ADDQconst SP) into (LEA SP), because lea is rematerializeable,
so this avoids register spill. We can't mark ADDQconst as rematerializeable,
because it clobbers flags. This makes go binary ~2kb smaller.
For reference here is generated code for function from bug report.
Before:
CALL "".g(SB)
MOVBLZX (SP), AX
LEAQ 8(SP), DI
TESTB AX, AX
JEQ 15
MOVQ "".p(SP), SI
DUFFCOPY $196
MOVQ $0, (SP)
PCDATA $0, $1
CALL "".h(SB)
RET
MOVQ DI, ""..autotmp_2-8(SP) // extra spill
PCDATA $0, $2
CALL "".g(SB)
MOVQ ""..autotmp_2-8(SP), DI // extra register fill
MOVQ "".p(SP), SI
DUFFCOPY $196
MOVQ $1, (SP)
PCDATA $0, $1
CALL "".h(SB)
JMP 14
END
After:
CALL "".g(SB)
MOVBLZX (SP), AX
TESTB AX, AX
JEQ 15
LEAQ 8(SP), DI
MOVQ "".p(SP), SI
DUFFCOPY $196
MOVQ $0, (SP)
PCDATA $0, $1
CALL "".h(SB)
RET
PCDATA $0, $0 // no spill
CALL "".g(SB)
LEAQ 8(SP), DI // rematerialized instead
MOVQ "".p(SP), SI
DUFFCOPY $196
MOVQ $1, (SP)
PCDATA $0, $1
CALL "".h(SB)
JMP 14
END
Fixes#22947
Change-Id: I8f33b860dc6c8828373477171b172ca2ce30074f
Reviewed-on: https://go-review.googlesource.com/81815
Run-TryBot: Ilya Tocar <ilya.tocar@intel.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Popcnt has false dependency on output register and generates
MOVQ $0, reg to break it. But recently we switched MOVQ $0, reg
encoding from xor reg, reg to actual mov $0, reg. This CL updates
code generation for popcnt to use actual XOR.
Change-Id: I4c1fc11e85758b53ba2679165fa55614ec54b27d
Reviewed-on: https://go-review.googlesource.com/82516
Run-TryBot: Ilya Tocar <ilya.tocar@intel.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Because getStackOffset is a function pointer, the compiler assumes that
its arguments escape. Pass a value instead to avoid heap allocations.
Change-Id: Ib94e5941847f134cd00e873040a4d7fcf15ced26
Reviewed-on: https://go-review.googlesource.com/92397
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
Not a big improvement, but does help edge cases like the SSA package.
Change-Id: I40e531110b97efd5f45955be477fd0f4faa8d545
Reviewed-on: https://go-review.googlesource.com/92396
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>