Compare commits
12 Commits
master
...
release.r5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb10bce0c2 | ||
|
|
ff5182390a | ||
|
|
36d155b2b5 | ||
|
|
f86856b083 | ||
|
|
f12a1d38b2 | ||
|
|
3b32b3eb3d | ||
|
|
f70c7b2b63 | ||
|
|
01a1c91696 | ||
|
|
dbdc8698df | ||
|
|
47906598d8 | ||
|
|
0ea0d7b65c | ||
|
|
35f3007cf1 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -14,6 +14,78 @@ hg pull
|
|||||||
hg update release.r<i>NN</i>
|
hg update release.r<i>NN</i>
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
<h2 id="r58">r58 (released 2011/06/29)</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The r58 release corresponds to
|
||||||
|
<code><a href="weekly.html#2011-06-09">weekly.2011-06-09</a></code>
|
||||||
|
with additional bug fixes.
|
||||||
|
This section highlights the most significant changes in this release.
|
||||||
|
For a more detailed summary, see the
|
||||||
|
<a href="weekly.html#2011-06-09">weekly release notes</a>.
|
||||||
|
For complete information, see the
|
||||||
|
<a href="http://code.google.com/p/go/source/list?r=release-branch.r58">Mercurial change list</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="r58.lang">Language</h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This release fixes a <a href="http://code.google.com/p/go/source/detail?r=b720749486e1">use of uninitialized memory in programs that misuse <code>goto</code></a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="r58.pkg">Packages</h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
As usual, <a href="/cmd/gofix/">gofix</a> will handle the bulk of the rewrites
|
||||||
|
necessary for these changes to package APIs.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="/pkg/http/">Package http</a> drops the <code>finalURL</code> return
|
||||||
|
value from the <a href="/pkg/http/#Client.Get">Client.Get</a> method. The value
|
||||||
|
is now available via the new <code>Request</code> field on <a
|
||||||
|
href="/pkg/http/#Response">http.Response</a>.
|
||||||
|
Most instances of the type map[string][]string in have been
|
||||||
|
replaced with the new <a href="/pkg/http/#Values">Values</a> type.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="/pkg/exec/">Package exec</a> has been redesigned with a more
|
||||||
|
convenient and succinct API.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="/pkg/strconv/">Package strconv</a>'s <a href="/pkg/strconv/#Quote">Quote</a>
|
||||||
|
function now escapes only those Unicode code points not classified as printable
|
||||||
|
by <a href="/pkg/unicode/#IsPrint">unicode.IsPrint</a>.
|
||||||
|
Previously Quote would escape all non-ASCII characters.
|
||||||
|
This also affects the <a href="/pkg/fmt/">fmt</a> package's <code>"%q"</code>
|
||||||
|
formatting directive. The previous quoting behavior is still available via
|
||||||
|
strconv's new <a href="/pkg/strconv/#QuoteToASCII">QuoteToASCII</a> function.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="/pkg/os/signal/">Package os/signal</a>'s
|
||||||
|
<a href="/pkg/os/#Signal">Signal</a> and
|
||||||
|
<a href="/pkg/os/#UnixSignal">UnixSignal</a> types have been moved to the
|
||||||
|
<a href="/pkg/os/">os</a> package.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="/pkg/image/draw/">Package image/draw</a> is the new name for
|
||||||
|
<code>exp/draw</code>. The GUI-related code from <code>exp/draw</code> is now
|
||||||
|
located in the <a href="/pkg/exp/gui/">exp/gui</a> package.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="r58.cmd">Tools</h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="/cmd/goinstall/">Goinstall</a> now observes the GOPATH environment
|
||||||
|
variable to build and install your own code and external libraries outside of
|
||||||
|
the Go tree (and avoid writing Makefiles).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
<h2 id="r57">r57 (released 2011/05/03)</h2>
|
<h2 id="r57">r57 (released 2011/05/03)</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
@@ -162,6 +234,7 @@ For other uses, see the <a href="/pkg/runtime/pprof/">runtime/pprof</a> document
|
|||||||
<h3 id="r57.minor">Minor revisions</h3>
|
<h3 id="r57.minor">Minor revisions</h3>
|
||||||
|
|
||||||
<p>r57.1 fixes a <a href="http://code.google.com/p/go/source/detail?r=ff2bc62726e7145eb2ecc1e0f076998e4a8f86f0">nil pointer dereference in http.FormFile</a>.</p>
|
<p>r57.1 fixes a <a href="http://code.google.com/p/go/source/detail?r=ff2bc62726e7145eb2ecc1e0f076998e4a8f86f0">nil pointer dereference in http.FormFile</a>.</p>
|
||||||
|
<p>r57.2 fixes a <a href="http://code.google.com/p/go/source/detail?r=063b0ff67d8277df03c956208abc068076818dae">use of uninitialized memory in programs that misuse <code>goto</code></a>.</p>
|
||||||
|
|
||||||
<h2 id="r56">r56 (released 2011/03/16)</h2>
|
<h2 id="r56">r56 (released 2011/03/16)</h2>
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ hg pull
|
|||||||
hg update weekly.<i>YYYY-MM-DD</i>
|
hg update weekly.<i>YYYY-MM-DD</i>
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<h2 id="2011-06-09">2011-06-09</h2>
|
<h2 id="2011-06-09">2011-06-09 (<a href="release.html#r58">base for r58</a>)</h2>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
This release includes changes to the strconv, http, and exp/draw packages.
|
This release includes changes to the strconv, http, and exp/draw packages.
|
||||||
|
|||||||
@@ -23,6 +23,17 @@ concepts: syntax, types, allocation, constants, I/O, sorting, printing,
|
|||||||
goroutines, and channels.
|
goroutines, and channels.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<h3 id="course_notes">Course Notes</h3>
|
||||||
|
<p>
|
||||||
|
Slides from a 3-day course about the Go programming language.
|
||||||
|
A more thorough introduction than the tutorial.
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li><a href="GoCourseDay1.pdf">Day 1: Basics</a> <small>[270KB PDF]</small>
|
||||||
|
<li><a href="GoCourseDay2.pdf">Day 2: Types, Methods, Interfaces</a> <small>[270KB PDF]</small>
|
||||||
|
<li><a href="GoCourseDay3.pdf">Day 3: Concurrency and Communication</a> <small>[180KB PDF]</small>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h3 id="effective_go"><a href="effective_go.html">Effective Go</a></h3>
|
<h3 id="effective_go"><a href="effective_go.html">Effective Go</a></h3>
|
||||||
<p>
|
<p>
|
||||||
A document that gives tips for writing clear, idiomatic Go code.
|
A document that gives tips for writing clear, idiomatic Go code.
|
||||||
@@ -209,7 +220,7 @@ from Hoare’s 1978 paper to Go provides insight into how and why Go works as it
|
|||||||
does.
|
does.
|
||||||
</i></p>
|
</i></p>
|
||||||
|
|
||||||
<h3 id="emerging_go"><a href="talks/gofrontend-gcc-summit-2010.pdf">The Go frontend for GCC</a></h3>
|
<h3 id="go_frontend_gcc"><a href="talks/gofrontend-gcc-summit-2010.pdf">The Go frontend for GCC</a></h3>
|
||||||
<p>
|
<p>
|
||||||
A description of the Go language frontend for gcc.
|
A description of the Go language frontend for gcc.
|
||||||
Ian Lance Taylor's paper delivered at the GCC Summit 2010.
|
Ian Lance Taylor's paper delivered at the GCC Summit 2010.
|
||||||
|
|||||||
@@ -183,16 +183,6 @@ easier to understand what happens when things combine.
|
|||||||
|
|
||||||
<h2 id="Usage">Usage</h2>
|
<h2 id="Usage">Usage</h2>
|
||||||
|
|
||||||
<h3 id="Who_should_use_the_language">
|
|
||||||
Who should use the language?</h3>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Go is an experiment. We hope adventurous users will give it a try and see
|
|
||||||
if they enjoy it. Not every programmer
|
|
||||||
will, but we hope enough will find satisfaction in the approach it
|
|
||||||
offers to justify further development.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3 id="Is_Google_using_go_internally"> Is Google using Go internally?</h3>
|
<h3 id="Is_Google_using_go_internally"> Is Google using Go internally?</h3>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
@@ -598,6 +588,24 @@ the interface idea. Sometimes, though, they're necessary to resolve ambiguities
|
|||||||
among similar interfaces.
|
among similar interfaces.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<h3 id="convert_slice_of_interface">
|
||||||
|
Can I convert a []T to an []interface{}?</h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Not directly because they do not have the same representation in memory.
|
||||||
|
It is necessary to copy the elements individually to the destination
|
||||||
|
slice. This example converts a slice of <code>int</code> to a slice of
|
||||||
|
<code>interface{}</code>:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
t := []int{1, 2, 3, 4}
|
||||||
|
s := make([]interface{}, len(t))
|
||||||
|
for i, v := range t {
|
||||||
|
s[i] = v
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
<h2 id="values">Values</h2>
|
<h2 id="values">Values</h2>
|
||||||
|
|
||||||
<h3 id="conversions">
|
<h3 id="conversions">
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ After you've read this tutorial, you should look at
|
|||||||
which digs deeper into how the language is used and
|
which digs deeper into how the language is used and
|
||||||
talks about the style and idioms of programming in Go.
|
talks about the style and idioms of programming in Go.
|
||||||
Also, slides from a 3-day course about Go are available.
|
Also, slides from a 3-day course about Go are available.
|
||||||
Although they're badly out of date, they provide some
|
They provide some background and a lot of examples:
|
||||||
background and a lot of examples:
|
|
||||||
<a href='/doc/GoCourseDay1.pdf'>Day 1</a>,
|
<a href='/doc/GoCourseDay1.pdf'>Day 1</a>,
|
||||||
<a href='/doc/GoCourseDay2.pdf'>Day 2</a>,
|
<a href='/doc/GoCourseDay2.pdf'>Day 2</a>,
|
||||||
<a href='/doc/GoCourseDay3.pdf'>Day 3</a>.
|
<a href='/doc/GoCourseDay3.pdf'>Day 3</a>.
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ After you've read this tutorial, you should look at
|
|||||||
which digs deeper into how the language is used and
|
which digs deeper into how the language is used and
|
||||||
talks about the style and idioms of programming in Go.
|
talks about the style and idioms of programming in Go.
|
||||||
Also, slides from a 3-day course about Go are available.
|
Also, slides from a 3-day course about Go are available.
|
||||||
Although they're badly out of date, they provide some
|
They provide some background and a lot of examples:
|
||||||
background and a lot of examples:
|
|
||||||
<a href='/doc/GoCourseDay1.pdf'>Day 1</a>,
|
<a href='/doc/GoCourseDay1.pdf'>Day 1</a>,
|
||||||
<a href='/doc/GoCourseDay2.pdf'>Day 2</a>,
|
<a href='/doc/GoCourseDay2.pdf'>Day 2</a>,
|
||||||
<a href='/doc/GoCourseDay3.pdf'>Day 3</a>.
|
<a href='/doc/GoCourseDay3.pdf'>Day 3</a>.
|
||||||
|
|||||||
@@ -124,6 +124,64 @@ newplist(void)
|
|||||||
return pl;
|
return pl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
clearstk(void)
|
||||||
|
{
|
||||||
|
Plist *pl;
|
||||||
|
Prog *p, *p1, *p2, *p3;
|
||||||
|
Node dst, end, zero, con;
|
||||||
|
|
||||||
|
if(plast->firstpc->to.offset <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// reestablish context for inserting code
|
||||||
|
// at beginning of function.
|
||||||
|
pl = plast;
|
||||||
|
p1 = pl->firstpc;
|
||||||
|
p2 = p1->link;
|
||||||
|
pc = mal(sizeof(*pc));
|
||||||
|
clearp(pc);
|
||||||
|
p1->link = pc;
|
||||||
|
|
||||||
|
// zero stack frame
|
||||||
|
|
||||||
|
// MOVW $4(SP), R1
|
||||||
|
nodreg(&dst, types[tptr], 1);
|
||||||
|
p = gins(AMOVW, N, &dst);
|
||||||
|
p->from.type = D_CONST;
|
||||||
|
p->from.reg = REGSP;
|
||||||
|
p->from.offset = 4;
|
||||||
|
|
||||||
|
// MOVW $n(R1), R2
|
||||||
|
nodreg(&end, types[tptr], 2);
|
||||||
|
p = gins(AMOVW, N, &end);
|
||||||
|
p->from.type = D_CONST;
|
||||||
|
p->from.reg = 1;
|
||||||
|
p->from.offset = p1->to.offset;
|
||||||
|
|
||||||
|
// MOVW $0, R3
|
||||||
|
nodreg(&zero, types[TUINT32], 3);
|
||||||
|
nodconst(&con, types[TUINT32], 0);
|
||||||
|
gmove(&con, &zero);
|
||||||
|
|
||||||
|
// L:
|
||||||
|
// MOVW.P R3, 0(R1) +4
|
||||||
|
// CMP R1, R2
|
||||||
|
// BNE L
|
||||||
|
p = gins(AMOVW, &zero, &dst);
|
||||||
|
p->to.type = D_OREG;
|
||||||
|
p->to.offset = 4;
|
||||||
|
p->scond |= C_PBIT;
|
||||||
|
p3 = p;
|
||||||
|
p = gins(ACMP, &dst, N);
|
||||||
|
raddr(&end, p);
|
||||||
|
patch(gbranch(ABNE, T), p3);
|
||||||
|
|
||||||
|
// continue with original code.
|
||||||
|
gins(ANOP, N, N)->link = p2;
|
||||||
|
pc = P;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
gused(Node *n)
|
gused(Node *n)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -120,6 +120,44 @@ newplist(void)
|
|||||||
return pl;
|
return pl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
clearstk(void)
|
||||||
|
{
|
||||||
|
Plist *pl;
|
||||||
|
Prog *p1, *p2;
|
||||||
|
Node sp, di, cx, con, ax;
|
||||||
|
|
||||||
|
if((uint32)plast->firstpc->to.offset <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// reestablish context for inserting code
|
||||||
|
// at beginning of function.
|
||||||
|
pl = plast;
|
||||||
|
p1 = pl->firstpc;
|
||||||
|
p2 = p1->link;
|
||||||
|
pc = mal(sizeof(*pc));
|
||||||
|
clearp(pc);
|
||||||
|
p1->link = pc;
|
||||||
|
|
||||||
|
// zero stack frame
|
||||||
|
nodreg(&sp, types[tptr], D_SP);
|
||||||
|
nodreg(&di, types[tptr], D_DI);
|
||||||
|
nodreg(&cx, types[TUINT64], D_CX);
|
||||||
|
nodconst(&con, types[TUINT64], (uint32)p1->to.offset / widthptr);
|
||||||
|
gins(ACLD, N, N);
|
||||||
|
gins(AMOVQ, &sp, &di);
|
||||||
|
gins(AMOVQ, &con, &cx);
|
||||||
|
nodconst(&con, types[TUINT64], 0);
|
||||||
|
nodreg(&ax, types[TUINT64], D_AX);
|
||||||
|
gins(AMOVQ, &con, &ax);
|
||||||
|
gins(AREP, N, N);
|
||||||
|
gins(ASTOSQ, N, N);
|
||||||
|
|
||||||
|
// continue with original code.
|
||||||
|
gins(ANOP, N, N)->link = p2;
|
||||||
|
pc = P;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
gused(Node *n)
|
gused(Node *n)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -122,6 +122,44 @@ newplist(void)
|
|||||||
return pl;
|
return pl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
clearstk(void)
|
||||||
|
{
|
||||||
|
Plist *pl;
|
||||||
|
Prog *p1, *p2;
|
||||||
|
Node sp, di, cx, con, ax;
|
||||||
|
|
||||||
|
if(plast->firstpc->to.offset <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// reestablish context for inserting code
|
||||||
|
// at beginning of function.
|
||||||
|
pl = plast;
|
||||||
|
p1 = pl->firstpc;
|
||||||
|
p2 = p1->link;
|
||||||
|
pc = mal(sizeof(*pc));
|
||||||
|
clearp(pc);
|
||||||
|
p1->link = pc;
|
||||||
|
|
||||||
|
// zero stack frame
|
||||||
|
nodreg(&sp, types[tptr], D_SP);
|
||||||
|
nodreg(&di, types[tptr], D_DI);
|
||||||
|
nodreg(&cx, types[TUINT32], D_CX);
|
||||||
|
nodconst(&con, types[TUINT32], p1->to.offset / widthptr);
|
||||||
|
gins(ACLD, N, N);
|
||||||
|
gins(AMOVL, &sp, &di);
|
||||||
|
gins(AMOVL, &con, &cx);
|
||||||
|
nodconst(&con, types[TUINT32], 0);
|
||||||
|
nodreg(&ax, types[TUINT32], D_AX);
|
||||||
|
gins(AMOVL, &con, &ax);
|
||||||
|
gins(AREP, N, N);
|
||||||
|
gins(ASTOSL, N, N);
|
||||||
|
|
||||||
|
// continue with original code.
|
||||||
|
gins(ANOP, N, N)->link = p2;
|
||||||
|
pc = P;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
gused(Node *n)
|
gused(Node *n)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1241,9 +1241,14 @@ funccompile(Node *n, int isclosure)
|
|||||||
stksize = 0;
|
stksize = 0;
|
||||||
dclcontext = PAUTO;
|
dclcontext = PAUTO;
|
||||||
funcdepth = n->funcdepth + 1;
|
funcdepth = n->funcdepth + 1;
|
||||||
|
hasgoto = 0;
|
||||||
compile(n);
|
compile(n);
|
||||||
|
if(hasgoto)
|
||||||
|
clearstk();
|
||||||
curfn = nil;
|
curfn = nil;
|
||||||
funcdepth = 0;
|
funcdepth = 0;
|
||||||
dclcontext = PEXTERN;
|
dclcontext = PEXTERN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -209,6 +209,7 @@ gen(Node *n)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case OGOTO:
|
case OGOTO:
|
||||||
|
hasgoto = 1;
|
||||||
newlab(OGOTO, n, N);
|
newlab(OGOTO, n, N);
|
||||||
gjmp(P);
|
gjmp(P);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1255,3 +1255,6 @@ void zhist(Biobuf *b, int line, vlong offset);
|
|||||||
void zname(Biobuf *b, Sym *s, int t);
|
void zname(Biobuf *b, Sym *s, int t);
|
||||||
void data(void);
|
void data(void);
|
||||||
void text(void);
|
void text(void);
|
||||||
|
|
||||||
|
EXTERN int hasgoto;
|
||||||
|
void clearstk(void);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ GOFILES=\
|
|||||||
httpserver.go\
|
httpserver.go\
|
||||||
procattr.go\
|
procattr.go\
|
||||||
reflect.go\
|
reflect.go\
|
||||||
|
signal.go\
|
||||||
typecheck.go\
|
typecheck.go\
|
||||||
|
|
||||||
include ../../Make.cmd
|
include ../../Make.cmd
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"go/token"
|
"go/token"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type fix struct {
|
type fix struct {
|
||||||
@@ -258,13 +259,28 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) {
|
|||||||
|
|
||||||
// imports returns true if f imports path.
|
// imports returns true if f imports path.
|
||||||
func imports(f *ast.File, path string) bool {
|
func imports(f *ast.File, path string) bool {
|
||||||
|
return importSpec(f, path) != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// importSpec returns the import spec if f imports path,
|
||||||
|
// or nil otherwise.
|
||||||
|
func importSpec(f *ast.File, path string) *ast.ImportSpec {
|
||||||
for _, s := range f.Imports {
|
for _, s := range f.Imports {
|
||||||
|
if importPath(s) == path {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// importPath returns the unquoted import path of s,
|
||||||
|
// or "" if the path is not properly quoted.
|
||||||
|
func importPath(s *ast.ImportSpec) string {
|
||||||
t, err := strconv.Unquote(s.Path.Value)
|
t, err := strconv.Unquote(s.Path.Value)
|
||||||
if err == nil && t == path {
|
if err == nil {
|
||||||
return true
|
return t
|
||||||
}
|
}
|
||||||
}
|
return ""
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// isPkgDot returns true if t is the expression "pkg.name"
|
// isPkgDot returns true if t is the expression "pkg.name"
|
||||||
@@ -420,3 +436,138 @@ func newPkgDot(pos token.Pos, pkg, name string) ast.Expr {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addImport adds the import path to the file f, if absent.
|
||||||
|
func addImport(f *ast.File, path string) {
|
||||||
|
if imports(f, path) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newImport := &ast.ImportSpec{
|
||||||
|
Path: &ast.BasicLit{
|
||||||
|
Kind: token.STRING,
|
||||||
|
Value: strconv.Quote(path),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var impdecl *ast.GenDecl
|
||||||
|
|
||||||
|
// Find an import decl to add to.
|
||||||
|
for _, decl := range f.Decls {
|
||||||
|
gen, ok := decl.(*ast.GenDecl)
|
||||||
|
|
||||||
|
if ok && gen.Tok == token.IMPORT {
|
||||||
|
impdecl = gen
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No import decl found. Add one.
|
||||||
|
if impdecl == nil {
|
||||||
|
impdecl = &ast.GenDecl{
|
||||||
|
Tok: token.IMPORT,
|
||||||
|
}
|
||||||
|
f.Decls = append(f.Decls, nil)
|
||||||
|
copy(f.Decls[1:], f.Decls)
|
||||||
|
f.Decls[0] = impdecl
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the import decl has parentheses, if needed.
|
||||||
|
if len(impdecl.Specs) > 0 && !impdecl.Lparen.IsValid() {
|
||||||
|
impdecl.Lparen = impdecl.Pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assume the import paths are alphabetically ordered.
|
||||||
|
// If they are not, the result is ugly, but legal.
|
||||||
|
insertAt := len(impdecl.Specs) // default to end of specs
|
||||||
|
for i, spec := range impdecl.Specs {
|
||||||
|
impspec := spec.(*ast.ImportSpec)
|
||||||
|
if importPath(impspec) > path {
|
||||||
|
insertAt = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impdecl.Specs = append(impdecl.Specs, nil)
|
||||||
|
copy(impdecl.Specs[insertAt+1:], impdecl.Specs[insertAt:])
|
||||||
|
impdecl.Specs[insertAt] = newImport
|
||||||
|
|
||||||
|
f.Imports = append(f.Imports, newImport)
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteImport deletes the import path from the file f, if present.
|
||||||
|
func deleteImport(f *ast.File, path string) {
|
||||||
|
oldImport := importSpec(f, path)
|
||||||
|
|
||||||
|
// Find the import node that imports path, if any.
|
||||||
|
for i, decl := range f.Decls {
|
||||||
|
gen, ok := decl.(*ast.GenDecl)
|
||||||
|
if !ok || gen.Tok != token.IMPORT {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for j, spec := range gen.Specs {
|
||||||
|
impspec := spec.(*ast.ImportSpec)
|
||||||
|
|
||||||
|
if oldImport != impspec {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// We found an import spec that imports path.
|
||||||
|
// Delete it.
|
||||||
|
copy(gen.Specs[j:], gen.Specs[j+1:])
|
||||||
|
gen.Specs = gen.Specs[:len(gen.Specs)-1]
|
||||||
|
|
||||||
|
// If this was the last import spec in this decl,
|
||||||
|
// delete the decl, too.
|
||||||
|
if len(gen.Specs) == 0 {
|
||||||
|
copy(f.Decls[i:], f.Decls[i+1:])
|
||||||
|
f.Decls = f.Decls[:len(f.Decls)-1]
|
||||||
|
} else if len(gen.Specs) == 1 {
|
||||||
|
gen.Lparen = token.NoPos // drop parens
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete it from f.Imports.
|
||||||
|
for i, imp := range f.Imports {
|
||||||
|
if imp == oldImport {
|
||||||
|
copy(f.Imports[i:], f.Imports[i+1:])
|
||||||
|
f.Imports = f.Imports[:len(f.Imports)-1]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func usesImport(f *ast.File, path string) (used bool) {
|
||||||
|
spec := importSpec(f, path)
|
||||||
|
if spec == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
name := spec.Name.String()
|
||||||
|
switch name {
|
||||||
|
case "<nil>":
|
||||||
|
// If the package name is not explicitly specified,
|
||||||
|
// make an educated guess. This is not guaranteed to be correct.
|
||||||
|
lastSlash := strings.LastIndex(path, "/")
|
||||||
|
if lastSlash == -1 {
|
||||||
|
name = path
|
||||||
|
} else {
|
||||||
|
name = path[lastSlash+1:]
|
||||||
|
}
|
||||||
|
case "_", ".":
|
||||||
|
// Not sure if this import is used - err on the side of caution.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
walk(f, func(n interface{}) {
|
||||||
|
sel, ok := n.(*ast.SelectorExpr)
|
||||||
|
if ok && isTopName(sel.X, name) {
|
||||||
|
used = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
49
src/cmd/gofix/signal.go
Normal file
49
src/cmd/gofix/signal.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
register(fix{
|
||||||
|
"signal",
|
||||||
|
signal,
|
||||||
|
`Adapt code to types moved from os/signal to signal.
|
||||||
|
|
||||||
|
http://codereview.appspot.com/4437091
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func signal(f *ast.File) (fixed bool) {
|
||||||
|
if !imports(f, "os/signal") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
walk(f, func(n interface{}) {
|
||||||
|
s, ok := n.(*ast.SelectorExpr)
|
||||||
|
|
||||||
|
if !ok || !isTopName(s.X, "signal") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sel := s.Sel.String()
|
||||||
|
if sel == "Signal" || sel == "UnixSignal" || strings.HasPrefix(sel, "SIG") {
|
||||||
|
s.X = &ast.Ident{Name: "os"}
|
||||||
|
fixed = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if fixed {
|
||||||
|
addImport(f, "os")
|
||||||
|
if !usesImport(f, "os/signal") {
|
||||||
|
deleteImport(f, "os/signal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
96
src/cmd/gofix/signal_test.go
Normal file
96
src/cmd/gofix/signal_test.go
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
addTestCases(signalTests)
|
||||||
|
}
|
||||||
|
|
||||||
|
var signalTests = []testCase{
|
||||||
|
{
|
||||||
|
Name: "signal.0",
|
||||||
|
In: `package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "a"
|
||||||
|
"os/signal"
|
||||||
|
_ "z"
|
||||||
|
)
|
||||||
|
|
||||||
|
type T1 signal.UnixSignal
|
||||||
|
type T2 signal.Signal
|
||||||
|
|
||||||
|
func f() {
|
||||||
|
_ = signal.SIGHUP
|
||||||
|
_ = signal.Incoming
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
Out: `package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "a"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
_ "z"
|
||||||
|
)
|
||||||
|
|
||||||
|
type T1 os.UnixSignal
|
||||||
|
type T2 os.Signal
|
||||||
|
|
||||||
|
func f() {
|
||||||
|
_ = os.SIGHUP
|
||||||
|
_ = signal.Incoming
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "signal.1",
|
||||||
|
In: `package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func f() {
|
||||||
|
var _ os.Error
|
||||||
|
_ = signal.SIGHUP
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
Out: `package main
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
|
||||||
|
func f() {
|
||||||
|
var _ os.Error
|
||||||
|
_ = os.SIGHUP
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "signal.2",
|
||||||
|
In: `package main
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
import "os/signal"
|
||||||
|
|
||||||
|
func f() {
|
||||||
|
var _ os.Error
|
||||||
|
_ = signal.SIGHUP
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
Out: `package main
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
|
||||||
|
func f() {
|
||||||
|
var _ os.Error
|
||||||
|
_ = os.SIGHUP
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -1804,7 +1804,7 @@ mkvarname(char* name, int da)
|
|||||||
|
|
||||||
// flush previous compilation unit.
|
// flush previous compilation unit.
|
||||||
static void
|
static void
|
||||||
flushunit(DWDie *dwinfo, vlong pc, vlong unitstart)
|
flushunit(DWDie *dwinfo, vlong pc, vlong unitstart, int32 header_length)
|
||||||
{
|
{
|
||||||
vlong here;
|
vlong here;
|
||||||
|
|
||||||
@@ -1820,7 +1820,9 @@ flushunit(DWDie *dwinfo, vlong pc, vlong unitstart)
|
|||||||
|
|
||||||
here = cpos();
|
here = cpos();
|
||||||
seek(cout, unitstart, 0);
|
seek(cout, unitstart, 0);
|
||||||
LPUT(here - unitstart - sizeof(int32));
|
LPUT(here - unitstart - sizeof(int32)); // unit_length
|
||||||
|
WPUT(3); // dwarf version
|
||||||
|
LPUT(header_length); // header lenght starting here
|
||||||
cflush();
|
cflush();
|
||||||
seek(cout, here, 0);
|
seek(cout, here, 0);
|
||||||
}
|
}
|
||||||
@@ -1832,7 +1834,7 @@ writelines(void)
|
|||||||
Prog *q;
|
Prog *q;
|
||||||
Sym *s;
|
Sym *s;
|
||||||
Auto *a;
|
Auto *a;
|
||||||
vlong unitstart, offs;
|
vlong unitstart, headerend, offs;
|
||||||
vlong pc, epc, lc, llc, lline;
|
vlong pc, epc, lc, llc, lline;
|
||||||
int currfile;
|
int currfile;
|
||||||
int i, lang, da, dt;
|
int i, lang, da, dt;
|
||||||
@@ -1842,6 +1844,7 @@ writelines(void)
|
|||||||
char *n, *nn;
|
char *n, *nn;
|
||||||
|
|
||||||
unitstart = -1;
|
unitstart = -1;
|
||||||
|
headerend = -1;
|
||||||
pc = 0;
|
pc = 0;
|
||||||
epc = 0;
|
epc = 0;
|
||||||
lc = 1;
|
lc = 1;
|
||||||
@@ -1859,7 +1862,7 @@ writelines(void)
|
|||||||
// we're entering a new compilation unit
|
// we're entering a new compilation unit
|
||||||
|
|
||||||
if (inithist(s->autom)) {
|
if (inithist(s->autom)) {
|
||||||
flushunit(dwinfo, epc, unitstart);
|
flushunit(dwinfo, epc, unitstart, headerend - unitstart - 10);
|
||||||
unitstart = cpos();
|
unitstart = cpos();
|
||||||
|
|
||||||
if(debug['v'] > 1) {
|
if(debug['v'] > 1) {
|
||||||
@@ -1880,10 +1883,10 @@ writelines(void)
|
|||||||
|
|
||||||
// Write .debug_line Line Number Program Header (sec 6.2.4)
|
// Write .debug_line Line Number Program Header (sec 6.2.4)
|
||||||
// Fields marked with (*) must be changed for 64-bit dwarf
|
// Fields marked with (*) must be changed for 64-bit dwarf
|
||||||
LPUT(0); // unit_length (*), will be filled in later.
|
LPUT(0); // unit_length (*), will be filled in by flushunit.
|
||||||
WPUT(3); // dwarf version (appendix F)
|
WPUT(3); // dwarf version (appendix F)
|
||||||
LPUT(11); // header_length (*), starting here.
|
LPUT(0); // header_length (*), filled in by flushunit.
|
||||||
|
// cpos == unitstart + 4 + 2 + 4
|
||||||
cput(1); // minimum_instruction_length
|
cput(1); // minimum_instruction_length
|
||||||
cput(1); // default_is_stmt
|
cput(1); // default_is_stmt
|
||||||
cput(LINE_BASE); // line_base
|
cput(LINE_BASE); // line_base
|
||||||
@@ -1894,17 +1897,15 @@ writelines(void)
|
|||||||
cput(1); // standard_opcode_lengths[3]
|
cput(1); // standard_opcode_lengths[3]
|
||||||
cput(1); // standard_opcode_lengths[4]
|
cput(1); // standard_opcode_lengths[4]
|
||||||
cput(0); // include_directories (empty)
|
cput(0); // include_directories (empty)
|
||||||
cput(0); // file_names (empty) (emitted by DW_LNE's below)
|
|
||||||
// header_length ends here.
|
|
||||||
|
|
||||||
for (i=1; i < histfilesize; i++) {
|
for (i=1; i < histfilesize; i++) {
|
||||||
cput(0); // start extended opcode
|
|
||||||
uleb128put(1 + strlen(histfile[i]) + 4);
|
|
||||||
cput(DW_LNE_define_file);
|
|
||||||
strnput(histfile[i], strlen(histfile[i]) + 4);
|
strnput(histfile[i], strlen(histfile[i]) + 4);
|
||||||
// 4 zeros: the string termination + 3 fields.
|
// 4 zeros: the string termination + 3 fields.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cput(0); // terminate file_names.
|
||||||
|
headerend = cpos();
|
||||||
|
|
||||||
pc = s->text->pc;
|
pc = s->text->pc;
|
||||||
epc = pc;
|
epc = pc;
|
||||||
currfile = 1;
|
currfile = 1;
|
||||||
@@ -2009,7 +2010,7 @@ writelines(void)
|
|||||||
dwfunc->hash = nil;
|
dwfunc->hash = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
flushunit(dwinfo, epc, unitstart);
|
flushunit(dwinfo, epc, unitstart, headerend - unitstart - 10);
|
||||||
linesize = cpos() - lineo;
|
linesize = cpos() - lineo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -150,7 +150,8 @@ pprof [options] <profile>
|
|||||||
The /<service> can be $HEAP_PAGE, $PROFILE_PAGE, /pprof/pmuprofile,
|
The /<service> can be $HEAP_PAGE, $PROFILE_PAGE, /pprof/pmuprofile,
|
||||||
$GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall,
|
$GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall,
|
||||||
or /pprof/filteredprofile.
|
or /pprof/filteredprofile.
|
||||||
For instance: "pprof http://myserver.com:80$HEAP_PAGE".
|
For instance:
|
||||||
|
pprof http://myserver.com:80$HEAP_PAGE
|
||||||
If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profiling).
|
If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profiling).
|
||||||
pprof --symbols <program>
|
pprof --symbols <program>
|
||||||
Maps addresses to symbol names. In this mode, stdin should be a
|
Maps addresses to symbol names. In this mode, stdin should be a
|
||||||
@@ -532,7 +533,7 @@ sub Init() {
|
|||||||
ConfigureObjTools($main::prog)
|
ConfigureObjTools($main::prog)
|
||||||
}
|
}
|
||||||
|
|
||||||
# Break the opt_list_prefix into the prefix_list array
|
# Break the opt_lib_prefix into the prefix_list array
|
||||||
@prefix_list = split (',', $main::opt_lib_prefix);
|
@prefix_list = split (',', $main::opt_lib_prefix);
|
||||||
|
|
||||||
# Remove trailing / from the prefixes, in the list to prevent
|
# Remove trailing / from the prefixes, in the list to prevent
|
||||||
@@ -626,7 +627,7 @@ sub Main() {
|
|||||||
if ($main::opt_disasm) {
|
if ($main::opt_disasm) {
|
||||||
PrintDisassembly($libs, $flat, $cumulative, $main::opt_disasm, $total);
|
PrintDisassembly($libs, $flat, $cumulative, $main::opt_disasm, $total);
|
||||||
} elsif ($main::opt_list) {
|
} elsif ($main::opt_list) {
|
||||||
PrintListing($libs, $flat, $cumulative, $main::opt_list);
|
PrintListing($total, $libs, $flat, $cumulative, $main::opt_list, 0);
|
||||||
} elsif ($main::opt_text) {
|
} elsif ($main::opt_text) {
|
||||||
# Make sure the output is empty when have nothing to report
|
# Make sure the output is empty when have nothing to report
|
||||||
# (only matters when --heapcheck is given but we must be
|
# (only matters when --heapcheck is given but we must be
|
||||||
@@ -814,7 +815,7 @@ sub InteractiveCommand {
|
|||||||
my $ignore;
|
my $ignore;
|
||||||
($routine, $ignore) = ParseInteractiveArgs($3);
|
($routine, $ignore) = ParseInteractiveArgs($3);
|
||||||
|
|
||||||
my $profile = ProcessProfile($orig_profile, $symbols, "", $ignore);
|
my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
|
||||||
my $reduced = ReduceProfile($symbols, $profile);
|
my $reduced = ReduceProfile($symbols, $profile);
|
||||||
|
|
||||||
# Get derived profiles
|
# Get derived profiles
|
||||||
@@ -841,21 +842,22 @@ sub InteractiveCommand {
|
|||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (m/^\s*list\s*(.+)/) {
|
if (m/^\s*(web)?list\s*(.+)/) {
|
||||||
|
my $html = (defined($1) && ($1 eq "web"));
|
||||||
$main::opt_list = 1;
|
$main::opt_list = 1;
|
||||||
|
|
||||||
my $routine;
|
my $routine;
|
||||||
my $ignore;
|
my $ignore;
|
||||||
($routine, $ignore) = ParseInteractiveArgs($1);
|
($routine, $ignore) = ParseInteractiveArgs($2);
|
||||||
|
|
||||||
my $profile = ProcessProfile($orig_profile, $symbols, "", $ignore);
|
my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
|
||||||
my $reduced = ReduceProfile($symbols, $profile);
|
my $reduced = ReduceProfile($symbols, $profile);
|
||||||
|
|
||||||
# Get derived profiles
|
# Get derived profiles
|
||||||
my $flat = FlatProfile($reduced);
|
my $flat = FlatProfile($reduced);
|
||||||
my $cumulative = CumulativeProfile($reduced);
|
my $cumulative = CumulativeProfile($reduced);
|
||||||
|
|
||||||
PrintListing($libs, $flat, $cumulative, $routine);
|
PrintListing($total, $libs, $flat, $cumulative, $routine, $html);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (m/^\s*disasm\s*(.+)/) {
|
if (m/^\s*disasm\s*(.+)/) {
|
||||||
@@ -866,7 +868,7 @@ sub InteractiveCommand {
|
|||||||
($routine, $ignore) = ParseInteractiveArgs($1);
|
($routine, $ignore) = ParseInteractiveArgs($1);
|
||||||
|
|
||||||
# Process current profile to account for various settings
|
# Process current profile to account for various settings
|
||||||
my $profile = ProcessProfile($orig_profile, $symbols, "", $ignore);
|
my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
|
||||||
my $reduced = ReduceProfile($symbols, $profile);
|
my $reduced = ReduceProfile($symbols, $profile);
|
||||||
|
|
||||||
# Get derived profiles
|
# Get derived profiles
|
||||||
@@ -890,7 +892,7 @@ sub InteractiveCommand {
|
|||||||
($focus, $ignore) = ParseInteractiveArgs($2);
|
($focus, $ignore) = ParseInteractiveArgs($2);
|
||||||
|
|
||||||
# Process current profile to account for various settings
|
# Process current profile to account for various settings
|
||||||
my $profile = ProcessProfile($orig_profile, $symbols, $focus, $ignore);
|
my $profile = ProcessProfile($total, $orig_profile, $symbols, $focus, $ignore);
|
||||||
my $reduced = ReduceProfile($symbols, $profile);
|
my $reduced = ReduceProfile($symbols, $profile);
|
||||||
|
|
||||||
# Get derived profiles
|
# Get derived profiles
|
||||||
@@ -916,6 +918,7 @@ sub InteractiveCommand {
|
|||||||
|
|
||||||
|
|
||||||
sub ProcessProfile {
|
sub ProcessProfile {
|
||||||
|
my $total_count = shift;
|
||||||
my $orig_profile = shift;
|
my $orig_profile = shift;
|
||||||
my $symbols = shift;
|
my $symbols = shift;
|
||||||
my $focus = shift;
|
my $focus = shift;
|
||||||
@@ -923,7 +926,6 @@ sub ProcessProfile {
|
|||||||
|
|
||||||
# Process current profile to account for various settings
|
# Process current profile to account for various settings
|
||||||
my $profile = $orig_profile;
|
my $profile = $orig_profile;
|
||||||
my $total_count = TotalProfile($profile);
|
|
||||||
printf("Total: %s %s\n", Unparse($total_count), Units());
|
printf("Total: %s %s\n", Unparse($total_count), Units());
|
||||||
if ($focus ne '') {
|
if ($focus ne '') {
|
||||||
$profile = FocusProfile($symbols, $profile, $focus);
|
$profile = FocusProfile($symbols, $profile, $focus);
|
||||||
@@ -970,6 +972,11 @@ Commands:
|
|||||||
list [routine_regexp] [-ignore1] [-ignore2]
|
list [routine_regexp] [-ignore1] [-ignore2]
|
||||||
Show source listing of routines whose names match "routine_regexp"
|
Show source listing of routines whose names match "routine_regexp"
|
||||||
|
|
||||||
|
weblist [routine_regexp] [-ignore1] [-ignore2]
|
||||||
|
Displays a source listing of routines whose names match "routine_regexp"
|
||||||
|
in a web browser. You can click on source lines to view the
|
||||||
|
corresponding disassembly.
|
||||||
|
|
||||||
top [--cum] [-ignore1] [-ignore2]
|
top [--cum] [-ignore1] [-ignore2]
|
||||||
top20 [--cum] [-ignore1] [-ignore2]
|
top20 [--cum] [-ignore1] [-ignore2]
|
||||||
top37 [--cum] [-ignore1] [-ignore2]
|
top37 [--cum] [-ignore1] [-ignore2]
|
||||||
@@ -1144,7 +1151,7 @@ sub PrintText {
|
|||||||
$sym);
|
$sym);
|
||||||
}
|
}
|
||||||
$lines++;
|
$lines++;
|
||||||
last if ($line_limit >= 0 && $lines > $line_limit);
|
last if ($line_limit >= 0 && $lines >= $line_limit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1291,11 +1298,32 @@ sub ByName {
|
|||||||
|
|
||||||
# Print source-listing for all all routines that match $main::opt_list
|
# Print source-listing for all all routines that match $main::opt_list
|
||||||
sub PrintListing {
|
sub PrintListing {
|
||||||
|
my $total = shift;
|
||||||
my $libs = shift;
|
my $libs = shift;
|
||||||
my $flat = shift;
|
my $flat = shift;
|
||||||
my $cumulative = shift;
|
my $cumulative = shift;
|
||||||
my $list_opts = shift;
|
my $list_opts = shift;
|
||||||
|
my $html = shift;
|
||||||
|
|
||||||
|
my $output = \*STDOUT;
|
||||||
|
my $fname = "";
|
||||||
|
|
||||||
|
|
||||||
|
if ($html) {
|
||||||
|
# Arrange to write the output to a temporary file
|
||||||
|
$fname = TempName($main::next_tmpfile, "html");
|
||||||
|
$main::next_tmpfile++;
|
||||||
|
if (!open(TEMP, ">$fname")) {
|
||||||
|
print STDERR "$fname: $!\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$output = \*TEMP;
|
||||||
|
print $output HtmlListingHeader();
|
||||||
|
printf $output ("<div class=\"legend\">%s<br>Total: %s %s</div>\n",
|
||||||
|
$main::prog, Unparse($total), Units());
|
||||||
|
}
|
||||||
|
|
||||||
|
my $listed = 0;
|
||||||
foreach my $lib (@{$libs}) {
|
foreach my $lib (@{$libs}) {
|
||||||
my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts);
|
my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts);
|
||||||
my $offset = AddressSub($lib->[1], $lib->[3]);
|
my $offset = AddressSub($lib->[1], $lib->[3]);
|
||||||
@@ -1307,15 +1335,98 @@ sub PrintListing {
|
|||||||
my $addr = AddressAdd($start_addr, $offset);
|
my $addr = AddressAdd($start_addr, $offset);
|
||||||
for (my $i = 0; $i < $length; $i++) {
|
for (my $i = 0; $i < $length; $i++) {
|
||||||
if (defined($cumulative->{$addr})) {
|
if (defined($cumulative->{$addr})) {
|
||||||
PrintSource($lib->[0], $offset,
|
$listed += PrintSource(
|
||||||
|
$lib->[0], $offset,
|
||||||
$routine, $flat, $cumulative,
|
$routine, $flat, $cumulative,
|
||||||
$start_addr, $end_addr);
|
$start_addr, $end_addr,
|
||||||
|
$html,
|
||||||
|
$output);
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
$addr = AddressInc($addr);
|
$addr = AddressInc($addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($html) {
|
||||||
|
if ($listed > 0) {
|
||||||
|
print $output HtmlListingFooter();
|
||||||
|
close($output);
|
||||||
|
RunWeb($fname);
|
||||||
|
} else {
|
||||||
|
close($output);
|
||||||
|
unlink($fname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub HtmlListingHeader {
|
||||||
|
return <<'EOF';
|
||||||
|
<DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Pprof listing</title>
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.legend {
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
.line {
|
||||||
|
color: #aaaaaa;
|
||||||
|
}
|
||||||
|
.livesrc {
|
||||||
|
color: #0000ff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.livesrc:hover {
|
||||||
|
background-color: #cccccc;
|
||||||
|
}
|
||||||
|
.asm {
|
||||||
|
color: #888888;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script type="text/javascript">
|
||||||
|
function pprof_toggle_asm(e) {
|
||||||
|
var target;
|
||||||
|
if (!e) e = window.event;
|
||||||
|
if (e.target) target = e.target;
|
||||||
|
else if (e.srcElement) target = e.srcElement;
|
||||||
|
|
||||||
|
if (target && target.className == "livesrc") {
|
||||||
|
var asm = target.nextSibling;
|
||||||
|
if (asm && asm.className == "asm") {
|
||||||
|
asm.style.display = (asm.style.display == "block" ? "none" : "block");
|
||||||
|
e.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
sub HtmlListingFooter {
|
||||||
|
return <<'EOF';
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
sub HtmlEscape {
|
||||||
|
my $text = shift;
|
||||||
|
$text =~ s/&/&/g;
|
||||||
|
$text =~ s/</</g;
|
||||||
|
$text =~ s/>/>/g;
|
||||||
|
return $text;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Returns the indentation of the line, if it has any non-whitespace
|
# Returns the indentation of the line, if it has any non-whitespace
|
||||||
@@ -1338,6 +1449,8 @@ sub PrintSource {
|
|||||||
my $cumulative = shift;
|
my $cumulative = shift;
|
||||||
my $start_addr = shift;
|
my $start_addr = shift;
|
||||||
my $end_addr = shift;
|
my $end_addr = shift;
|
||||||
|
my $html = shift;
|
||||||
|
my $output = shift;
|
||||||
|
|
||||||
# Disassemble all instructions (just to get line numbers)
|
# Disassemble all instructions (just to get line numbers)
|
||||||
my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
|
my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
|
||||||
@@ -1353,7 +1466,7 @@ sub PrintSource {
|
|||||||
}
|
}
|
||||||
if (!defined($filename)) {
|
if (!defined($filename)) {
|
||||||
print STDERR "no filename found in $routine\n";
|
print STDERR "no filename found in $routine\n";
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Hack 2: assume that the largest line number from $filename is the
|
# Hack 2: assume that the largest line number from $filename is the
|
||||||
@@ -1386,7 +1499,7 @@ sub PrintSource {
|
|||||||
{
|
{
|
||||||
if (!open(FILE, "<$filename")) {
|
if (!open(FILE, "<$filename")) {
|
||||||
print STDERR "$filename: $!\n";
|
print STDERR "$filename: $!\n";
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
my $l = 0;
|
my $l = 0;
|
||||||
my $first_indentation = -1;
|
my $first_indentation = -1;
|
||||||
@@ -1414,12 +1527,21 @@ sub PrintSource {
|
|||||||
# Assign all samples to the range $firstline,$lastline,
|
# Assign all samples to the range $firstline,$lastline,
|
||||||
# Hack 4: If an instruction does not occur in the range, its samples
|
# Hack 4: If an instruction does not occur in the range, its samples
|
||||||
# are moved to the next instruction that occurs in the range.
|
# are moved to the next instruction that occurs in the range.
|
||||||
my $samples1 = {};
|
my $samples1 = {}; # Map from line number to flat count
|
||||||
my $samples2 = {};
|
my $samples2 = {}; # Map from line number to cumulative count
|
||||||
my $running1 = 0; # Unassigned flat counts
|
my $running1 = 0; # Unassigned flat counts
|
||||||
my $running2 = 0; # Unassigned cumulative counts
|
my $running2 = 0; # Unassigned cumulative counts
|
||||||
my $total1 = 0; # Total flat counts
|
my $total1 = 0; # Total flat counts
|
||||||
my $total2 = 0; # Total cumulative counts
|
my $total2 = 0; # Total cumulative counts
|
||||||
|
my %disasm = (); # Map from line number to disassembly
|
||||||
|
my $running_disasm = ""; # Unassigned disassembly
|
||||||
|
my $skip_marker = "---\n";
|
||||||
|
if ($html) {
|
||||||
|
$skip_marker = "";
|
||||||
|
for (my $l = $firstline; $l <= $lastline; $l++) {
|
||||||
|
$disasm{$l} = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
foreach my $e (@instructions) {
|
foreach my $e (@instructions) {
|
||||||
# Add up counts for all address that fall inside this instruction
|
# Add up counts for all address that fall inside this instruction
|
||||||
my $c1 = 0;
|
my $c1 = 0;
|
||||||
@@ -1428,6 +1550,15 @@ sub PrintSource {
|
|||||||
$c1 += GetEntry($flat, $a);
|
$c1 += GetEntry($flat, $a);
|
||||||
$c2 += GetEntry($cumulative, $a);
|
$c2 += GetEntry($cumulative, $a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($html) {
|
||||||
|
$running_disasm .= sprintf(" %6s %6s \t\t%8s: %s\n",
|
||||||
|
HtmlPrintNumber($c1),
|
||||||
|
HtmlPrintNumber($c2),
|
||||||
|
$e->[0],
|
||||||
|
CleanDisassembly($e->[3]));
|
||||||
|
}
|
||||||
|
|
||||||
$running1 += $c1;
|
$running1 += $c1;
|
||||||
$running2 += $c2;
|
$running2 += $c2;
|
||||||
$total1 += $c1;
|
$total1 += $c1;
|
||||||
@@ -1442,6 +1573,10 @@ sub PrintSource {
|
|||||||
AddEntry($samples2, $line, $running2);
|
AddEntry($samples2, $line, $running2);
|
||||||
$running1 = 0;
|
$running1 = 0;
|
||||||
$running2 = 0;
|
$running2 = 0;
|
||||||
|
if ($html) {
|
||||||
|
$disasm{$line} .= $running_disasm;
|
||||||
|
$running_disasm = '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1449,16 +1584,28 @@ sub PrintSource {
|
|||||||
AddEntry($samples1, $lastline, $running1);
|
AddEntry($samples1, $lastline, $running1);
|
||||||
AddEntry($samples2, $lastline, $running2);
|
AddEntry($samples2, $lastline, $running2);
|
||||||
|
|
||||||
printf("ROUTINE ====================== %s in %s\n" .
|
if ($html) {
|
||||||
|
printf $output (
|
||||||
|
"<h1>%s</h1>%s\n<pre onClick=\"pprof_toggle_asm()\">\n" .
|
||||||
|
"Total:%6s %6s (flat / cumulative %s)\n",
|
||||||
|
HtmlEscape(ShortFunctionName($routine)),
|
||||||
|
HtmlEscape($filename),
|
||||||
|
Unparse($total1),
|
||||||
|
Unparse($total2),
|
||||||
|
Units());
|
||||||
|
} else {
|
||||||
|
printf $output (
|
||||||
|
"ROUTINE ====================== %s in %s\n" .
|
||||||
"%6s %6s Total %s (flat / cumulative)\n",
|
"%6s %6s Total %s (flat / cumulative)\n",
|
||||||
ShortFunctionName($routine),
|
ShortFunctionName($routine),
|
||||||
$filename,
|
$filename,
|
||||||
Units(),
|
|
||||||
Unparse($total1),
|
Unparse($total1),
|
||||||
Unparse($total2));
|
Unparse($total2),
|
||||||
|
Units());
|
||||||
|
}
|
||||||
if (!open(FILE, "<$filename")) {
|
if (!open(FILE, "<$filename")) {
|
||||||
print STDERR "$filename: $!\n";
|
print STDERR "$filename: $!\n";
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
my $l = 0;
|
my $l = 0;
|
||||||
while (<FILE>) {
|
while (<FILE>) {
|
||||||
@@ -1468,16 +1615,47 @@ sub PrintSource {
|
|||||||
(($l <= $oldlastline + 5) || ($l <= $lastline))) {
|
(($l <= $oldlastline + 5) || ($l <= $lastline))) {
|
||||||
chop;
|
chop;
|
||||||
my $text = $_;
|
my $text = $_;
|
||||||
if ($l == $firstline) { printf("---\n"); }
|
if ($l == $firstline) { print $output $skip_marker; }
|
||||||
printf("%6s %6s %4d: %s\n",
|
my $n1 = GetEntry($samples1, $l);
|
||||||
UnparseAlt(GetEntry($samples1, $l)),
|
my $n2 = GetEntry($samples2, $l);
|
||||||
UnparseAlt(GetEntry($samples2, $l)),
|
if ($html) {
|
||||||
|
my $dis = $disasm{$l};
|
||||||
|
if (!defined($dis) || $n1 + $n2 == 0) {
|
||||||
|
# No samples/disassembly for this source line
|
||||||
|
printf $output (
|
||||||
|
"<span class=\"line\">%5d</span> " .
|
||||||
|
"<span class=\"deadsrc\">%6s %6s %s</span>\n",
|
||||||
|
$l,
|
||||||
|
HtmlPrintNumber($n1),
|
||||||
|
HtmlPrintNumber($n2),
|
||||||
|
HtmlEscape($text));
|
||||||
|
} else {
|
||||||
|
printf $output (
|
||||||
|
"<span class=\"line\">%5d</span> " .
|
||||||
|
"<span class=\"livesrc\">%6s %6s %s</span>" .
|
||||||
|
"<span class=\"asm\">%s</span>\n",
|
||||||
|
$l,
|
||||||
|
HtmlPrintNumber($n1),
|
||||||
|
HtmlPrintNumber($n2),
|
||||||
|
HtmlEscape($text),
|
||||||
|
HtmlEscape($dis));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf $output(
|
||||||
|
"%6s %6s %4d: %s\n",
|
||||||
|
UnparseAlt($n1),
|
||||||
|
UnparseAlt($n2),
|
||||||
$l,
|
$l,
|
||||||
$text);
|
$text);
|
||||||
if ($l == $lastline) { printf("---\n"); }
|
}
|
||||||
|
if ($l == $lastline) { print $output $skip_marker; }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
close(FILE);
|
close(FILE);
|
||||||
|
if ($html) {
|
||||||
|
print $output "</pre>\n";
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Return the source line for the specified file/linenumber.
|
# Return the source line for the specified file/linenumber.
|
||||||
@@ -1625,16 +1803,11 @@ sub PrintDisassembledFunction {
|
|||||||
$address =~ s/^0x//;
|
$address =~ s/^0x//;
|
||||||
$address =~ s/^0*//;
|
$address =~ s/^0*//;
|
||||||
|
|
||||||
# Trim symbols
|
|
||||||
my $d = $e->[3];
|
|
||||||
while ($d =~ s/\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax)
|
|
||||||
while ($d =~ s/(\w+)<[^<>]*>/$1/g) { } # Remove template arguments
|
|
||||||
|
|
||||||
printf("%6s %6s %8s: %6s\n",
|
printf("%6s %6s %8s: %6s\n",
|
||||||
UnparseAlt($flat_count[$x]),
|
UnparseAlt($flat_count[$x]),
|
||||||
UnparseAlt($cum_count[$x]),
|
UnparseAlt($cum_count[$x]),
|
||||||
$address,
|
$address,
|
||||||
$d);
|
CleanDisassembly($e->[3]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2254,6 +2427,16 @@ sub UnparseAlt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Alternate pretty-printed form: 0 maps to ""
|
||||||
|
sub HtmlPrintNumber {
|
||||||
|
my $num = shift;
|
||||||
|
if ($num == 0) {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
return Unparse($num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Return output units
|
# Return output units
|
||||||
sub Units {
|
sub Units {
|
||||||
if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
|
if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
|
||||||
@@ -2415,6 +2598,8 @@ sub RemoveUninterestingFrames {
|
|||||||
'copyin',
|
'copyin',
|
||||||
'gostring',
|
'gostring',
|
||||||
'gostringsize',
|
'gostringsize',
|
||||||
|
'growslice1',
|
||||||
|
'appendslice1',
|
||||||
'hash_init',
|
'hash_init',
|
||||||
'hash_subtable_new',
|
'hash_subtable_new',
|
||||||
'hash_conv',
|
'hash_conv',
|
||||||
@@ -2422,6 +2607,8 @@ sub RemoveUninterestingFrames {
|
|||||||
'hash_insert_internal',
|
'hash_insert_internal',
|
||||||
'hash_insert',
|
'hash_insert',
|
||||||
'mapassign',
|
'mapassign',
|
||||||
|
'runtime.mapassign',
|
||||||
|
'runtime.appendslice',
|
||||||
'runtime.mapassign1',
|
'runtime.mapassign1',
|
||||||
'makechan',
|
'makechan',
|
||||||
'makemap',
|
'makemap',
|
||||||
@@ -2433,11 +2620,13 @@ sub RemoveUninterestingFrames {
|
|||||||
'unsafe.New',
|
'unsafe.New',
|
||||||
'runtime.mallocgc',
|
'runtime.mallocgc',
|
||||||
'runtime.catstring',
|
'runtime.catstring',
|
||||||
|
'runtime.growslice',
|
||||||
'runtime.ifaceT2E',
|
'runtime.ifaceT2E',
|
||||||
'runtime.ifaceT2I',
|
'runtime.ifaceT2I',
|
||||||
'runtime.makechan',
|
'runtime.makechan',
|
||||||
'runtime.makechan_c',
|
'runtime.makechan_c',
|
||||||
'runtime.makemap',
|
'runtime.makemap',
|
||||||
|
'runtime.makemap_c',
|
||||||
'runtime.makeslice',
|
'runtime.makeslice',
|
||||||
'runtime.mal',
|
'runtime.mal',
|
||||||
'runtime.slicebytetostring',
|
'runtime.slicebytetostring',
|
||||||
@@ -4302,6 +4491,14 @@ sub ShortFunctionName {
|
|||||||
return $function;
|
return $function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Trim overly long symbols found in disassembler output
|
||||||
|
sub CleanDisassembly {
|
||||||
|
my $d = shift;
|
||||||
|
while ($d =~ s/\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax)
|
||||||
|
while ($d =~ s/(\w+)<[^<>]*>/$1/g) { } # Remove template arguments
|
||||||
|
return $d;
|
||||||
|
}
|
||||||
|
|
||||||
##### Miscellaneous #####
|
##### Miscellaneous #####
|
||||||
|
|
||||||
# Find the right versions of the above object tools to use. The
|
# Find the right versions of the above object tools to use. The
|
||||||
|
|||||||
22
test/fixedbugs/bug344.go
Normal file
22
test/fixedbugs/bug344.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// $G $D/$F.go && $L $F.$A && ./$A.out || echo BUG: bug344
|
||||||
|
|
||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// invalid use of goto.
|
||||||
|
// do whatever you like, just don't crash.
|
||||||
|
i := 42
|
||||||
|
a := []*int{&i, &i, &i, &i}
|
||||||
|
x := a[0]
|
||||||
|
goto start
|
||||||
|
for _, x = range a {
|
||||||
|
start:
|
||||||
|
fmt.Sprint(*x)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user