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
<p>
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.
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>
......@@ -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
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>.
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.
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>
......@@ -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.
If <code>NOSPLIT</code> is not specified for the <code>TEXT</code>,
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>
......@@ -216,19 +224,20 @@ simple name <code>profileloop</code>.
</p>
<p>
For <code>DATA</code> directives, the symbol is followed by a slash and the number
of bytes the memory associated with the symbol occupies.
The arguments are optional flags and the data itself.
For instance,
</p>
Global data symbols are defined by a sequence of initializing
<code>DATA</code> directives followed by a <code>GLOBL</code> directive.
Each <code>DATA</code> directive initializes a section of the
corresponding memory.
The memory not explicitly initialized is zeroed.
The general form of the <code>DATA</code> directive is
<pre>
DATA runtime·isplan9(SB)/4, $1
DATA symbol+offset(SB)/width, value
</pre>
<p>
declares the local symbol <code>runtime·isplan9</code> of size 4 and value 1.
Again the symbol has the middle dot and is offset from <code>SB</code>.
which initializes the symbol memory at the given offset and width with the given value.
The <code>DATA</code> directives for a given symbol must be written with increasing offsets.
</p>
<p>
......@@ -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
has initialized it.
The <code>GLOBL</code> directive must follow any corresponding <code>DATA</code> directives.
This example
</p>
<p>
For example,
</p>
<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>
<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>
......@@ -299,6 +319,80 @@ This is a wrapper function and should not count as disabling <code>recover</code
</li>
</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>
<p>
......@@ -434,13 +528,10 @@ Here's how the 386 runtime defines the 64-bit atomic load function.
// so actually
// void atomicload64(uint64 *res, uint64 volatile *addr);
TEXT runtime·atomicload64(SB), NOSPLIT, $0-8
MOVL 4(SP), BX
MOVL 8(SP), AX
// MOVQ (%EAX), %MM0
BYTE $0x0f; BYTE $0x6f; BYTE $0x00
// MOVQ %MM0, 0(%EBX)
BYTE $0x0f; BYTE $0x7f; BYTE $0x03
// EMMS
BYTE $0x0F; BYTE $0x77
MOVL ptr+0(FP), AX
LEAL ret_lo+4(FP), BX
BYTE $0x0f; BYTE $0x6f; BYTE $0x00 // MOVQ (%EAX), %MM0
BYTE $0x0f; BYTE $0x7f; BYTE $0x03 // MOVQ %MM0, 0(%EBX)
BYTE $0x0F; BYTE $0x77 // EMMS
RET
</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
observe values produced by writes to the same variable in a different goroutine.
</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>
<p>
......
<!--{
"Title": "The Go Programming Language Specification",
"Subtitle": "Version of October 23, 2014",
"Subtitle": "Version of October 27, 2014",
"Path": "/ref/spec"
}-->
......@@ -2449,12 +2449,11 @@ Primary expressions are the operands for unary and binary expressions.
PrimaryExpr =
Operand |
Conversion |
BuiltinCall |
PrimaryExpr Selector |
PrimaryExpr Index |
PrimaryExpr Slice |
PrimaryExpr TypeAssertion |
PrimaryExpr Call .
PrimaryExpr Arguments .
Selector = "." identifier .
Index = "[" Expression "]" .
......@@ -2462,8 +2461,7 @@ Slice = "[" ( [ Expression ] ":" [ Expression ] ) |
( [ Expression ] ":" Expression ":" Expression )
"]" .
TypeAssertion = "." "(" Type ")" .
Call = "(" [ ArgumentList [ "," ] ] ")" .
ArgumentList = ExpressionList [ "..." ] .
Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
</pre>
......@@ -3166,7 +3164,7 @@ the method.
<pre>
math.Atan2(x, y) // function call
var pt *Point
pt.Scale(3.5) // method call with receiver pt
pt.Scale(3.5) // method call with receiver pt
</pre>
<p>
......@@ -5371,11 +5369,6 @@ so they can only appear in <a href="#Calls">call expressions</a>;
they cannot be used as function values.
</p>
<pre class="ebnf">
BuiltinCall = identifier "(" [ BuiltinArgs [ "," ] ] ")" .
BuiltinArgs = Type [ "," ArgumentList ] | ArgumentList .
</pre>
<h3 id="Close">Close</h3>
<p>
......
......@@ -24,7 +24,6 @@ char *runtimeimport =
"func @\"\".printslice (? any)\n"
"func @\"\".printnl ()\n"
"func @\"\".printsp ()\n"
"func @\"\".goprintf ()\n"
"func @\"\".concatstring2 (? string, ? string) (? string)\n"
"func @\"\".concatstring3 (? string, ? string, ? string) (? string)\n"
"func @\"\".concatstring4 (? string, ? string, ? string, ? string) (? string)\n"
......
......@@ -36,7 +36,6 @@ func printeface(any)
func printslice(any)
func printnl()
func printsp()
func goprintf()
func concatstring2(string, string) string
func concatstring3(string, string, string) string
......
......@@ -7,7 +7,7 @@
#include "go.h"
#include "../ld/textflag.h"
static Node* walkprint(Node*, NodeList**, int);
static Node* walkprint(Node*, NodeList**);
static Node* writebarrierfn(char*, Type*, Type*);
static Node* applywritebarrier(Node*, NodeList**);
static Node* mapfn(char*, Type*);
......@@ -32,6 +32,7 @@ static void walkmul(Node**, NodeList**);
static void walkdiv(Node**, NodeList**);
static int bounded(Node*, int64);
static Mpint mpzero;
static void walkprintfunc(Node**, NodeList**);
void
walk(Node *fn)
......@@ -226,8 +227,7 @@ walkstmt(Node **np)
switch(n->left->op) {
case OPRINT:
case OPRINTN:
walkexprlist(n->left->list, &n->ninit);
n->left = walkprint(n->left, &n->ninit, 1);
walkprintfunc(&n->left, &n->ninit);
break;
case OCOPY:
n->left = copyany(n->left, &n->ninit, 1);
......@@ -260,8 +260,7 @@ walkstmt(Node **np)
switch(n->left->op) {
case OPRINT:
case OPRINTN:
walkexprlist(n->left->list, &n->ninit);
n->left = walkprint(n->left, &n->ninit, 1);
walkprintfunc(&n->left, &n->ninit);
break;
case OCOPY:
n->left = copyany(n->left, &n->ninit, 1);
......@@ -543,7 +542,7 @@ walkexpr(Node **np, NodeList **init)
case OPRINT:
case OPRINTN:
walkexprlist(n->list, init);
n = walkprint(n, init, 0);
n = walkprint(n, init);
goto ret;
case OPANIC:
......@@ -1757,7 +1756,7 @@ ret:
// generate code for print
static Node*
walkprint(Node *nn, NodeList **init, int defer)
walkprint(Node *nn, NodeList **init)
{
Node *r;
Node *n;
......@@ -1765,31 +1764,17 @@ walkprint(Node *nn, NodeList **init, int defer)
Node *on;
Type *t;
int notfirst, et, op;
NodeList *calls, *intypes, *args;
Fmt fmt;
NodeList *calls;
on = nil;
op = nn->op;
all = nn->list;
calls = nil;
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) {
if(notfirst) {
if(defer)
fmtprint(&fmt, " ");
else
calls = list(calls, mkcall("printsp", T, init));
calls = list(calls, mkcall("printsp", T, init));
}
notfirst = op == OPRINTN;
......@@ -1817,122 +1802,63 @@ walkprint(Node *nn, NodeList **init, int defer)
t = n->type;
et = n->type->etype;
if(isinter(n->type)) {
if(defer) {
if(isnilinter(n->type))
fmtprint(&fmt, "%%e");
else
fmtprint(&fmt, "%%i");
} else {
if(isnilinter(n->type))
on = syslook("printeface", 1);
else
on = syslook("printiface", 1);
argtype(on, n->type); // any-1
}
if(isnilinter(n->type))
on = syslook("printeface", 1);
else
on = syslook("printiface", 1);
argtype(on, n->type); // any-1
} else if(isptr[et] || et == TCHAN || et == TMAP || et == TFUNC || et == TUNSAFEPTR) {
if(defer) {
fmtprint(&fmt, "%%p");
} else {
on = syslook("printpointer", 1);
argtype(on, n->type); // any-1
}
on = syslook("printpointer", 1);
argtype(on, n->type); // any-1
} else if(isslice(n->type)) {
if(defer) {
fmtprint(&fmt, "%%a");
} else {
on = syslook("printslice", 1);
argtype(on, n->type); // any-1
}
on = syslook("printslice", 1);
argtype(on, n->type); // any-1
} else if(isint[et]) {
if(defer) {
if(et == TUINT64)
fmtprint(&fmt, "%%U");
else {
fmtprint(&fmt, "%%D");
t = types[TINT64];
}
} else {
if(et == TUINT64) {
if((t->sym->pkg == runtimepkg || compiling_runtime) && strcmp(t->sym->name, "hex") == 0)
on = syslook("printhex", 0);
else
on = syslook("printuint", 0);
} else
on = syslook("printint", 0);
}
} else if(isfloat[et]) {
if(defer) {
fmtprint(&fmt, "%%f");
t = types[TFLOAT64];
if(et == TUINT64) {
if((t->sym->pkg == runtimepkg || compiling_runtime) && strcmp(t->sym->name, "hex") == 0)
on = syslook("printhex", 0);
else
on = syslook("printuint", 0);
} else
on = syslook("printfloat", 0);
on = syslook("printint", 0);
} else if(isfloat[et]) {
on = syslook("printfloat", 0);
} 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) {
if(defer)
fmtprint(&fmt, "%%t");
else
on = syslook("printbool", 0);
on = syslook("printbool", 0);
} else if(et == TSTRING) {
if(defer)
fmtprint(&fmt, "%%S");
else
on = syslook("printstring", 0);
on = syslook("printstring", 0);
} else {
badtype(OPRINT, n->type, T);
continue;
}
if(!defer) {
t = *getinarg(on->type);
if(t != nil)
t = t->type;
if(t != nil)
t = t->type;
}
t = *getinarg(on->type);
if(t != nil)
t = t->type;
if(t != nil)
t = t->type;
if(!eqtype(t, n->type)) {
n = nod(OCONV, n, N);
n->type = t;
}
if(defer) {
intypes = list(intypes, nod(ODCLFIELD, N, typenod(t)));
args = list(args, n);
} else {
r = nod(OCALL, on, N);
r->list = list1(n);
calls = list(calls, r);
}
r = nod(OCALL, on, N);
r->list = list1(n);
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)
calls = list(calls, mkcall("printnl", T, nil));
typechecklist(calls, Etop);
walkexprlist(calls, init);
if(op == OPRINTN)
calls = list(calls, mkcall("printnl", T, nil));
typechecklist(calls, Etop);
walkexprlist(calls, init);
r = nod(OEMPTY, N, N);
typecheck(&r, Etop);
walkexpr(&r, init);
r->ninit = calls;
}
r = nod(OEMPTY, N, N);
typecheck(&r, Etop);
walkexpr(&r, init);
r->ninit = calls;
return r;
}
......@@ -3229,7 +3155,7 @@ countfield(Type *t)
static void
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;
Type *t, *t1;
......@@ -3249,18 +3175,25 @@ walkcompare(Node **np, NodeList **init)
break;
}
if(!islvalue(n->left) || !islvalue(n->right)) {
fatal("arguments of comparison must be lvalues");
cmpl = n->left;
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));
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
typecheck(&a, Etop);
*init = list(*init, a);
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
typecheck(&a, Etop);
*init = list(*init, a);
......@@ -3941,3 +3874,71 @@ candiscard(Node *n)
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 (
)
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",
Long: `
Get downloads and installs the packages named by the import paths,
......@@ -25,6 +25,11 @@ along with their dependencies.
The -d flag instructs get to stop after downloading the packages; that is,
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
before resolving dependencies or building the code.
......@@ -53,6 +58,7 @@ See also: go build, go install, go clean.
}
var getD = cmdGet.Flag.Bool("d", false, "")
var getF = cmdGet.Flag.Bool("f", false, "")
var getT = cmdGet.Flag.Bool("t", false, "")
var getU = cmdGet.Flag.Bool("u", false, "")
var getFix = cmdGet.Flag.Bool("fix", false, "")
......@@ -63,6 +69,10 @@ func init() {
}
func runGet(cmd *Command, args []string) {
if *getF && !*getU {
fatalf("go get: cannot use -f flag without -u")
}
// Phase 1. Download/update.
var stk importStack
for _, arg := range downloadPaths(args) {
......@@ -268,7 +278,7 @@ func downloadPackage(p *Package) error {
repo = "<local>" // should be unused; make distinctive
// Double-check where it came from.
if *getU && vcs.remoteRepo != nil {
if *getU && vcs.remoteRepo != nil && !*getF {
dir := filepath.Join(p.build.SrcRoot, rootPath)
if remote, err := vcs.remoteRepo(vcs, dir); err == nil {
if rr, err := repoRootForImportPath(p.ImportPath); err == nil {
......
......@@ -219,6 +219,16 @@ q' | ed $d/src/$config >/dev/null 2>&1
cat $d/err
ok=false
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
rm -rf $d
}
......
......@@ -159,7 +159,7 @@ func dump(tab *gosym.Table, lookup lookupFunc, disasm disasmFunc, goarch string,
printed := false
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
}
if sym.Addr >= textStart+uint64(len(textData)) || sym.Addr+uint64(sym.Size) > textStart+uint64(len(textData)) {
......
......@@ -157,12 +157,15 @@ var armNeed = []string{
// binary for the current system (only) and test that objdump
// can handle that one.
func TestDisasm(t *testing.T) {
func testDisasm(t *testing.T, flags ...string) {
tmp, exe := buildObjdump(t)
defer os.RemoveAll(tmp)
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 {
t.Fatalf("go build fmthello.go: %v\n%s", err, out)
}
......@@ -194,3 +197,15 @@ func TestDisasm(t *testing.T) {
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) {
case '\r':
e.WriteByte('\\')
e.WriteByte('r')
case '\t':
e.WriteByte('\\')
e.WriteByte('t')
default:
// This encodes bytes < 0x20 except for \n and \r,
// as well as <, > and &. The latter are escaped because they
......@@ -878,9 +881,12 @@ func (e *encodeState) stringBytes(s []byte) (int, error) {
case '\r':
e.WriteByte('\\')
e.WriteByte('r')
case '\t':
e.WriteByte('\\')
e.WriteByte('t')
default:
// 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
// are rendered into JSON and served to some browsers.
e.WriteString(`\u00`)
......
......@@ -478,3 +478,55 @@ func TestEncodePointerString(t *testing.T) {
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 @@
%E scientific notation, e.g. -1234.456E+78
%f decimal point but no exponent, e.g. 123.456
%F synonym for %f
%g whichever of %e or %f produces more compact output
%G whichever of %E or %f produces more compact output
%g %e for large exponents, %f otherwise
%G %E for large exponents, %F otherwise
String and slice of bytes:
%s the uninterpreted bytes of the string or slice
%q a double-quoted string safely escaped with Go syntax
......
......@@ -138,7 +138,7 @@ func TestJSValEscaper(t *testing.T) {
// Newlines.
{"\r\n\u2028\u2029", `"\r\n\u2028\u2029"`},
// "\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}`},
{[]interface{}{}, "[]"},
{[]interface{}{42, "foo", nil}, `[42,"foo",null]`},
......
......@@ -8,6 +8,7 @@ import (
"flag"
"fmt"
"testing"
"time"
)
var testDNSFlood = flag.Bool("dnsflood", false, "whether to test dns query flooding")
......@@ -35,3 +36,64 @@ func TestDNSThreadLimit(t *testing.T) {
// 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) {
if d.bufp >= d.nbuf {
d.bufp = 0
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 {
return names, NewSyscallError("readdirent", errno)
}
......
......@@ -255,3 +255,12 @@ var lstat = Lstat
func Rename(oldpath, newpath string) error {
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) {
// read reads up to len(b) bytes from the File.
// It returns the number of bytes read and an error, if any.
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.
// It returns the number of bytes read and the error, if any.
// EOF is signaled by a zero count with err set to nil.
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.
......@@ -262,7 +262,7 @@ func (f *File) write(b []byte) (n int, err error) {
if len(b) == 0 {
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.
......@@ -273,7 +273,7 @@ func (f *File) pwrite(b []byte, off int64) (n int, err error) {
if len(b) == 0 {
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
......
......@@ -18,7 +18,7 @@ func sigpipe() // implemented in package runtime
func Readlink(name string) (string, error) {
for len := 128; ; len *= 2 {
b := make([]byte, len)
n, e := syscall.Readlink(name, b)
n, e := fixCount(syscall.Readlink(name, b))
if e != nil {
return "", &PathError{"readlink", name, e}
}
......
......@@ -187,7 +187,7 @@ func (f *File) read(b []byte) (n int, err error) {
if needsMaxRW && len(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.
......@@ -197,7 +197,7 @@ func (f *File) pread(b []byte, off int64) (n int, err error) {
if needsMaxRW && len(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.
......@@ -208,7 +208,7 @@ func (f *File) write(b []byte) (n int, err error) {
if needsMaxRW && len(bcap) > maxRW {
bcap = bcap[:maxRW]
}
m, err := syscall.Write(f.fd, bcap)
m, err := fixCount(syscall.Write(f.fd, bcap))
n += m
// 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) {
if needsMaxRW && len(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
......
......@@ -295,7 +295,7 @@ func (f *File) read(b []byte) (n int, err error) {
if f.isConsole {
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.
......@@ -376,7 +376,7 @@ func (f *File) write(b []byte) (n int, err error) {
if f.isConsole {
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.
......
......@@ -732,6 +732,20 @@ needm:
MOVL g(CX), 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:
// 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.
......
......@@ -717,6 +717,20 @@ needm:
get_tls(CX)
MOVQ g(CX), 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:
// Now there's a valid m, and we're running on its m->g0.
......
......@@ -556,6 +556,21 @@ TEXT ·cgocallback_gofunc(SB),NOSPLIT,$8-12
MOVW $runtime·needm(SB), 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:
MOVW g_m(g), R8
MOVW R8, savedm-4(SP)
......
......@@ -8,6 +8,7 @@ package runtime_test
import (
"runtime"
"strings"
"testing"
)
......@@ -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 = `
package main
......@@ -117,3 +129,43 @@ func main() {
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 {
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 {
case "android", "nacl":
t.Skipf("skipping on %s", runtime.GOOS)
......@@ -61,7 +61,20 @@ func executeTest(t *testing.T, templ string, data interface{}) string {
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)
}
......
......@@ -16,6 +16,7 @@ type GCStats struct {
NumGC int64 // number of garbage collections
PauseTotal time.Duration // total pause for all collections
Pause []time.Duration // pause history, most recent first
PauseEnd []time.Time // pause end times history, most recent first
PauseQuantiles []time.Duration
}
......@@ -30,25 +31,36 @@ type GCStats struct {
func ReadGCStats(stats *GCStats) {
// Create a buffer with space for at least two copies of the
// pause history tracked by the runtime. One will be returned
// to the caller and the other will be used as a temporary buffer
// for computing quantiles.
// to the caller and the other will be used as transfer buffer
// for end times history and as a temporary buffer for
// computing quantiles.
const maxPause = len(((*runtime.MemStats)(nil)).PauseNs)
if cap(stats.Pause) < 2*maxPause {
stats.Pause = make([]time.Duration, 2*maxPause)
if cap(stats.Pause) < 2*maxPause+3 {
stats.Pause = make([]time.Duration, 2*maxPause+3)
}
// readGCStats fills in the pause history (up to maxPause entries)
// and then three more: Unix ns time of last GC, number of GC,
// and total pause time in nanoseconds. Here we depend on the
// fact that time.Duration's native unit is nanoseconds, so the
// pauses and the total pause time do not need any conversion.
// readGCStats fills in the pause and end times histories (up to
// maxPause entries) and then three more: Unix ns time of last GC,
// number of GC, and total pause time in nanoseconds. Here we
// depend on the fact that time.Duration's native unit is
// nanoseconds, so the pauses and the total pause time do not need
// any conversion.
readGCStats(&stats.Pause)
n := len(stats.Pause) - 3
stats.LastGC = time.Unix(0, int64(stats.Pause[n]))
stats.NumGC = int64(stats.Pause[n+1])
stats.PauseTotal = stats.Pause[n+2]
n /= 2 // buffer holds pauses and end times
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 n == 0 {
for i := range stats.PauseQuantiles {
......
......@@ -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])
}
}
// 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)
......
......@@ -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
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
detailed multiline info every X milliseconds, describing state of the scheduler,
processors, threads and goroutines.
......
......@@ -28,6 +28,9 @@
// defines the pointer map for the function's arguments.
// GO_ARGS should be the first instruction in a function that uses it.
// 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)
// GO_RESULTS_INITIALIZED indicates that the assembly function
......
......@@ -267,7 +267,8 @@ struct MStats
uint64 next_gc; // next GC (in heap_alloc time)
uint64 last_gc; // last GC (in absolute time)
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;
bool enablegc;
bool debuggc;
......
......@@ -44,7 +44,8 @@ type MemStats struct {
NextGC uint64 // next collection will happen when HeapAlloc ≥ this amount
LastGC uint64 // end time of last collection (nanoseconds since 1970)
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
EnableGC bool
DebugGC bool
......
......@@ -330,7 +330,7 @@ scanblock(byte *b, uintptr n, byte *ptrmask)
if(obj == nil)
continue;
if(obj < arena_start || obj >= arena_used) {
if((uintptr)obj < PhysPageSize) {
if((uintptr)obj < PhysPageSize && runtime·invalidptr) {
s = nil;
goto badobj;
}
......@@ -375,7 +375,7 @@ scanblock(byte *b, uintptr n, byte *ptrmask)
else
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)
runtime·throw("bad pointer");
runtime·throw("invalid heap pointer");
// Add to badblock list, which will cause the garbage collection
// to keep repeating until it has traced the chain of pointers
// leading to obj all the way back to a root.
......@@ -1459,6 +1459,7 @@ gc(struct gc_args *args)
t4 = runtime·nanotime();
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_end[mstats.numgc%nelem(mstats.pause_end)] = t4;
mstats.pause_total_ns += t4 - t0;
mstats.numgc++;
if(mstats.debuggc)
......@@ -1572,7 +1573,7 @@ readgcstats_m(void)
{
Slice *pauses;
uint64 *p;
uint32 i, n;
uint32 i, j, n;
pauses = g->m->ptrarg[0];
g->m->ptrarg[0] = nil;
......@@ -1581,25 +1582,29 @@ readgcstats_m(void)
if(pauses->cap < nelem(mstats.pause_ns)+3)
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;
runtime·lock(&runtime·mheap.lock);
n = mstats.numgc;
if(n > nelem(mstats.pause_ns))
n = nelem(mstats.pause_ns);
// The pause buffer is circular. The most recent pause is at
// pause_ns[(numgc-1)%nelem(pause_ns)], and then backward
// from there to go back farther in time. We deliver the times
// most recent first (in p[0]).
for(i=0; i<n; i++)
p[i] = mstats.pause_ns[(mstats.numgc-1-i)%nelem(mstats.pause_ns)];
for(i=0; i<n; i++) {
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+1] = mstats.numgc;
p[n+2] = mstats.pause_total_ns;
p[n+n] = mstats.last_gc;
p[n+n+1] = mstats.numgc;
p[n+n+2] = mstats.pause_total_ns;
runtime·unlock(&runtime·mheap.lock);
pauses->len = n+3;
pauses->len = n+n+3;
}
void
......
......@@ -19,32 +19,17 @@ func bytes(s string) (ret []byte) {
return
}
// goprintf is the function call that is actually deferred when you write
// defer print(...)
// It is otherwise unused. In particular it is not used for ordinary prints.
// 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.
// printf is only called from C code. It has no type information for the args,
// but C stacks are ignored by the garbage collector anyway, so having
// type information would not add anything.
//go:nosplit
func printf(s *byte) {
vprintf(gostringnocopy(s), add(unsafe.Pointer(&s), unsafe.Sizeof(s)))
}
// sprintf is only called from C code.
// It has the same problem as goprintf.
// sprintf is only called from C code. It has no type information for the args,
// but C stacks are ignored by the garbage collector anyway, so having
// type information would not add anything.
//go:nosplit
func snprintf(dst *byte, n int32, s *byte) {
buf := (*[1 << 30]byte)(unsafe.Pointer(dst))[0:n:n]
......
......@@ -2758,6 +2758,8 @@ static void
checkdead(void)
{
G *gp;
P *p;
M *mp;
int32 run, grunning, s;
uintptr i;
......@@ -2799,6 +2801,24 @@ checkdead(void)
runtime·unlock(&runtime·allglock);
if(grunning == 0) // possible if main goroutine calls runtime·Goexit()
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
runtime·throw("all goroutines are asleep - deadlock!");
}
......
......@@ -26,5 +26,5 @@ TEXT _rt0_amd64p32_nacl(SB),NOSPLIT,$16
TEXT main(SB),NOSPLIT,$0
// Uncomment for fake time like on Go Playground.
//MOVQ $1257894000000000000, AX
//MOVQ AX, runtime·timens(SB)
//MOVQ AX, runtime·faketime(SB)
JMP runtime·rt0_go(SB)
......@@ -282,9 +282,13 @@ struct DbgVar
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 */
static DbgVar dbgvar[] = {
{"allocfreetrace", &runtime·debug.allocfreetrace},
{"invalidptr", &runtime·invalidptr},
{"efence", &runtime·debug.efence},
{"gctrace", &runtime·debug.gctrace},
{"gcdead", &runtime·debug.gcdead},
......
......@@ -657,6 +657,8 @@ enum {
byte* runtime·startup_random_data;
uint32 runtime·startup_random_data_len;
int32 runtime·invalidptr;
enum {
// hashinit wants this many random bytes
HashRandomBytes = 32
......
......@@ -401,12 +401,12 @@ adjustpointers(byte **scanp, BitVector *bv, AdjustInfo *adjinfo, Func *f)
break;
case BitsPointer:
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.
// Live analysis wrong?
g->m->traceback = 2;
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(StackDebug >= 3)
......
......@@ -60,7 +60,7 @@ TEXT syscall·naclWrite(SB), NOSPLIT, $24-20
TEXT runtime·write(SB),NOSPLIT,$16-20
// If using fake time and writing to stdout or stderr,
// emit playback header before actual data.
MOVQ runtime·timens(SB), AX
MOVQ runtime·faketime(SB), AX
CMPQ AX, $0
JEQ write
MOVL fd+0(FP), DI
......@@ -242,7 +242,7 @@ TEXT runtime·mmap(SB),NOSPLIT,$8
RET
TEXT time·now(SB),NOSPLIT,$16
MOVQ runtime·timens(SB), AX
MOVQ runtime·faketime(SB), AX
CMPQ AX, $0
JEQ realtime
MOVQ $0, DX
......@@ -277,7 +277,7 @@ TEXT runtime·nacl_clock_gettime(SB),NOSPLIT,$0
RET
TEXT runtime·nanotime(SB),NOSPLIT,$16
MOVQ runtime·timens(SB), AX
MOVQ runtime·faketime(SB), AX
CMPQ AX, $0
JEQ 3(PC)
MOVQ AX, ret+0(FP)
......
......@@ -35,8 +35,8 @@ var timers struct {
t []*timer
}
// nacl fake time support.
var timens int64
// nacl fake time support - time in nanoseconds since 1970
var faketime int64
// Package time APIs.
// Godoc uses the comments in package time, not these.
......@@ -194,7 +194,7 @@ func timerproc() {
f(arg, seq)
lock(&timers.lock)
}
if delta < 0 {
if delta < 0 || faketime > 0 {
// No timers left - put goroutine to sleep.
timers.rescheduling = true
goparkunlock(&timers.lock, "timer goroutine (idle)")
......@@ -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.
func siftupTimer(i int) {
......
......@@ -818,6 +818,12 @@ func create(name string, mode uint32, sec int64, data []byte) error {
fs.mu.Unlock()
f, err := fs.open(name, O_CREATE|O_EXCL, mode)
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
}
ip := f.(*fsysFile).inode
......
......@@ -153,7 +153,7 @@ func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) {
// RTAX_NETMASK socket address on the FreeBSD kernel.
preferredFamily := uint8(AF_UNSPEC)
for i := uint(0); i < RTAX_MAX; i++ {
if m.Header.Addrs&rtaIfaMask&(1<<i) == 0 {
if m.Header.Addrs&(1<<i) == 0 {
continue
}
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)) {
}
return
}
fails++
if fails == 6 {
// If n ops run in under a second and the ratio
// 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",
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