Commit 599199fd authored by Russ Cox's avatar Russ Cox

[dev.power64] all: merge default (dd5014ed9b01) into dev.power64

Still passes on amd64.

LGTM=austin
R=austin
CC=golang-codereviews
https://golang.org/cl/165110043
parents b55791e2 3bbc8638
...@@ -117,6 +117,9 @@ All user-defined symbols other than jump labels are written as offsets to these ...@@ -117,6 +117,9 @@ All user-defined symbols other than jump labels are written as offsets to these
<p> <p>
The <code>SB</code> pseudo-register can be thought of as the origin of memory, so the symbol <code>foo(SB)</code> The <code>SB</code> pseudo-register can be thought of as the origin of memory, so the symbol <code>foo(SB)</code>
is the name <code>foo</code> as an address in memory. is the name <code>foo</code> as an address in memory.
This form is used to name global functions and data.
Adding <code>&lt;&gt;</code> to the name, as in <code>foo&lt;&gt;(SB)</code>, makes the name
visible only in the current source file, like a top-level <code>static</code> declaration in a C file.
</p> </p>
<p> <p>
...@@ -128,8 +131,11 @@ Thus <code>0(FP)</code> is the first argument to the function, ...@@ -128,8 +131,11 @@ Thus <code>0(FP)</code> is the first argument to the function,
When referring to a function argument this way, it is conventional to place the name When referring to a function argument this way, it is conventional to place the name
at the beginning, as in <code>first_arg+0(FP)</code> and <code>second_arg+8(FP)</code>. at the beginning, as in <code>first_arg+0(FP)</code> and <code>second_arg+8(FP)</code>.
Some of the assemblers enforce this convention, rejecting plain <code>0(FP)</code> and <code>8(FP)</code>. Some of the assemblers enforce this convention, rejecting plain <code>0(FP)</code> and <code>8(FP)</code>.
For assembly functions with Go prototypes, <code>go vet</code> will check that the argument names For assembly functions with Go prototypes, <code>go</code> <code>vet</code> will check that the argument names
and offsets match. and offsets match.
On 32-bit systems, the low and high 32 bits of a 64-bit value are distinguished by adding
a <code>_lo</code> or <code>_hi</code> suffix to the name, as in <code>arg_lo+0(FP)</code> or <code>arg_hi+4(FP)</code>.
If a Go prototype does not name its result, the expected assembly name is <code>ret</code>.
</p> </p>
<p> <p>
...@@ -206,6 +212,8 @@ The frame size <code>$24-8</code> states that the function has a 24-byte frame ...@@ -206,6 +212,8 @@ The frame size <code>$24-8</code> states that the function has a 24-byte frame
and is called with 8 bytes of argument, which live on the caller's frame. and is called with 8 bytes of argument, which live on the caller's frame.
If <code>NOSPLIT</code> is not specified for the <code>TEXT</code>, If <code>NOSPLIT</code> is not specified for the <code>TEXT</code>,
the argument size must be provided. the argument size must be provided.
For assembly functions with Go prototypes, <code>go</code> <code>vet</code> will check that the
argument size is correct.
</p> </p>
<p> <p>
...@@ -216,19 +224,20 @@ simple name <code>profileloop</code>. ...@@ -216,19 +224,20 @@ simple name <code>profileloop</code>.
</p> </p>
<p> <p>
For <code>DATA</code> directives, the symbol is followed by a slash and the number Global data symbols are defined by a sequence of initializing
of bytes the memory associated with the symbol occupies. <code>DATA</code> directives followed by a <code>GLOBL</code> directive.
The arguments are optional flags and the data itself. Each <code>DATA</code> directive initializes a section of the
For instance, corresponding memory.
</p> The memory not explicitly initialized is zeroed.
The general form of the <code>DATA</code> directive is
<pre> <pre>
DATA runtime·isplan9(SB)/4, $1 DATA symbol+offset(SB)/width, value
</pre> </pre>
<p> <p>
declares the local symbol <code>runtime·isplan9</code> of size 4 and value 1. which initializes the symbol memory at the given offset and width with the given value.
Again the symbol has the middle dot and is offset from <code>SB</code>. The <code>DATA</code> directives for a given symbol must be written with increasing offsets.
</p> </p>
<p> <p>
...@@ -237,15 +246,26 @@ The arguments are optional flags and the size of the data being declared as a gl ...@@ -237,15 +246,26 @@ The arguments are optional flags and the size of the data being declared as a gl
which will have initial value all zeros unless a <code>DATA</code> directive which will have initial value all zeros unless a <code>DATA</code> directive
has initialized it. has initialized it.
The <code>GLOBL</code> directive must follow any corresponding <code>DATA</code> directives. The <code>GLOBL</code> directive must follow any corresponding <code>DATA</code> directives.
This example </p>
<p>
For example,
</p> </p>
<pre> <pre>
GLOBL runtime·tlsoffset(SB),$4 DATA divtab&lt;&gt;+0x00(SB)/4, $0xf4f8fcff
DATA divtab&lt;&gt;+0x04(SB)/4, $0xe6eaedf0
...
DATA divtab&lt;&gt;+0x3c(SB)/4, $0x81828384
GLOBL divtab&lt;&gt;(SB), RODATA, $64
GLOBL runtime·tlsoffset(SB), NOPTR, $4
</pre> </pre>
<p> <p>
declares <code>runtime·tlsoffset</code> to have size 4. declares and initializes <code>divtab&lt;&gt;</code>, a read-only 64-byte table of 4-byte integer values,
and declares <code>runtime·tlsoffset</code>, a 4-byte, implicitly zeroed variable that
contains no pointers.
</p> </p>
<p> <p>
...@@ -299,6 +319,80 @@ This is a wrapper function and should not count as disabling <code>recover</code ...@@ -299,6 +319,80 @@ This is a wrapper function and should not count as disabling <code>recover</code
</li> </li>
</ul> </ul>
<h3 id="runtime">Runtime Coordination</h3>
<p>
For garbage collection to run correctly, the runtime must know the
location of pointers in all global data and in most stack frames.
The Go compiler emits this information when compiling Go source files,
but assembly programs must define it explicitly.
</p>
<p>
A data symbol marked with the <code>NOPTR</code> flag (see above)
is treated as containing no pointers to runtime-allocated data.
A data symbol with the <code>RODATA</code> flag
is allocated in read-only memory and is therefore treated
as implicitly marked <code>NOPTR</code>.
A data symbol with a total size smaller than a pointer
is also treated as implicitly marked <code>NOPTR</code>.
It is not possible to define a symbol containing pointers in an assembly source file;
such a symbol must be defined in a Go source file instead.
Assembly source can still refer to the symbol by name
even without <code>DATA</code> and <code>GLOBL</code> directives.
A good general rule of thumb is to define all non-<code>RODATA</code>
symbols in Go instead of in assembly.
</p>
<p>
Each function also needs annotations giving the location of
live pointers in its arguments, results, and local stack frame.
For an assembly function with no pointer results and
either no local stack frame or no function calls,
the only requirement is to define a Go prototype for the function
in a Go source file in the same package.
For more complex situations, explicit annotation is needed.
These annotations use pseudo-instructions defined in the standard
<code>#include</code> file <code>funcdata.h</code>.
</p>
<p>
If a function has no arguments and no results,
the pointer information can be omitted.
This is indicated by an argument size annotation of <code>$<i>n</i>-0</code>
on the <code>TEXT</code> instruction.
Otherwise, pointer information must be provided by
a Go prototype for the function in a Go source file,
even for assembly functions not called directly from Go.
(The prototype will also let <code>go</code> <code>vet</code> check the argument references.)
At the start of the function, the arguments are assumed
to be initialized but the results are assumed uninitialized.
If the results will hold live pointers during a call instruction,
the function should start by zeroing the results and then
executing the pseudo-instruction <code>GO_RESULTS_INITIALIZED</code>.
This instruction records that the results are now initialized
and should be scanned during stack movement and garbage collection.
It is typically easier to arrange that assembly functions do not
return pointers or do not contain call instructions;
no assembly functions in the standard library use
<code>GO_RESULTS_INITIALIZED</code>.
</p>
<p>
If a function has no local stack frame,
the pointer information can be omitted.
This is indicated by a local frame size annotation of <code>$0-<i>n</i></code>
on the <code>TEXT</code> instruction.
The pointer information can also be omitted if the
function contains no call instructions.
Otherwise, the local stack frame must not contain pointers,
and the assembly must confirm this fact by executing the
pseudo-instruction <code>NO_LOCAL_POINTERS</code>.
Because stack resizing is implemented by moving the stack,
the stack pointer may change during any function call:
even pointers to stack data must not be kept in local variables.
</p>
<h2 id="architectures">Architecture-specific details</h2> <h2 id="architectures">Architecture-specific details</h2>
<p> <p>
...@@ -434,13 +528,10 @@ Here's how the 386 runtime defines the 64-bit atomic load function. ...@@ -434,13 +528,10 @@ Here's how the 386 runtime defines the 64-bit atomic load function.
// so actually // so actually
// void atomicload64(uint64 *res, uint64 volatile *addr); // void atomicload64(uint64 *res, uint64 volatile *addr);
TEXT runtime·atomicload64(SB), NOSPLIT, $0-8 TEXT runtime·atomicload64(SB), NOSPLIT, $0-8
MOVL 4(SP), BX MOVL ptr+0(FP), AX
MOVL 8(SP), AX LEAL ret_lo+4(FP), BX
// MOVQ (%EAX), %MM0 BYTE $0x0f; BYTE $0x6f; BYTE $0x00 // MOVQ (%EAX), %MM0
BYTE $0x0f; BYTE $0x6f; BYTE $0x00 BYTE $0x0f; BYTE $0x7f; BYTE $0x03 // MOVQ %MM0, 0(%EBX)
// MOVQ %MM0, 0(%EBX) BYTE $0x0F; BYTE $0x77 // EMMS
BYTE $0x0f; BYTE $0x7f; BYTE $0x03
// EMMS
BYTE $0x0F; BYTE $0x77
RET RET
</pre> </pre>
This diff is collapsed.
This file collects notes about what has changed since Go 1.3
and should be mentioned in the Go 1.4 release notes.
Please keep the descriptions to a single line, starting with the
package or cmd/xxx directory name, and ending in a CL number.
Please keep the list sorted (as in sort.Strings of the lines).
spec: permit for range x (CL 104680043)
the directory src/pkg has been deleted, for instance src/pkg/fmt is now just src/fmt (CL 134570043)
cmd/6l, liblink: use pc-relative addressing for all memory references, so that linking Go binaries at high addresses works (CL 125140043). This cuts the maximum size of a Go binary's text+data+bss from 4GB to 2GB.
cmd/go: import comments (CL 124940043)
cmd/go: implement "internal" (CL 120600043)
cmd/go: implement "generate" (CL 125580044)
cmd/go: disallow C sources except when using cgo (CL 149720043)
cmd/go: add test -o flag (CL 149070043)
cmd/go: redefine build -a to skip standard library in releases (CL 151730045)
cmd/go: compile and link all _test.go files during 'go test', even in packages where there are no Test functions (CL 150980043)
cmd/go: (via go/build): a GOOS prefix acts as a tag only if preceded by an underscore. this is a breaking change. (CL 147690043)
asm: make textflag.h available outside of cmd/ld (CL 128050043)
bufio: handling of empty tokens at EOF changed, may require scanner change (CL 145390043)
compress/flate, compress/gzip, compress/zlib: Reset support (https://codereview.appspot.com/97140043)
crypto/tls: add support for ALPN (RFC 7301) (CL 108710046)
crypto/tls: support programmatic selection of server certificates (CL 107400043)
encoding/asn1: optional elements with a default value will now only be omitted if they have that value (CL 86960045)
flag: it is now an error to set a flag multiple times (CL 156390043)
fmt: print type *map[T]T as &map[k:v] (CL 154870043)
encoding/csv: do not quote empty strings, quote \. (CL 164760043)
encoding/gob: remove unsafe (CL 102680045)
misc: deleted editor support; refer to https://code.google.com/p/go-wiki/wiki/IDEsAndTextEditorPlugins instead (CL 105470043)
net/http: add Request.BasicAuth method (CL 76540043)
net/http: add Transport.DialTLS hook (CL 137940043)
net/http/httputil: add ReverseProxy.ErrorLog (CL 132750043)
os: implement symlink support for windows (CL 86160044)
reflect: add type.Comparable (CL 144020043)
runtime: implement monotonic clocks on windows (CL 108700045)
runtime: memory consumption is reduced by 10-30% (CL 106260045 removes type info from heap, CL 145790043 reduces stack size to 2K (4K on plan 9 and windows))
runtime: MemStats.Mallocs now counts very small allocations missed in Go 1.3. This may break tests using runtime.ReadMemStats or testing.AllocsPerRun by giving a more accurate answer than Go 1.3 did (CL 143150043).
runtime/race: freebsd is supported (CL 107270043)
swig: Due to runtime changes Go 1.4 will require SWIG 3.0.3 (not yet released)
sync/atomic: add Value (CL 136710045)
syscall: Setuid, Setgid are disabled on linux platforms. On linux those syscalls operate on the calling thread, not the whole process. This does not match the semantics of other platforms, nor the expectations of the caller, so the operations have been disabled until issue 1435 is resolved (CL 106170043)
syscall: now frozen (CL 129820043)
testing: add Coverage (CL 98150043)
testing: add TestMain support (CL 148770043)
text/scanner: add IsIdentRune field of Scanner. (CL 108030044)
text/template: allow comparison of signed and unsigned integers (CL 149780043)
time: use the micro symbol (µ (U+00B5)) to print microsecond duration (CL 105030046)
unsafe: document the existing situation that unsafe programs are not go1-guaranteed (CL 162060043)
go.sys subrepo created: http://golang.org/s/go1.4-syscall
...@@ -21,6 +21,29 @@ reads of a variable in one goroutine can be guaranteed to ...@@ -21,6 +21,29 @@ reads of a variable in one goroutine can be guaranteed to
observe values produced by writes to the same variable in a different goroutine. observe values produced by writes to the same variable in a different goroutine.
</p> </p>
<h2>Advice</h2>
<p>
Programs that modify data being simultaneously accessed by multiple goroutines
must serialize such access.
</p>
<p>
To serialize access, protect the data with channel operations or other synchronization primitives
such as those in the <a href="/pkg/sync/"><code>sync</code></a>
and <a href="/pkg/sync/atomic/"><code>sync/atomic</code></a> packages.
</p>
<p>
If you must read the rest of this document to understand the behavior of your program,
you are being too clever.
</p>
<p>
Don't be clever.
</p>
<h2>Happens Before</h2> <h2>Happens Before</h2>
<p> <p>
......
<!--{ <!--{
"Title": "The Go Programming Language Specification", "Title": "The Go Programming Language Specification",
"Subtitle": "Version of October 23, 2014", "Subtitle": "Version of October 27, 2014",
"Path": "/ref/spec" "Path": "/ref/spec"
}--> }-->
...@@ -2449,12 +2449,11 @@ Primary expressions are the operands for unary and binary expressions. ...@@ -2449,12 +2449,11 @@ Primary expressions are the operands for unary and binary expressions.
PrimaryExpr = PrimaryExpr =
Operand | Operand |
Conversion | Conversion |
BuiltinCall |
PrimaryExpr Selector | PrimaryExpr Selector |
PrimaryExpr Index | PrimaryExpr Index |
PrimaryExpr Slice | PrimaryExpr Slice |
PrimaryExpr TypeAssertion | PrimaryExpr TypeAssertion |
PrimaryExpr Call . PrimaryExpr Arguments .
Selector = "." identifier . Selector = "." identifier .
Index = "[" Expression "]" . Index = "[" Expression "]" .
...@@ -2462,8 +2461,7 @@ Slice = "[" ( [ Expression ] ":" [ Expression ] ) | ...@@ -2462,8 +2461,7 @@ Slice = "[" ( [ Expression ] ":" [ Expression ] ) |
( [ Expression ] ":" Expression ":" Expression ) ( [ Expression ] ":" Expression ":" Expression )
"]" . "]" .
TypeAssertion = "." "(" Type ")" . TypeAssertion = "." "(" Type ")" .
Call = "(" [ ArgumentList [ "," ] ] ")" . Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
ArgumentList = ExpressionList [ "..." ] .
</pre> </pre>
...@@ -5371,11 +5369,6 @@ so they can only appear in <a href="#Calls">call expressions</a>; ...@@ -5371,11 +5369,6 @@ so they can only appear in <a href="#Calls">call expressions</a>;
they cannot be used as function values. they cannot be used as function values.
</p> </p>
<pre class="ebnf">
BuiltinCall = identifier "(" [ BuiltinArgs [ "," ] ] ")" .
BuiltinArgs = Type [ "," ArgumentList ] | ArgumentList .
</pre>
<h3 id="Close">Close</h3> <h3 id="Close">Close</h3>
<p> <p>
......
...@@ -24,7 +24,6 @@ char *runtimeimport = ...@@ -24,7 +24,6 @@ char *runtimeimport =
"func @\"\".printslice (? any)\n" "func @\"\".printslice (? any)\n"
"func @\"\".printnl ()\n" "func @\"\".printnl ()\n"
"func @\"\".printsp ()\n" "func @\"\".printsp ()\n"
"func @\"\".goprintf ()\n"
"func @\"\".concatstring2 (? string, ? string) (? string)\n" "func @\"\".concatstring2 (? string, ? string) (? string)\n"
"func @\"\".concatstring3 (? string, ? string, ? string) (? string)\n" "func @\"\".concatstring3 (? string, ? string, ? string) (? string)\n"
"func @\"\".concatstring4 (? string, ? string, ? string, ? string) (? string)\n" "func @\"\".concatstring4 (? string, ? string, ? string, ? string) (? string)\n"
......
...@@ -36,7 +36,6 @@ func printeface(any) ...@@ -36,7 +36,6 @@ func printeface(any)
func printslice(any) func printslice(any)
func printnl() func printnl()
func printsp() func printsp()
func goprintf()
func concatstring2(string, string) string func concatstring2(string, string) string
func concatstring3(string, string, string) string func concatstring3(string, string, string) string
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
#include "go.h" #include "go.h"
#include "../ld/textflag.h" #include "../ld/textflag.h"
static Node* walkprint(Node*, NodeList**, int); static Node* walkprint(Node*, NodeList**);
static Node* writebarrierfn(char*, Type*, Type*); static Node* writebarrierfn(char*, Type*, Type*);
static Node* applywritebarrier(Node*, NodeList**); static Node* applywritebarrier(Node*, NodeList**);
static Node* mapfn(char*, Type*); static Node* mapfn(char*, Type*);
...@@ -32,6 +32,7 @@ static void walkmul(Node**, NodeList**); ...@@ -32,6 +32,7 @@ static void walkmul(Node**, NodeList**);
static void walkdiv(Node**, NodeList**); static void walkdiv(Node**, NodeList**);
static int bounded(Node*, int64); static int bounded(Node*, int64);
static Mpint mpzero; static Mpint mpzero;
static void walkprintfunc(Node**, NodeList**);
void void
walk(Node *fn) walk(Node *fn)
...@@ -226,8 +227,7 @@ walkstmt(Node **np) ...@@ -226,8 +227,7 @@ walkstmt(Node **np)
switch(n->left->op) { switch(n->left->op) {
case OPRINT: case OPRINT:
case OPRINTN: case OPRINTN:
walkexprlist(n->left->list, &n->ninit); walkprintfunc(&n->left, &n->ninit);
n->left = walkprint(n->left, &n->ninit, 1);
break; break;
case OCOPY: case OCOPY:
n->left = copyany(n->left, &n->ninit, 1); n->left = copyany(n->left, &n->ninit, 1);
...@@ -260,8 +260,7 @@ walkstmt(Node **np) ...@@ -260,8 +260,7 @@ walkstmt(Node **np)
switch(n->left->op) { switch(n->left->op) {
case OPRINT: case OPRINT:
case OPRINTN: case OPRINTN:
walkexprlist(n->left->list, &n->ninit); walkprintfunc(&n->left, &n->ninit);
n->left = walkprint(n->left, &n->ninit, 1);
break; break;
case OCOPY: case OCOPY:
n->left = copyany(n->left, &n->ninit, 1); n->left = copyany(n->left, &n->ninit, 1);
...@@ -543,7 +542,7 @@ walkexpr(Node **np, NodeList **init) ...@@ -543,7 +542,7 @@ walkexpr(Node **np, NodeList **init)
case OPRINT: case OPRINT:
case OPRINTN: case OPRINTN:
walkexprlist(n->list, init); walkexprlist(n->list, init);
n = walkprint(n, init, 0); n = walkprint(n, init);
goto ret; goto ret;
case OPANIC: case OPANIC:
...@@ -1757,7 +1756,7 @@ ret: ...@@ -1757,7 +1756,7 @@ ret:
// generate code for print // generate code for print
static Node* static Node*
walkprint(Node *nn, NodeList **init, int defer) walkprint(Node *nn, NodeList **init)
{ {
Node *r; Node *r;
Node *n; Node *n;
...@@ -1765,30 +1764,16 @@ walkprint(Node *nn, NodeList **init, int defer) ...@@ -1765,30 +1764,16 @@ walkprint(Node *nn, NodeList **init, int defer)
Node *on; Node *on;
Type *t; Type *t;
int notfirst, et, op; int notfirst, et, op;
NodeList *calls, *intypes, *args; NodeList *calls;
Fmt fmt;
on = nil; on = nil;
op = nn->op; op = nn->op;
all = nn->list; all = nn->list;
calls = nil; calls = nil;
notfirst = 0; notfirst = 0;
intypes = nil;
args = nil;
memset(&fmt, 0, sizeof fmt);
if(defer) {
// defer print turns into defer printf with format string
fmtstrinit(&fmt);
intypes = list(intypes, nod(ODCLFIELD, N, typenod(types[TSTRING])));
args = list1(nod(OXXX, N, N));
}
for(l=all; l; l=l->next) { for(l=all; l; l=l->next) {
if(notfirst) { if(notfirst) {
if(defer)
fmtprint(&fmt, " ");
else
calls = list(calls, mkcall("printsp", T, init)); calls = list(calls, mkcall("printsp", T, init));
} }
notfirst = op == OPRINTN; notfirst = op == OPRINTN;
...@@ -1817,41 +1802,18 @@ walkprint(Node *nn, NodeList **init, int defer) ...@@ -1817,41 +1802,18 @@ walkprint(Node *nn, NodeList **init, int defer)
t = n->type; t = n->type;
et = n->type->etype; et = n->type->etype;
if(isinter(n->type)) { if(isinter(n->type)) {
if(defer) {
if(isnilinter(n->type))
fmtprint(&fmt, "%%e");
else
fmtprint(&fmt, "%%i");
} else {
if(isnilinter(n->type)) if(isnilinter(n->type))
on = syslook("printeface", 1); on = syslook("printeface", 1);
else else
on = syslook("printiface", 1); on = syslook("printiface", 1);
argtype(on, n->type); // any-1 argtype(on, n->type); // any-1
}
} else if(isptr[et] || et == TCHAN || et == TMAP || et == TFUNC || et == TUNSAFEPTR) { } else if(isptr[et] || et == TCHAN || et == TMAP || et == TFUNC || et == TUNSAFEPTR) {
if(defer) {
fmtprint(&fmt, "%%p");
} else {
on = syslook("printpointer", 1); on = syslook("printpointer", 1);
argtype(on, n->type); // any-1 argtype(on, n->type); // any-1
}
} else if(isslice(n->type)) { } else if(isslice(n->type)) {
if(defer) {
fmtprint(&fmt, "%%a");
} else {
on = syslook("printslice", 1); on = syslook("printslice", 1);
argtype(on, n->type); // any-1 argtype(on, n->type); // any-1
}
} else if(isint[et]) { } else if(isint[et]) {
if(defer) {
if(et == TUINT64)
fmtprint(&fmt, "%%U");
else {
fmtprint(&fmt, "%%D");
t = types[TINT64];
}
} else {
if(et == TUINT64) { if(et == TUINT64) {
if((t->sym->pkg == runtimepkg || compiling_runtime) && strcmp(t->sym->name, "hex") == 0) if((t->sym->pkg == runtimepkg || compiling_runtime) && strcmp(t->sym->name, "hex") == 0)
on = syslook("printhex", 0); on = syslook("printhex", 0);
...@@ -1859,70 +1821,35 @@ walkprint(Node *nn, NodeList **init, int defer) ...@@ -1859,70 +1821,35 @@ walkprint(Node *nn, NodeList **init, int defer)
on = syslook("printuint", 0); on = syslook("printuint", 0);
} else } else
on = syslook("printint", 0); on = syslook("printint", 0);
}
} else if(isfloat[et]) { } else if(isfloat[et]) {
if(defer) {
fmtprint(&fmt, "%%f");
t = types[TFLOAT64];
} else
on = syslook("printfloat", 0); on = syslook("printfloat", 0);
} else if(iscomplex[et]) { } else if(iscomplex[et]) {
if(defer) {
fmtprint(&fmt, "%%C");
t = types[TCOMPLEX128];
} else
on = syslook("printcomplex", 0); on = syslook("printcomplex", 0);
} else if(et == TBOOL) { } else if(et == TBOOL) {
if(defer)
fmtprint(&fmt, "%%t");
else
on = syslook("printbool", 0); on = syslook("printbool", 0);
} else if(et == TSTRING) { } else if(et == TSTRING) {
if(defer)
fmtprint(&fmt, "%%S");
else
on = syslook("printstring", 0); on = syslook("printstring", 0);
} else { } else {
badtype(OPRINT, n->type, T); badtype(OPRINT, n->type, T);
continue; continue;
} }
if(!defer) {
t = *getinarg(on->type); t = *getinarg(on->type);
if(t != nil) if(t != nil)
t = t->type; t = t->type;
if(t != nil) if(t != nil)
t = t->type; t = t->type;
}
if(!eqtype(t, n->type)) { if(!eqtype(t, n->type)) {
n = nod(OCONV, n, N); n = nod(OCONV, n, N);
n->type = t; n->type = t;
} }
if(defer) {
intypes = list(intypes, nod(ODCLFIELD, N, typenod(t)));
args = list(args, n);
} else {
r = nod(OCALL, on, N); r = nod(OCALL, on, N);
r->list = list1(n); r->list = list1(n);
calls = list(calls, r); calls = list(calls, r);
} }
}
if(defer) {
if(op == OPRINTN)
fmtprint(&fmt, "\n");
on = syslook("goprintf", 1);
on->type = functype(nil, intypes, nil);
args->n = nod(OLITERAL, N, N);
args->n->val.ctype = CTSTR;
args->n->val.u.sval = strlit(fmtstrflush(&fmt));
r = nod(OCALL, on, N);
r->list = args;
typecheck(&r, Etop);
walkexpr(&r, init);
} else {
if(op == OPRINTN) if(op == OPRINTN)
calls = list(calls, mkcall("printnl", T, nil)); calls = list(calls, mkcall("printnl", T, nil));
typechecklist(calls, Etop); typechecklist(calls, Etop);
...@@ -1932,7 +1859,6 @@ walkprint(Node *nn, NodeList **init, int defer) ...@@ -1932,7 +1859,6 @@ walkprint(Node *nn, NodeList **init, int defer)
typecheck(&r, Etop); typecheck(&r, Etop);
walkexpr(&r, init); walkexpr(&r, init);
r->ninit = calls; r->ninit = calls;
}
return r; return r;
} }
...@@ -3229,7 +3155,7 @@ countfield(Type *t) ...@@ -3229,7 +3155,7 @@ countfield(Type *t)
static void static void
walkcompare(Node **np, NodeList **init) walkcompare(Node **np, NodeList **init)
{ {
Node *n, *l, *r, *call, *a, *li, *ri, *expr; Node *n, *l, *r, *call, *a, *li, *ri, *expr, *cmpl, *cmpr;
int andor, i; int andor, i;
Type *t, *t1; Type *t, *t1;
...@@ -3249,18 +3175,25 @@ walkcompare(Node **np, NodeList **init) ...@@ -3249,18 +3175,25 @@ walkcompare(Node **np, NodeList **init)
break; break;
} }
if(!islvalue(n->left) || !islvalue(n->right)) { cmpl = n->left;
fatal("arguments of comparison must be lvalues"); while(cmpl != N && cmpl->op == OCONVNOP)
cmpl = cmpl->left;
cmpr = n->right;
while(cmpr != N && cmpr->op == OCONVNOP)
cmpr = cmpr->left;
if(!islvalue(cmpl) || !islvalue(cmpr)) {
fatal("arguments of comparison must be lvalues - %N %N", cmpl, cmpr);
} }
l = temp(ptrto(t)); l = temp(ptrto(t));
a = nod(OAS, l, nod(OADDR, n->left, N)); a = nod(OAS, l, nod(OADDR, cmpl, N));
a->right->etype = 1; // addr does not escape a->right->etype = 1; // addr does not escape
typecheck(&a, Etop); typecheck(&a, Etop);
*init = list(*init, a); *init = list(*init, a);
r = temp(ptrto(t)); r = temp(ptrto(t));
a = nod(OAS, r, nod(OADDR, n->right, N)); a = nod(OAS, r, nod(OADDR, cmpr, N));
a->right->etype = 1; // addr does not escape a->right->etype = 1; // addr does not escape
typecheck(&a, Etop); typecheck(&a, Etop);
*init = list(*init, a); *init = list(*init, a);
...@@ -3941,3 +3874,71 @@ candiscard(Node *n) ...@@ -3941,3 +3874,71 @@ candiscard(Node *n)
return 1; return 1;
} }
// rewrite
// print(x, y, z)
// into
// func(a1, a2, a3) {
// print(a1, a2, a3)
// }(x, y, z)
// and same for println.
static void
walkprintfunc(Node **np, NodeList **init)
{
Node *n;
Node *a, *fn, *t, *oldfn;
NodeList *l, *printargs;
int num;
char buf[100];
static int prgen;
n = *np;
if(n->ninit != nil) {
walkstmtlist(n->ninit);
*init = concat(*init, n->ninit);
n->ninit = nil;
}
t = nod(OTFUNC, N, N);
num = 0;
printargs = nil;
for(l=n->list; l != nil; l=l->next) {
snprint(buf, sizeof buf, "a%d", num++);
a = nod(ODCLFIELD, newname(lookup(buf)), typenod(l->n->type));
t->list = list(t->list, a);
printargs = list(printargs, a->left);
}
fn = nod(ODCLFUNC, N, N);
snprint(buf, sizeof buf, "print·%d", ++prgen);
fn->nname = newname(lookup(buf));
fn->nname->defn = fn;
fn->nname->ntype = t;
declare(fn->nname, PFUNC);
oldfn = curfn;
curfn = nil;
funchdr(fn);
a = nod(n->op, N, N);
a->list = printargs;
typecheck(&a, Etop);
walkstmt(&a);
fn->nbody = list1(a);
funcbody(fn);
typecheck(&fn, Etop);
typechecklist(fn->nbody, Etop);
xtop = list(xtop, fn);
curfn = oldfn;
a = nod(OCALL, N, N);
a->left = fn->nname;
a->list = n->list;
typecheck(&a, Etop);
walkexpr(&a, init);
*np = a;
}
...@@ -16,7 +16,7 @@ import ( ...@@ -16,7 +16,7 @@ import (
) )
var cmdGet = &Command{ var cmdGet = &Command{
UsageLine: "get [-d] [-fix] [-t] [-u] [build flags] [packages]", UsageLine: "get [-d] [-f] [-fix] [-t] [-u] [build flags] [packages]",
Short: "download and install packages and dependencies", Short: "download and install packages and dependencies",
Long: ` Long: `
Get downloads and installs the packages named by the import paths, Get downloads and installs the packages named by the import paths,
...@@ -25,6 +25,11 @@ along with their dependencies. ...@@ -25,6 +25,11 @@ along with their dependencies.
The -d flag instructs get to stop after downloading the packages; that is, The -d flag instructs get to stop after downloading the packages; that is,
it instructs get not to install the packages. it instructs get not to install the packages.
The -f flag, valid only when -u is set, forces get -u not to verify that
each package has been checked out from the source control repository
implied by its import path. This can be useful if the source is a local fork
of the original.
The -fix flag instructs get to run the fix tool on the downloaded packages The -fix flag instructs get to run the fix tool on the downloaded packages
before resolving dependencies or building the code. before resolving dependencies or building the code.
...@@ -53,6 +58,7 @@ See also: go build, go install, go clean. ...@@ -53,6 +58,7 @@ See also: go build, go install, go clean.
} }
var getD = cmdGet.Flag.Bool("d", false, "") var getD = cmdGet.Flag.Bool("d", false, "")
var getF = cmdGet.Flag.Bool("f", false, "")
var getT = cmdGet.Flag.Bool("t", false, "") var getT = cmdGet.Flag.Bool("t", false, "")
var getU = cmdGet.Flag.Bool("u", false, "") var getU = cmdGet.Flag.Bool("u", false, "")
var getFix = cmdGet.Flag.Bool("fix", false, "") var getFix = cmdGet.Flag.Bool("fix", false, "")
...@@ -63,6 +69,10 @@ func init() { ...@@ -63,6 +69,10 @@ func init() {
} }
func runGet(cmd *Command, args []string) { func runGet(cmd *Command, args []string) {
if *getF && !*getU {
fatalf("go get: cannot use -f flag without -u")
}
// Phase 1. Download/update. // Phase 1. Download/update.
var stk importStack var stk importStack
for _, arg := range downloadPaths(args) { for _, arg := range downloadPaths(args) {
...@@ -268,7 +278,7 @@ func downloadPackage(p *Package) error { ...@@ -268,7 +278,7 @@ func downloadPackage(p *Package) error {
repo = "<local>" // should be unused; make distinctive repo = "<local>" // should be unused; make distinctive
// Double-check where it came from. // Double-check where it came from.
if *getU && vcs.remoteRepo != nil { if *getU && vcs.remoteRepo != nil && !*getF {
dir := filepath.Join(p.build.SrcRoot, rootPath) dir := filepath.Join(p.build.SrcRoot, rootPath)
if remote, err := vcs.remoteRepo(vcs, dir); err == nil { if remote, err := vcs.remoteRepo(vcs, dir); err == nil {
if rr, err := repoRootForImportPath(p.ImportPath); err == nil { if rr, err := repoRootForImportPath(p.ImportPath); err == nil {
......
...@@ -219,6 +219,16 @@ q' | ed $d/src/$config >/dev/null 2>&1 ...@@ -219,6 +219,16 @@ q' | ed $d/src/$config >/dev/null 2>&1
cat $d/err cat $d/err
ok=false ok=false
fi fi
if GOPATH=$d ./testgo get -d -f -u $url 2>$d/err; then
echo "go get -d -u $url succeeded with wrong remote repo"
cat $d/err
ok=false
elif ! egrep -i 'validating server certificate|not found' $d/err >/dev/null; then
echo "go get -d -f -u $url failed for wrong reason"
cat $d/err
ok=false
fi
fi fi
rm -rf $d rm -rf $d
} }
......
...@@ -159,7 +159,7 @@ func dump(tab *gosym.Table, lookup lookupFunc, disasm disasmFunc, goarch string, ...@@ -159,7 +159,7 @@ func dump(tab *gosym.Table, lookup lookupFunc, disasm disasmFunc, goarch string,
printed := false printed := false
for _, sym := range syms { for _, sym := range syms {
if sym.Code != 'T' || sym.Size == 0 || sym.Name == "_text" || sym.Name == "text" || sym.Addr < textStart || symRE != nil && !symRE.MatchString(sym.Name) { if (sym.Code != 'T' && sym.Code != 't') || sym.Size == 0 || sym.Name == "_text" || sym.Name == "text" || sym.Addr < textStart || symRE != nil && !symRE.MatchString(sym.Name) {
continue continue
} }
if sym.Addr >= textStart+uint64(len(textData)) || sym.Addr+uint64(sym.Size) > textStart+uint64(len(textData)) { if sym.Addr >= textStart+uint64(len(textData)) || sym.Addr+uint64(sym.Size) > textStart+uint64(len(textData)) {
......
...@@ -157,12 +157,15 @@ var armNeed = []string{ ...@@ -157,12 +157,15 @@ var armNeed = []string{
// binary for the current system (only) and test that objdump // binary for the current system (only) and test that objdump
// can handle that one. // can handle that one.
func TestDisasm(t *testing.T) { func testDisasm(t *testing.T, flags ...string) {
tmp, exe := buildObjdump(t) tmp, exe := buildObjdump(t)
defer os.RemoveAll(tmp) defer os.RemoveAll(tmp)
hello := filepath.Join(tmp, "hello.exe") hello := filepath.Join(tmp, "hello.exe")
out, err := exec.Command("go", "build", "-o", hello, "testdata/fmthello.go").CombinedOutput() args := []string{"build", "-o", hello}
args = append(args, flags...)
args = append(args, "testdata/fmthello.go")
out, err := exec.Command("go", args...).CombinedOutput()
if err != nil { if err != nil {
t.Fatalf("go build fmthello.go: %v\n%s", err, out) t.Fatalf("go build fmthello.go: %v\n%s", err, out)
} }
...@@ -194,3 +197,15 @@ func TestDisasm(t *testing.T) { ...@@ -194,3 +197,15 @@ func TestDisasm(t *testing.T) {
t.Logf("full disassembly:\n%s", text) t.Logf("full disassembly:\n%s", text)
} }
} }
func TestDisasm(t *testing.T) {
testDisasm(t)
}
func TestDisasmExtld(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
t.Skipf("skipping on %s", runtime.GOOS)
}
testDisasm(t, "-ldflags=-linkmode=external")
}
...@@ -805,6 +805,9 @@ func (e *encodeState) string(s string) (int, error) { ...@@ -805,6 +805,9 @@ func (e *encodeState) string(s string) (int, error) {
case '\r': case '\r':
e.WriteByte('\\') e.WriteByte('\\')
e.WriteByte('r') e.WriteByte('r')
case '\t':
e.WriteByte('\\')
e.WriteByte('t')
default: default:
// This encodes bytes < 0x20 except for \n and \r, // This encodes bytes < 0x20 except for \n and \r,
// as well as <, > and &. The latter are escaped because they // as well as <, > and &. The latter are escaped because they
...@@ -878,9 +881,12 @@ func (e *encodeState) stringBytes(s []byte) (int, error) { ...@@ -878,9 +881,12 @@ func (e *encodeState) stringBytes(s []byte) (int, error) {
case '\r': case '\r':
e.WriteByte('\\') e.WriteByte('\\')
e.WriteByte('r') e.WriteByte('r')
case '\t':
e.WriteByte('\\')
e.WriteByte('t')
default: default:
// This encodes bytes < 0x20 except for \n and \r, // This encodes bytes < 0x20 except for \n and \r,
// as well as < and >. The latter are escaped because they // as well as <, >, and &. The latter are escaped because they
// can lead to security holes when user-controlled strings // can lead to security holes when user-controlled strings
// are rendered into JSON and served to some browsers. // are rendered into JSON and served to some browsers.
e.WriteString(`\u00`) e.WriteString(`\u00`)
......
...@@ -478,3 +478,55 @@ func TestEncodePointerString(t *testing.T) { ...@@ -478,3 +478,55 @@ func TestEncodePointerString(t *testing.T) {
t.Fatalf("*N = %d; want 42", *back.N) t.Fatalf("*N = %d; want 42", *back.N)
} }
} }
var encodeStringTests = []struct {
in string
out string
}{
{"\x00", `"\u0000"`},
{"\x01", `"\u0001"`},
{"\x02", `"\u0002"`},
{"\x03", `"\u0003"`},
{"\x04", `"\u0004"`},
{"\x05", `"\u0005"`},
{"\x06", `"\u0006"`},
{"\x07", `"\u0007"`},
{"\x08", `"\u0008"`},
{"\x09", `"\t"`},
{"\x0a", `"\n"`},
{"\x0b", `"\u000b"`},
{"\x0c", `"\u000c"`},
{"\x0d", `"\r"`},
{"\x0e", `"\u000e"`},
{"\x0f", `"\u000f"`},
{"\x10", `"\u0010"`},
{"\x11", `"\u0011"`},
{"\x12", `"\u0012"`},
{"\x13", `"\u0013"`},
{"\x14", `"\u0014"`},
{"\x15", `"\u0015"`},
{"\x16", `"\u0016"`},
{"\x17", `"\u0017"`},
{"\x18", `"\u0018"`},
{"\x19", `"\u0019"`},
{"\x1a", `"\u001a"`},
{"\x1b", `"\u001b"`},
{"\x1c", `"\u001c"`},
{"\x1d", `"\u001d"`},
{"\x1e", `"\u001e"`},
{"\x1f", `"\u001f"`},
}
func TestEncodeString(t *testing.T) {
for _, tt := range encodeStringTests {
b, err := Marshal(tt.in)
if err != nil {
t.Errorf("Marshal(%q): %v", tt.in, err)
continue
}
out := string(b)
if out != tt.out {
t.Errorf("Marshal(%q) = %#q, want %#q", tt.in, out, tt.out)
}
}
}
...@@ -38,8 +38,8 @@ ...@@ -38,8 +38,8 @@
%E scientific notation, e.g. -1234.456E+78 %E scientific notation, e.g. -1234.456E+78
%f decimal point but no exponent, e.g. 123.456 %f decimal point but no exponent, e.g. 123.456
%F synonym for %f %F synonym for %f
%g whichever of %e or %f produces more compact output %g %e for large exponents, %f otherwise
%G whichever of %E or %f produces more compact output %G %E for large exponents, %F otherwise
String and slice of bytes: String and slice of bytes:
%s the uninterpreted bytes of the string or slice %s the uninterpreted bytes of the string or slice
%q a double-quoted string safely escaped with Go syntax %q a double-quoted string safely escaped with Go syntax
......
...@@ -138,7 +138,7 @@ func TestJSValEscaper(t *testing.T) { ...@@ -138,7 +138,7 @@ func TestJSValEscaper(t *testing.T) {
// Newlines. // Newlines.
{"\r\n\u2028\u2029", `"\r\n\u2028\u2029"`}, {"\r\n\u2028\u2029", `"\r\n\u2028\u2029"`},
// "\v" == "v" on IE 6 so use "\x0b" instead. // "\v" == "v" on IE 6 so use "\x0b" instead.
{"\t\x0b", `"\u0009\u000b"`}, {"\t\x0b", `"\t\u000b"`},
{struct{ X, Y int }{1, 2}, `{"X":1,"Y":2}`}, {struct{ X, Y int }{1, 2}, `{"X":1,"Y":2}`},
{[]interface{}{}, "[]"}, {[]interface{}{}, "[]"},
{[]interface{}{42, "foo", nil}, `[42,"foo",null]`}, {[]interface{}{42, "foo", nil}, `[42,"foo",null]`},
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"flag" "flag"
"fmt" "fmt"
"testing" "testing"
"time"
) )
var testDNSFlood = flag.Bool("dnsflood", false, "whether to test dns query flooding") var testDNSFlood = flag.Bool("dnsflood", false, "whether to test dns query flooding")
...@@ -35,3 +36,64 @@ func TestDNSThreadLimit(t *testing.T) { ...@@ -35,3 +36,64 @@ func TestDNSThreadLimit(t *testing.T) {
// If we're still here, it worked. // If we're still here, it worked.
} }
func TestLookupIPDeadline(t *testing.T) {
if !*testDNSFlood {
t.Skip("test disabled; use -dnsflood to enable")
}
const N = 5000
const timeout = 3 * time.Second
c := make(chan error, 2*N)
for i := 0; i < N; i++ {
name := fmt.Sprintf("%d.net-test.golang.org", i)
go func() {
_, err := lookupIPDeadline(name, time.Now().Add(timeout/2))
c <- err
}()
go func() {
_, err := lookupIPDeadline(name, time.Now().Add(timeout))
c <- err
}()
}
qstats := struct {
succeeded, failed int
timeout, temporary, other int
unknown int
}{}
deadline := time.After(timeout + time.Second)
for i := 0; i < 2*N; i++ {
select {
case <-deadline:
t.Fatal("deadline exceeded")
case err := <-c:
switch err := err.(type) {
case nil:
qstats.succeeded++
case Error:
qstats.failed++
if err.Timeout() {
qstats.timeout++
}
if err.Temporary() {
qstats.temporary++
}
if !err.Timeout() && !err.Temporary() {
qstats.other++
}
default:
qstats.failed++
qstats.unknown++
}
}
}
// A high volume of DNS queries for sub-domain of golang.org
// would be coordinated by authoritative or recursive server,
// or stub resolver which implements query-response rate
// limitation, so we can expect some query successes and more
// failures including timeout, temporary and other here.
// As a rule, unknown must not be shown but it might possibly
// happen due to issue 4856 for now.
t.Logf("%v succeeded, %v failed (%v timeout, %v temporary, %v other, %v unknown)", qstats.succeeded, qstats.failed, qstats.timeout, qstats.temporary, qstats.other, qstats.unknown)
}
...@@ -36,7 +36,7 @@ func (f *File) readdirnames(n int) (names []string, err error) { ...@@ -36,7 +36,7 @@ func (f *File) readdirnames(n int) (names []string, err error) {
if d.bufp >= d.nbuf { if d.bufp >= d.nbuf {
d.bufp = 0 d.bufp = 0
var errno error var errno error
d.nbuf, errno = syscall.ReadDirent(f.fd, d.buf) d.nbuf, errno = fixCount(syscall.ReadDirent(f.fd, d.buf))
if errno != nil { if errno != nil {
return names, NewSyscallError("readdirent", errno) return names, NewSyscallError("readdirent", errno)
} }
......
...@@ -255,3 +255,12 @@ var lstat = Lstat ...@@ -255,3 +255,12 @@ var lstat = Lstat
func Rename(oldpath, newpath string) error { func Rename(oldpath, newpath string) error {
return rename(oldpath, newpath) return rename(oldpath, newpath)
} }
// Many functions in package syscall return a count of -1 instead of 0.
// Using fixCount(call()) instead of call() corrects the count.
func fixCount(n int, err error) (int, error) {
if n < 0 {
n = 0
}
return n, err
}
...@@ -244,14 +244,14 @@ func (f *File) Sync() (err error) { ...@@ -244,14 +244,14 @@ func (f *File) Sync() (err error) {
// read reads up to len(b) bytes from the File. // read reads up to len(b) bytes from the File.
// It returns the number of bytes read and an error, if any. // It returns the number of bytes read and an error, if any.
func (f *File) read(b []byte) (n int, err error) { func (f *File) read(b []byte) (n int, err error) {
return syscall.Read(f.fd, b) return fixCount(syscall.Read(f.fd, b))
} }
// pread reads len(b) bytes from the File starting at byte offset off. // pread reads len(b) bytes from the File starting at byte offset off.
// It returns the number of bytes read and the error, if any. // It returns the number of bytes read and the error, if any.
// EOF is signaled by a zero count with err set to nil. // EOF is signaled by a zero count with err set to nil.
func (f *File) pread(b []byte, off int64) (n int, err error) { func (f *File) pread(b []byte, off int64) (n int, err error) {
return syscall.Pread(f.fd, b, off) return fixCount(syscall.Pread(f.fd, b, off))
} }
// write writes len(b) bytes to the File. // write writes len(b) bytes to the File.
...@@ -262,7 +262,7 @@ func (f *File) write(b []byte) (n int, err error) { ...@@ -262,7 +262,7 @@ func (f *File) write(b []byte) (n int, err error) {
if len(b) == 0 { if len(b) == 0 {
return 0, nil return 0, nil
} }
return syscall.Write(f.fd, b) return fixCount(syscall.Write(f.fd, b))
} }
// pwrite writes len(b) bytes to the File starting at byte offset off. // pwrite writes len(b) bytes to the File starting at byte offset off.
...@@ -273,7 +273,7 @@ func (f *File) pwrite(b []byte, off int64) (n int, err error) { ...@@ -273,7 +273,7 @@ func (f *File) pwrite(b []byte, off int64) (n int, err error) {
if len(b) == 0 { if len(b) == 0 {
return 0, nil return 0, nil
} }
return syscall.Pwrite(f.fd, b, off) return fixCount(syscall.Pwrite(f.fd, b, off))
} }
// seek sets the offset for the next Read or Write on file to offset, interpreted // seek sets the offset for the next Read or Write on file to offset, interpreted
......
...@@ -18,7 +18,7 @@ func sigpipe() // implemented in package runtime ...@@ -18,7 +18,7 @@ func sigpipe() // implemented in package runtime
func Readlink(name string) (string, error) { func Readlink(name string) (string, error) {
for len := 128; ; len *= 2 { for len := 128; ; len *= 2 {
b := make([]byte, len) b := make([]byte, len)
n, e := syscall.Readlink(name, b) n, e := fixCount(syscall.Readlink(name, b))
if e != nil { if e != nil {
return "", &PathError{"readlink", name, e} return "", &PathError{"readlink", name, e}
} }
......
...@@ -187,7 +187,7 @@ func (f *File) read(b []byte) (n int, err error) { ...@@ -187,7 +187,7 @@ func (f *File) read(b []byte) (n int, err error) {
if needsMaxRW && len(b) > maxRW { if needsMaxRW && len(b) > maxRW {
b = b[:maxRW] b = b[:maxRW]
} }
return syscall.Read(f.fd, b) return fixCount(syscall.Read(f.fd, b))
} }
// pread reads len(b) bytes from the File starting at byte offset off. // pread reads len(b) bytes from the File starting at byte offset off.
...@@ -197,7 +197,7 @@ func (f *File) pread(b []byte, off int64) (n int, err error) { ...@@ -197,7 +197,7 @@ func (f *File) pread(b []byte, off int64) (n int, err error) {
if needsMaxRW && len(b) > maxRW { if needsMaxRW && len(b) > maxRW {
b = b[:maxRW] b = b[:maxRW]
} }
return syscall.Pread(f.fd, b, off) return fixCount(syscall.Pread(f.fd, b, off))
} }
// write writes len(b) bytes to the File. // write writes len(b) bytes to the File.
...@@ -208,7 +208,7 @@ func (f *File) write(b []byte) (n int, err error) { ...@@ -208,7 +208,7 @@ func (f *File) write(b []byte) (n int, err error) {
if needsMaxRW && len(bcap) > maxRW { if needsMaxRW && len(bcap) > maxRW {
bcap = bcap[:maxRW] bcap = bcap[:maxRW]
} }
m, err := syscall.Write(f.fd, bcap) m, err := fixCount(syscall.Write(f.fd, bcap))
n += m n += m
// If the syscall wrote some data but not all (short write) // If the syscall wrote some data but not all (short write)
...@@ -234,7 +234,7 @@ func (f *File) pwrite(b []byte, off int64) (n int, err error) { ...@@ -234,7 +234,7 @@ func (f *File) pwrite(b []byte, off int64) (n int, err error) {
if needsMaxRW && len(b) > maxRW { if needsMaxRW && len(b) > maxRW {
b = b[:maxRW] b = b[:maxRW]
} }
return syscall.Pwrite(f.fd, b, off) return fixCount(syscall.Pwrite(f.fd, b, off))
} }
// seek sets the offset for the next Read or Write on file to offset, interpreted // seek sets the offset for the next Read or Write on file to offset, interpreted
......
...@@ -295,7 +295,7 @@ func (f *File) read(b []byte) (n int, err error) { ...@@ -295,7 +295,7 @@ func (f *File) read(b []byte) (n int, err error) {
if f.isConsole { if f.isConsole {
return f.readConsole(b) return f.readConsole(b)
} }
return syscall.Read(f.fd, b) return fixCount(syscall.Read(f.fd, b))
} }
// pread reads len(b) bytes from the File starting at byte offset off. // pread reads len(b) bytes from the File starting at byte offset off.
...@@ -376,7 +376,7 @@ func (f *File) write(b []byte) (n int, err error) { ...@@ -376,7 +376,7 @@ func (f *File) write(b []byte) (n int, err error) {
if f.isConsole { if f.isConsole {
return f.writeConsole(b) return f.writeConsole(b)
} }
return syscall.Write(f.fd, b) return fixCount(syscall.Write(f.fd, b))
} }
// pwrite writes len(b) bytes to the File starting at byte offset off. // pwrite writes len(b) bytes to the File starting at byte offset off.
......
...@@ -732,6 +732,20 @@ needm: ...@@ -732,6 +732,20 @@ needm:
MOVL g(CX), BP MOVL g(CX), BP
MOVL g_m(BP), BP MOVL g_m(BP), BP
// Set m->sched.sp = SP, so that if a panic happens
// during the function we are about to execute, it will
// have a valid SP to run on the g0 stack.
// The next few lines (after the havem label)
// will save this SP onto the stack and then write
// the same SP back to m->sched.sp. That seems redundant,
// but if an unrecovered panic happens, unwindm will
// restore the g->sched.sp from the stack location
// and then onM will try to use it. If we don't set it here,
// that restored SP will be uninitialized (typically 0) and
// will not be usable.
MOVL m_g0(BP), SI
MOVL SP, (g_sched+gobuf_sp)(SI)
havem: havem:
// Now there's a valid m, and we're running on its m->g0. // Now there's a valid m, and we're running on its m->g0.
// Save current m->g0->sched.sp on stack and then set it to SP. // Save current m->g0->sched.sp on stack and then set it to SP.
......
...@@ -718,6 +718,20 @@ needm: ...@@ -718,6 +718,20 @@ needm:
MOVQ g(CX), BP MOVQ g(CX), BP
MOVQ g_m(BP), BP MOVQ g_m(BP), BP
// Set m->sched.sp = SP, so that if a panic happens
// during the function we are about to execute, it will
// have a valid SP to run on the g0 stack.
// The next few lines (after the havem label)
// will save this SP onto the stack and then write
// the same SP back to m->sched.sp. That seems redundant,
// but if an unrecovered panic happens, unwindm will
// restore the g->sched.sp from the stack location
// and then onM will try to use it. If we don't set it here,
// that restored SP will be uninitialized (typically 0) and
// will not be usable.
MOVQ m_g0(BP), SI
MOVQ SP, (g_sched+gobuf_sp)(SI)
havem: havem:
// Now there's a valid m, and we're running on its m->g0. // Now there's a valid m, and we're running on its m->g0.
// Save current m->g0->sched.sp on stack and then set it to SP. // Save current m->g0->sched.sp on stack and then set it to SP.
......
...@@ -556,6 +556,21 @@ TEXT ·cgocallback_gofunc(SB),NOSPLIT,$8-12 ...@@ -556,6 +556,21 @@ TEXT ·cgocallback_gofunc(SB),NOSPLIT,$8-12
MOVW $runtime·needm(SB), R0 MOVW $runtime·needm(SB), R0
BL (R0) BL (R0)
// Set m->sched.sp = SP, so that if a panic happens
// during the function we are about to execute, it will
// have a valid SP to run on the g0 stack.
// The next few lines (after the havem label)
// will save this SP onto the stack and then write
// the same SP back to m->sched.sp. That seems redundant,
// but if an unrecovered panic happens, unwindm will
// restore the g->sched.sp from the stack location
// and then onM will try to use it. If we don't set it here,
// that restored SP will be uninitialized (typically 0) and
// will not be usable.
MOVW g_m(g), R8
MOVW m_g0(R8), R3
MOVW R13, (g_sched+gobuf_sp)(R3)
havem: havem:
MOVW g_m(g), R8 MOVW g_m(g), R8
MOVW R8, savedm-4(SP) MOVW R8, savedm-4(SP)
......
...@@ -8,6 +8,7 @@ package runtime_test ...@@ -8,6 +8,7 @@ package runtime_test
import ( import (
"runtime" "runtime"
"strings"
"testing" "testing"
) )
...@@ -34,6 +35,17 @@ func TestCgoTraceback(t *testing.T) { ...@@ -34,6 +35,17 @@ func TestCgoTraceback(t *testing.T) {
} }
} }
func TestCgoExternalThreadPanic(t *testing.T) {
if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
t.Skipf("no pthreads on %s", runtime.GOOS)
}
got := executeTest(t, cgoExternalThreadPanicSource, nil, "main.c", cgoExternalThreadPanicC)
want := "panic: BOOM"
if !strings.Contains(got, want) {
t.Fatalf("want failure containing %q. output:\n%s\n", want, got)
}
}
const cgoSignalDeadlockSource = ` const cgoSignalDeadlockSource = `
package main package main
...@@ -117,3 +129,43 @@ func main() { ...@@ -117,3 +129,43 @@ func main() {
fmt.Printf("OK\n") fmt.Printf("OK\n")
} }
` `
const cgoExternalThreadPanicSource = `
package main
// void start(void);
import "C"
func main() {
C.start()
select {}
}
//export gopanic
func gopanic() {
panic("BOOM")
}
`
const cgoExternalThreadPanicC = `
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
void gopanic(void);
static void*
die(void* x)
{
gopanic();
return 0;
}
void
start(void)
{
pthread_t t;
if(pthread_create(&t, 0, die, 0) != 0)
printf("pthread_create failed\n");
}
`
...@@ -31,7 +31,7 @@ func testEnv(cmd *exec.Cmd) *exec.Cmd { ...@@ -31,7 +31,7 @@ func testEnv(cmd *exec.Cmd) *exec.Cmd {
return cmd return cmd
} }
func executeTest(t *testing.T, templ string, data interface{}) string { func executeTest(t *testing.T, templ string, data interface{}, extra ...string) string {
switch runtime.GOOS { switch runtime.GOOS {
case "android", "nacl": case "android", "nacl":
t.Skipf("skipping on %s", runtime.GOOS) t.Skipf("skipping on %s", runtime.GOOS)
...@@ -61,7 +61,20 @@ func executeTest(t *testing.T, templ string, data interface{}) string { ...@@ -61,7 +61,20 @@ func executeTest(t *testing.T, templ string, data interface{}) string {
t.Fatalf("failed to close file: %v", err) t.Fatalf("failed to close file: %v", err)
} }
got, _ := testEnv(exec.Command("go", "run", src)).CombinedOutput() for i := 0; i < len(extra); i += 2 {
if err := ioutil.WriteFile(filepath.Join(dir, extra[i]), []byte(extra[i+1]), 0666); err != nil {
t.Fatal(err)
}
}
cmd := exec.Command("go", "build", "-o", "a.exe")
cmd.Dir = dir
out, err := testEnv(cmd).CombinedOutput()
if err != nil {
t.Fatalf("building source: %v\n%s", err, out)
}
got, _ := testEnv(exec.Command(filepath.Join(dir, "a.exe"))).CombinedOutput()
return string(got) return string(got)
} }
......
...@@ -16,6 +16,7 @@ type GCStats struct { ...@@ -16,6 +16,7 @@ type GCStats struct {
NumGC int64 // number of garbage collections NumGC int64 // number of garbage collections
PauseTotal time.Duration // total pause for all collections PauseTotal time.Duration // total pause for all collections
Pause []time.Duration // pause history, most recent first Pause []time.Duration // pause history, most recent first
PauseEnd []time.Time // pause end times history, most recent first
PauseQuantiles []time.Duration PauseQuantiles []time.Duration
} }
...@@ -30,25 +31,36 @@ type GCStats struct { ...@@ -30,25 +31,36 @@ type GCStats struct {
func ReadGCStats(stats *GCStats) { func ReadGCStats(stats *GCStats) {
// Create a buffer with space for at least two copies of the // Create a buffer with space for at least two copies of the
// pause history tracked by the runtime. One will be returned // pause history tracked by the runtime. One will be returned
// to the caller and the other will be used as a temporary buffer // to the caller and the other will be used as transfer buffer
// for computing quantiles. // for end times history and as a temporary buffer for
// computing quantiles.
const maxPause = len(((*runtime.MemStats)(nil)).PauseNs) const maxPause = len(((*runtime.MemStats)(nil)).PauseNs)
if cap(stats.Pause) < 2*maxPause { if cap(stats.Pause) < 2*maxPause+3 {
stats.Pause = make([]time.Duration, 2*maxPause) stats.Pause = make([]time.Duration, 2*maxPause+3)
} }
// readGCStats fills in the pause history (up to maxPause entries) // readGCStats fills in the pause and end times histories (up to
// and then three more: Unix ns time of last GC, number of GC, // maxPause entries) and then three more: Unix ns time of last GC,
// and total pause time in nanoseconds. Here we depend on the // number of GC, and total pause time in nanoseconds. Here we
// fact that time.Duration's native unit is nanoseconds, so the // depend on the fact that time.Duration's native unit is
// pauses and the total pause time do not need any conversion. // nanoseconds, so the pauses and the total pause time do not need
// any conversion.
readGCStats(&stats.Pause) readGCStats(&stats.Pause)
n := len(stats.Pause) - 3 n := len(stats.Pause) - 3
stats.LastGC = time.Unix(0, int64(stats.Pause[n])) stats.LastGC = time.Unix(0, int64(stats.Pause[n]))
stats.NumGC = int64(stats.Pause[n+1]) stats.NumGC = int64(stats.Pause[n+1])
stats.PauseTotal = stats.Pause[n+2] stats.PauseTotal = stats.Pause[n+2]
n /= 2 // buffer holds pauses and end times
stats.Pause = stats.Pause[:n] stats.Pause = stats.Pause[:n]
if cap(stats.PauseEnd) < maxPause {
stats.PauseEnd = make([]time.Time, 0, maxPause)
}
stats.PauseEnd = stats.PauseEnd[:0]
for _, ns := range stats.Pause[n : n+n] {
stats.PauseEnd = append(stats.PauseEnd, time.Unix(0, int64(ns)))
}
if len(stats.PauseQuantiles) > 0 { if len(stats.PauseQuantiles) > 0 {
if n == 0 { if n == 0 {
for i := range stats.PauseQuantiles { for i := range stats.PauseQuantiles {
......
...@@ -70,6 +70,19 @@ func TestReadGCStats(t *testing.T) { ...@@ -70,6 +70,19 @@ func TestReadGCStats(t *testing.T) {
t.Errorf("stats.PauseQuantiles[%d]=%d > stats.PauseQuantiles[%d]=%d", i, q[i], i+1, q[i+1]) t.Errorf("stats.PauseQuantiles[%d]=%d > stats.PauseQuantiles[%d]=%d", i, q[i], i+1, q[i+1])
} }
} }
// compare memory stats with gc stats:
if len(stats.PauseEnd) != n {
t.Fatalf("len(stats.PauseEnd) = %d, want %d", len(stats.PauseEnd), n)
}
off := (int(mstats.NumGC) + len(mstats.PauseEnd) - 1) % len(mstats.PauseEnd)
for i := 0; i < n; i++ {
dt := stats.PauseEnd[i]
if dt.UnixNano() != int64(mstats.PauseEnd[off]) {
t.Errorf("stats.PauseEnd[%d] = %d, want %d", i, dt, mstats.PauseEnd[off])
}
off = (off + len(mstats.PauseEnd) - 1) % len(mstats.PauseEnd)
}
} }
var big = make([]byte, 1<<20) var big = make([]byte, 1<<20)
......
...@@ -39,6 +39,12 @@ a comma-separated list of name=val pairs. Supported names are: ...@@ -39,6 +39,12 @@ a comma-separated list of name=val pairs. Supported names are:
gcdead: setting gcdead=1 causes the garbage collector to clobber all stack slots gcdead: setting gcdead=1 causes the garbage collector to clobber all stack slots
that it thinks are dead. that it thinks are dead.
invalidptr: defaults to invalidptr=1, causing the garbage collector and stack
copier to crash the program if an invalid pointer value (for example, 1)
is found in a pointer-typed location. Setting invalidptr=0 disables this check.
This should only be used as a temporary workaround to diagnose buggy code.
The real fix is to not store integers in pointer-typed locations.
scheddetail: setting schedtrace=X and scheddetail=1 causes the scheduler to emit scheddetail: setting schedtrace=X and scheddetail=1 causes the scheduler to emit
detailed multiline info every X milliseconds, describing state of the scheduler, detailed multiline info every X milliseconds, describing state of the scheduler,
processors, threads and goroutines. processors, threads and goroutines.
......
...@@ -28,6 +28,9 @@ ...@@ -28,6 +28,9 @@
// defines the pointer map for the function's arguments. // defines the pointer map for the function's arguments.
// GO_ARGS should be the first instruction in a function that uses it. // GO_ARGS should be the first instruction in a function that uses it.
// It can be omitted if there are no arguments at all. // It can be omitted if there are no arguments at all.
// GO_ARGS is inserted implicitly by the linker for any function
// that also has a Go prototype and therefore is usually not necessary
// to write explicitly.
#define GO_ARGS FUNCDATA $FUNCDATA_ArgsPointerMaps, go_args_stackmap(SB) #define GO_ARGS FUNCDATA $FUNCDATA_ArgsPointerMaps, go_args_stackmap(SB)
// GO_RESULTS_INITIALIZED indicates that the assembly function // GO_RESULTS_INITIALIZED indicates that the assembly function
......
...@@ -267,7 +267,8 @@ struct MStats ...@@ -267,7 +267,8 @@ struct MStats
uint64 next_gc; // next GC (in heap_alloc time) uint64 next_gc; // next GC (in heap_alloc time)
uint64 last_gc; // last GC (in absolute time) uint64 last_gc; // last GC (in absolute time)
uint64 pause_total_ns; uint64 pause_total_ns;
uint64 pause_ns[256]; uint64 pause_ns[256]; // circular buffer of recent GC pause lengths
uint64 pause_end[256]; // circular buffer of recent GC end times (nanoseconds since 1970)
uint32 numgc; uint32 numgc;
bool enablegc; bool enablegc;
bool debuggc; bool debuggc;
......
...@@ -44,7 +44,8 @@ type MemStats struct { ...@@ -44,7 +44,8 @@ type MemStats struct {
NextGC uint64 // next collection will happen when HeapAlloc ≥ this amount NextGC uint64 // next collection will happen when HeapAlloc ≥ this amount
LastGC uint64 // end time of last collection (nanoseconds since 1970) LastGC uint64 // end time of last collection (nanoseconds since 1970)
PauseTotalNs uint64 PauseTotalNs uint64
PauseNs [256]uint64 // circular buffer of recent GC pause times, most recent at [(NumGC+255)%256] PauseNs [256]uint64 // circular buffer of recent GC pause durations, most recent at [(NumGC+255)%256]
PauseEnd [256]uint64 // circular buffer of recent GC pause end times
NumGC uint32 NumGC uint32
EnableGC bool EnableGC bool
DebugGC bool DebugGC bool
......
...@@ -330,7 +330,7 @@ scanblock(byte *b, uintptr n, byte *ptrmask) ...@@ -330,7 +330,7 @@ scanblock(byte *b, uintptr n, byte *ptrmask)
if(obj == nil) if(obj == nil)
continue; continue;
if(obj < arena_start || obj >= arena_used) { if(obj < arena_start || obj >= arena_used) {
if((uintptr)obj < PhysPageSize) { if((uintptr)obj < PhysPageSize && runtime·invalidptr) {
s = nil; s = nil;
goto badobj; goto badobj;
} }
...@@ -375,7 +375,7 @@ scanblock(byte *b, uintptr n, byte *ptrmask) ...@@ -375,7 +375,7 @@ scanblock(byte *b, uintptr n, byte *ptrmask)
else else
runtime·printf(" span=%p-%p-%p state=%d\n", (uintptr)s->start<<PageShift, s->limit, (uintptr)(s->start+s->npages)<<PageShift, s->state); runtime·printf(" span=%p-%p-%p state=%d\n", (uintptr)s->start<<PageShift, s->limit, (uintptr)(s->start+s->npages)<<PageShift, s->state);
if(ptrmask != nil) if(ptrmask != nil)
runtime·throw("bad pointer"); runtime·throw("invalid heap pointer");
// Add to badblock list, which will cause the garbage collection // Add to badblock list, which will cause the garbage collection
// to keep repeating until it has traced the chain of pointers // to keep repeating until it has traced the chain of pointers
// leading to obj all the way back to a root. // leading to obj all the way back to a root.
...@@ -1459,6 +1459,7 @@ gc(struct gc_args *args) ...@@ -1459,6 +1459,7 @@ gc(struct gc_args *args)
t4 = runtime·nanotime(); t4 = runtime·nanotime();
runtime·atomicstore64(&mstats.last_gc, runtime·unixnanotime()); // must be Unix time to make sense to user runtime·atomicstore64(&mstats.last_gc, runtime·unixnanotime()); // must be Unix time to make sense to user
mstats.pause_ns[mstats.numgc%nelem(mstats.pause_ns)] = t4 - t0; mstats.pause_ns[mstats.numgc%nelem(mstats.pause_ns)] = t4 - t0;
mstats.pause_end[mstats.numgc%nelem(mstats.pause_end)] = t4;
mstats.pause_total_ns += t4 - t0; mstats.pause_total_ns += t4 - t0;
mstats.numgc++; mstats.numgc++;
if(mstats.debuggc) if(mstats.debuggc)
...@@ -1572,7 +1573,7 @@ readgcstats_m(void) ...@@ -1572,7 +1573,7 @@ readgcstats_m(void)
{ {
Slice *pauses; Slice *pauses;
uint64 *p; uint64 *p;
uint32 i, n; uint32 i, j, n;
pauses = g->m->ptrarg[0]; pauses = g->m->ptrarg[0];
g->m->ptrarg[0] = nil; g->m->ptrarg[0] = nil;
...@@ -1581,9 +1582,10 @@ readgcstats_m(void) ...@@ -1581,9 +1582,10 @@ readgcstats_m(void)
if(pauses->cap < nelem(mstats.pause_ns)+3) if(pauses->cap < nelem(mstats.pause_ns)+3)
runtime·throw("runtime: short slice passed to readGCStats"); runtime·throw("runtime: short slice passed to readGCStats");
// Pass back: pauses, last gc (absolute time), number of gc, total pause ns. // Pass back: pauses, pause ends, last gc (absolute time), number of gc, total pause ns.
p = (uint64*)pauses->array; p = (uint64*)pauses->array;
runtime·lock(&runtime·mheap.lock); runtime·lock(&runtime·mheap.lock);
n = mstats.numgc; n = mstats.numgc;
if(n > nelem(mstats.pause_ns)) if(n > nelem(mstats.pause_ns))
n = nelem(mstats.pause_ns); n = nelem(mstats.pause_ns);
...@@ -1592,14 +1594,17 @@ readgcstats_m(void) ...@@ -1592,14 +1594,17 @@ readgcstats_m(void)
// pause_ns[(numgc-1)%nelem(pause_ns)], and then backward // pause_ns[(numgc-1)%nelem(pause_ns)], and then backward
// from there to go back farther in time. We deliver the times // from there to go back farther in time. We deliver the times
// most recent first (in p[0]). // most recent first (in p[0]).
for(i=0; i<n; i++) for(i=0; i<n; i++) {
p[i] = mstats.pause_ns[(mstats.numgc-1-i)%nelem(mstats.pause_ns)]; j = (mstats.numgc-1-i)%nelem(mstats.pause_ns);
p[i] = mstats.pause_ns[j];
p[n+i] = mstats.pause_end[j];
}
p[n] = mstats.last_gc; p[n+n] = mstats.last_gc;
p[n+1] = mstats.numgc; p[n+n+1] = mstats.numgc;
p[n+2] = mstats.pause_total_ns; p[n+n+2] = mstats.pause_total_ns;
runtime·unlock(&runtime·mheap.lock); runtime·unlock(&runtime·mheap.lock);
pauses->len = n+3; pauses->len = n+n+3;
} }
void void
......
...@@ -19,32 +19,17 @@ func bytes(s string) (ret []byte) { ...@@ -19,32 +19,17 @@ func bytes(s string) (ret []byte) {
return return
} }
// goprintf is the function call that is actually deferred when you write // printf is only called from C code. It has no type information for the args,
// defer print(...) // but C stacks are ignored by the garbage collector anyway, so having
// It is otherwise unused. In particular it is not used for ordinary prints. // type information would not add anything.
// Right now a dynamically allocated string that is being passed as an
// argument is invisible to the garbage collector and might be collected
// if that argument list is the only reference. For now we ignore that possibility.
// To fix, we should change to defer a call to vprintf with a pointer to
// an argument list on the stack, stored in an appropriately typed
// struct. golang.org/issue/8614.
//go:nosplit
func goprintf(s string) {
vprintf(s, add(unsafe.Pointer(&s), unsafe.Sizeof(s)))
}
// printf is only called from C code. It has the same problem as goprintf
// with strings possibly being collected from underneath.
// However, the runtime never prints dynamically allocated
// Go strings using printf. The strings it prints come from the symbol
// and type tables.
//go:nosplit //go:nosplit
func printf(s *byte) { func printf(s *byte) {
vprintf(gostringnocopy(s), add(unsafe.Pointer(&s), unsafe.Sizeof(s))) vprintf(gostringnocopy(s), add(unsafe.Pointer(&s), unsafe.Sizeof(s)))
} }
// sprintf is only called from C code. // sprintf is only called from C code. It has no type information for the args,
// It has the same problem as goprintf. // but C stacks are ignored by the garbage collector anyway, so having
// type information would not add anything.
//go:nosplit //go:nosplit
func snprintf(dst *byte, n int32, s *byte) { func snprintf(dst *byte, n int32, s *byte) {
buf := (*[1 << 30]byte)(unsafe.Pointer(dst))[0:n:n] buf := (*[1 << 30]byte)(unsafe.Pointer(dst))[0:n:n]
......
...@@ -2758,6 +2758,8 @@ static void ...@@ -2758,6 +2758,8 @@ static void
checkdead(void) checkdead(void)
{ {
G *gp; G *gp;
P *p;
M *mp;
int32 run, grunning, s; int32 run, grunning, s;
uintptr i; uintptr i;
...@@ -2799,6 +2801,24 @@ checkdead(void) ...@@ -2799,6 +2801,24 @@ checkdead(void)
runtime·unlock(&runtime·allglock); runtime·unlock(&runtime·allglock);
if(grunning == 0) // possible if main goroutine calls runtime·Goexit() if(grunning == 0) // possible if main goroutine calls runtime·Goexit()
runtime·throw("no goroutines (main called runtime.Goexit) - deadlock!"); runtime·throw("no goroutines (main called runtime.Goexit) - deadlock!");
// Maybe jump time forward for playground.
if((gp = runtime·timejump()) != nil) {
runtime·casgstatus(gp, Gwaiting, Grunnable);
globrunqput(gp);
p = pidleget();
if(p == nil)
runtime·throw("checkdead: no p for timer");
mp = mget();
if(mp == nil)
newm(nil, p);
else {
mp->nextp = p;
runtime·notewakeup(&mp->park);
}
return;
}
g->m->throwing = -1; // do not dump full stacks g->m->throwing = -1; // do not dump full stacks
runtime·throw("all goroutines are asleep - deadlock!"); runtime·throw("all goroutines are asleep - deadlock!");
} }
......
...@@ -26,5 +26,5 @@ TEXT _rt0_amd64p32_nacl(SB),NOSPLIT,$16 ...@@ -26,5 +26,5 @@ TEXT _rt0_amd64p32_nacl(SB),NOSPLIT,$16
TEXT main(SB),NOSPLIT,$0 TEXT main(SB),NOSPLIT,$0
// Uncomment for fake time like on Go Playground. // Uncomment for fake time like on Go Playground.
//MOVQ $1257894000000000000, AX //MOVQ $1257894000000000000, AX
//MOVQ AX, runtime·timens(SB) //MOVQ AX, runtime·faketime(SB)
JMP runtime·rt0_go(SB) JMP runtime·rt0_go(SB)
...@@ -282,9 +282,13 @@ struct DbgVar ...@@ -282,9 +282,13 @@ struct DbgVar
int32* value; int32* value;
}; };
// Do we report invalid pointers found during stack or heap scans?
int32 runtime·invalidptr = 1;
#pragma dataflag NOPTR /* dbgvar has no heap pointers */ #pragma dataflag NOPTR /* dbgvar has no heap pointers */
static DbgVar dbgvar[] = { static DbgVar dbgvar[] = {
{"allocfreetrace", &runtime·debug.allocfreetrace}, {"allocfreetrace", &runtime·debug.allocfreetrace},
{"invalidptr", &runtime·invalidptr},
{"efence", &runtime·debug.efence}, {"efence", &runtime·debug.efence},
{"gctrace", &runtime·debug.gctrace}, {"gctrace", &runtime·debug.gctrace},
{"gcdead", &runtime·debug.gcdead}, {"gcdead", &runtime·debug.gcdead},
......
...@@ -657,6 +657,8 @@ enum { ...@@ -657,6 +657,8 @@ enum {
byte* runtime·startup_random_data; byte* runtime·startup_random_data;
uint32 runtime·startup_random_data_len; uint32 runtime·startup_random_data_len;
int32 runtime·invalidptr;
enum { enum {
// hashinit wants this many random bytes // hashinit wants this many random bytes
HashRandomBytes = 32 HashRandomBytes = 32
......
...@@ -401,12 +401,12 @@ adjustpointers(byte **scanp, BitVector *bv, AdjustInfo *adjinfo, Func *f) ...@@ -401,12 +401,12 @@ adjustpointers(byte **scanp, BitVector *bv, AdjustInfo *adjinfo, Func *f)
break; break;
case BitsPointer: case BitsPointer:
p = scanp[i]; p = scanp[i];
if(f != nil && (byte*)0 < p && (p < (byte*)PageSize || (uintptr)p == PoisonGC || (uintptr)p == PoisonStack)) { if(f != nil && (byte*)0 < p && (p < (byte*)PageSize && runtime·invalidptr || (uintptr)p == PoisonGC || (uintptr)p == PoisonStack)) {
// Looks like a junk value in a pointer slot. // Looks like a junk value in a pointer slot.
// Live analysis wrong? // Live analysis wrong?
g->m->traceback = 2; g->m->traceback = 2;
runtime·printf("runtime: bad pointer in frame %s at %p: %p\n", runtime·funcname(f), &scanp[i], p); runtime·printf("runtime: bad pointer in frame %s at %p: %p\n", runtime·funcname(f), &scanp[i], p);
runtime·throw("bad pointer!"); runtime·throw("invalid stack pointer");
} }
if(minp <= p && p < maxp) { if(minp <= p && p < maxp) {
if(StackDebug >= 3) if(StackDebug >= 3)
......
...@@ -60,7 +60,7 @@ TEXT syscall·naclWrite(SB), NOSPLIT, $24-20 ...@@ -60,7 +60,7 @@ TEXT syscall·naclWrite(SB), NOSPLIT, $24-20
TEXT runtime·write(SB),NOSPLIT,$16-20 TEXT runtime·write(SB),NOSPLIT,$16-20
// If using fake time and writing to stdout or stderr, // If using fake time and writing to stdout or stderr,
// emit playback header before actual data. // emit playback header before actual data.
MOVQ runtime·timens(SB), AX MOVQ runtime·faketime(SB), AX
CMPQ AX, $0 CMPQ AX, $0
JEQ write JEQ write
MOVL fd+0(FP), DI MOVL fd+0(FP), DI
...@@ -242,7 +242,7 @@ TEXT runtime·mmap(SB),NOSPLIT,$8 ...@@ -242,7 +242,7 @@ TEXT runtime·mmap(SB),NOSPLIT,$8
RET RET
TEXT time·now(SB),NOSPLIT,$16 TEXT time·now(SB),NOSPLIT,$16
MOVQ runtime·timens(SB), AX MOVQ runtime·faketime(SB), AX
CMPQ AX, $0 CMPQ AX, $0
JEQ realtime JEQ realtime
MOVQ $0, DX MOVQ $0, DX
...@@ -277,7 +277,7 @@ TEXT runtime·nacl_clock_gettime(SB),NOSPLIT,$0 ...@@ -277,7 +277,7 @@ TEXT runtime·nacl_clock_gettime(SB),NOSPLIT,$0
RET RET
TEXT runtime·nanotime(SB),NOSPLIT,$16 TEXT runtime·nanotime(SB),NOSPLIT,$16
MOVQ runtime·timens(SB), AX MOVQ runtime·faketime(SB), AX
CMPQ AX, $0 CMPQ AX, $0
JEQ 3(PC) JEQ 3(PC)
MOVQ AX, ret+0(FP) MOVQ AX, ret+0(FP)
......
...@@ -35,8 +35,8 @@ var timers struct { ...@@ -35,8 +35,8 @@ var timers struct {
t []*timer t []*timer
} }
// nacl fake time support. // nacl fake time support - time in nanoseconds since 1970
var timens int64 var faketime int64
// Package time APIs. // Package time APIs.
// Godoc uses the comments in package time, not these. // Godoc uses the comments in package time, not these.
...@@ -194,7 +194,7 @@ func timerproc() { ...@@ -194,7 +194,7 @@ func timerproc() {
f(arg, seq) f(arg, seq)
lock(&timers.lock) lock(&timers.lock)
} }
if delta < 0 { if delta < 0 || faketime > 0 {
// No timers left - put goroutine to sleep. // No timers left - put goroutine to sleep.
timers.rescheduling = true timers.rescheduling = true
goparkunlock(&timers.lock, "timer goroutine (idle)") goparkunlock(&timers.lock, "timer goroutine (idle)")
...@@ -208,6 +208,29 @@ func timerproc() { ...@@ -208,6 +208,29 @@ func timerproc() {
} }
} }
func timejump() *g {
if faketime == 0 {
return nil
}
lock(&timers.lock)
if !timers.created || len(timers.t) == 0 {
unlock(&timers.lock)
return nil
}
var gp *g
if faketime < timers.t[0].when {
faketime = timers.t[0].when
if timers.rescheduling {
timers.rescheduling = false
gp = timers.gp
}
}
unlock(&timers.lock)
return gp
}
// Heap maintenance algorithms. // Heap maintenance algorithms.
func siftupTimer(i int) { func siftupTimer(i int) {
......
...@@ -818,6 +818,12 @@ func create(name string, mode uint32, sec int64, data []byte) error { ...@@ -818,6 +818,12 @@ func create(name string, mode uint32, sec int64, data []byte) error {
fs.mu.Unlock() fs.mu.Unlock()
f, err := fs.open(name, O_CREATE|O_EXCL, mode) f, err := fs.open(name, O_CREATE|O_EXCL, mode)
if err != nil { if err != nil {
if mode&S_IFMT == S_IFDIR {
ip, _, err := fs.namei(name, false)
if err == nil && (ip.Mode&S_IFMT) == S_IFDIR {
return nil // directory already exists
}
}
return err return err
} }
ip := f.(*fsysFile).inode ip := f.(*fsysFile).inode
......
...@@ -153,7 +153,7 @@ func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) { ...@@ -153,7 +153,7 @@ func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) {
// RTAX_NETMASK socket address on the FreeBSD kernel. // RTAX_NETMASK socket address on the FreeBSD kernel.
preferredFamily := uint8(AF_UNSPEC) preferredFamily := uint8(AF_UNSPEC)
for i := uint(0); i < RTAX_MAX; i++ { for i := uint(0); i < RTAX_MAX; i++ {
if m.Header.Addrs&rtaIfaMask&(1<<i) == 0 { if m.Header.Addrs&(1<<i) == 0 {
continue continue
} }
rsa := (*RawSockaddr)(unsafe.Pointer(&b[0])) rsa := (*RawSockaddr)(unsafe.Pointer(&b[0]))
......
// run
// Copyright 2014 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 main
type T1 struct {
X int
}
func NewT1(x int) T1 { return T1{x} }
type T2 int
func NewT2(x int) T2 { return T2(x) }
func main() {
switch (T1{}) {
case NewT1(1):
panic("bad1")
case NewT1(0):
// ok
default:
panic("bad2")
}
switch T2(0) {
case NewT2(2):
panic("bad3")
case NewT2(0):
// ok
default:
panic("bad4")
}
}
...@@ -44,14 +44,21 @@ func checkLinear(typ string, tries int, f func(n int)) { ...@@ -44,14 +44,21 @@ func checkLinear(typ string, tries int, f func(n int)) {
} }
return return
} }
fails++ // If n ops run in under a second and the ratio
if fails == 6 { // doesn't work out, make n bigger, trying to reduce
// the effect that a constant amount of overhead has
// on the computed ratio.
if t1 < 1*time.Second {
n *= 2
continue
}
// Once the test runs long enough for n ops,
// try to get the right ratio at least once.
// If five in a row all fail, give up.
if fails++; fails >= 5 {
panic(fmt.Sprintf("%s: too slow: %d inserts: %v; %d inserts: %v\n", panic(fmt.Sprintf("%s: too slow: %d inserts: %v; %d inserts: %v\n",
typ, n, t1, 2*n, t2)) typ, n, t1, 2*n, t2))
} }
if fails < 4 {
n *= 2
}
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment