mirror of
https://github.com/golang/go.git
synced 2026-01-29 07:02:05 +03:00
Compare commits
42 Commits
cf0c42c2ca
...
go1.1.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d6d8fca24 | ||
|
|
f1f37cce1d | ||
|
|
018854d24a | ||
|
|
2041d55aac | ||
|
|
3e917131a1 | ||
|
|
89020aa72c | ||
|
|
b5245b9c77 | ||
|
|
ceeda72bab | ||
|
|
fc9a18f16b | ||
|
|
f2fa995324 | ||
|
|
9db29c2784 | ||
|
|
b82120bd3e | ||
|
|
b315b7ffff | ||
|
|
e33810b291 | ||
|
|
6baa8dba68 | ||
|
|
d260448f6b | ||
|
|
a00b3d29de | ||
|
|
fa6b1232e6 | ||
|
|
3b7e6e8db1 | ||
|
|
13af44f8a5 | ||
|
|
b87a963f87 | ||
|
|
3e88a825df | ||
|
|
d72c550f1c | ||
|
|
371a3ab28a | ||
|
|
ff7cb872a4 | ||
|
|
b9b37d1292 | ||
|
|
03290b55e9 | ||
|
|
5f1cf34402 | ||
|
|
09879160e5 | ||
|
|
ea3b4c9321 | ||
|
|
41644eec2c | ||
|
|
205f850cea | ||
|
|
6c7631126f | ||
|
|
9294a08f1b | ||
|
|
46a6097aa7 | ||
|
|
91504a0e0d | ||
|
|
836b670612 | ||
|
|
0a98e78c1f | ||
|
|
c785d6af66 | ||
|
|
99aa2da7ea | ||
|
|
0f1a18b773 | ||
|
|
1c5438aae8 |
1
.hgtags
1
.hgtags
@@ -114,4 +114,3 @@ dc5e410f0b4c32ab11dc992593a2bcf5f607381b weekly
|
||||
2ccfd4b451d319941bfe3e08037e1462d3c15093 go1.0.1
|
||||
5e806355a9e1491aaab53d3612fed4c550b130c0 go1.0.2
|
||||
2d8bc3c94ecb3ec8f70556d5fd237788903c7281 go1.0.3
|
||||
2d8bc3c94ecb3ec8f70556d5fd237788903c7281 release
|
||||
|
||||
@@ -13,6 +13,32 @@ hg pull
|
||||
hg update <i>tag</i>
|
||||
</pre>
|
||||
|
||||
<h2 id="go1.1">go1.1 (released 2013/05/13)</h2>
|
||||
|
||||
<p>
|
||||
Go 1.1 is a major release of Go.
|
||||
Read the <a href="/doc/go1.1.html">Go 1.1 Release Notes</a> for
|
||||
more information.
|
||||
</p>
|
||||
|
||||
<h3 id="go1.1.minor">Minor revisions</h3>
|
||||
|
||||
<p>
|
||||
go1.1.1 (released 2013/06/13) includes several compiler and runtime bug fixes.
|
||||
See the <a href="https://code.google.com/p/go/source/list?name=release-branch.go1.1&r=43c4a41d24382a56a90e924800c681e435d9e399">change history</a> for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.1.2 (released 2013/08/13) includes fixes to the <code>gc</code> compiler
|
||||
and <code>cgo</code>, and the <code>bufio</code>, <code>runtime</code>,
|
||||
<code>syscall</code>, and <code>time</code> packages.
|
||||
See the <a href="https://code.google.com/p/go/source/list?name=release-branch.go1.1&r=a6a9792f94acd4ff686b2bc57383d163608b91cf">change history</a> for details.
|
||||
If you use package syscall's <code>Getrlimit</code> and <code>Setrlimit</code>
|
||||
functions under Linux on the ARM or 386 architectures, please note change
|
||||
<a href="http://golang.org/change/55ac276af5a7">55ac276af5a7</a>
|
||||
that fixes <a href="http://golang.org/issue/5949">issue 5949</a>.
|
||||
</p>
|
||||
|
||||
<h2 id="go1">go1 (released 2012/03/28)</h2>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -403,8 +403,10 @@ if owner != user {
|
||||
|
||||
<p>
|
||||
By convention, one-method interfaces are named by
|
||||
the method name plus the -er suffix: <code>Reader</code>,
|
||||
<code>Writer</code>, <code>Formatter</code> etc.
|
||||
the method name plus an -er suffix or similar modification
|
||||
to construct an agent noun: <code>Reader</code>,
|
||||
<code>Writer</code>, <code>Formatter</code>,
|
||||
<code>CloseNotifier</code> etc.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -159,7 +159,7 @@ The GCC release schedule does not coincide with the Go release schedule, so some
|
||||
<code>gccgo</code>'s releases.
|
||||
The 4.8.0 version of GCC shipped in March, 2013 and includes a nearly-Go 1.1 version of <code>gccgo</code>.
|
||||
Its library is a little behind the release, but the biggest difference is that method values are not implemented.
|
||||
Sometime around May 2013, we expect 4.8.1 of GCC to ship with a <code>gccgo</code>
|
||||
Sometime around July 2013, we expect 4.8.2 of GCC to ship with a <code>gccgo</code>
|
||||
providing a complete Go 1.1 implementaiton.
|
||||
</p>
|
||||
|
||||
@@ -694,6 +694,36 @@ The old package <code>exp/norm</code> has also been moved, but to a new reposito
|
||||
be developed.
|
||||
</p>
|
||||
|
||||
<h3 id="new_packages">New packages</h3>
|
||||
|
||||
<p>
|
||||
There are three new packages.
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
The <a href="/pkg/go/format/"><code>go/format</code></a> package provides
|
||||
a convenient way for a program to access the formatting capabilities of the
|
||||
<a href="/cmd/go/#hdr-Run_gofmt_on_package_sources"><code>go fmt</code></a> command.
|
||||
It has two functions,
|
||||
<a href="/pkg/go/format/#Node"><code>Node</code></a> to format a Go parser
|
||||
<a href="/pkg/go/ast/#Node"><code>Node</code></a>,
|
||||
and
|
||||
<a href="/pkg/go/format/#Source"><code>Source</code></a>
|
||||
to reformat arbitrary Go source code into the standard format as provided by the
|
||||
<a href="/cmd/go/#hdr-Run_gofmt_on_package_sources"><code>go fmt</code></a> command.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
The <a href="/pkg/net/http/cookiejar/"><code>net/http/cookiejar</code></a> package provides the basics for managing HTTP cookies.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
The <a href="/pkg/runtime/race/"><code>runtime/race</code></a> package provides low-level facilities for data race detection.
|
||||
It is internal to the race detector and does not otherwise export any user-visible functionality.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="minor_library_changes">Minor changes to the library</h3>
|
||||
|
||||
<p>
|
||||
@@ -796,17 +826,6 @@ information that the <a href="/cmd/godoc/"><code>godoc</code></a>
|
||||
command can filter or present according to the value of the <code>-notes</code> flag.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
A new package, <a href="/pkg/go/format/"><code>go/format</code></a>, provides
|
||||
a convenient way for a program to access the formatting capabilities of <code>gofmt</code>.
|
||||
It has two functions,
|
||||
<a href="/pkg/go/format/#Node"><code>Node</code></a> to format a Go parser
|
||||
<a href="/pkg/go/ast/#Node"><code>Node</code></a>,
|
||||
and
|
||||
<a href="/pkg/go/format/#Source"><code>Source</code></a>
|
||||
to format arbitrary Go source code.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
The undocumented and only partially implemented "noescape" feature of the
|
||||
<a href="/pkg/html/template/"><code>html/template</code></a>
|
||||
@@ -943,10 +962,6 @@ a <a href="/pkg/net/http/#Response"><code>Response.Body</code></a> is closed bef
|
||||
being fully consumed.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
The new <a href="/pkg/net/http/cookiejar/"><code>net/http/cookiejar</code></a> package provides the basics for managing HTTP cookies.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
The <a href="/pkg/net/mail/"><code>net/mail</code></a> package has two new functions,
|
||||
<a href="/pkg/net/mail/#ParseAddress"><code>ParseAddress</code></a> and
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!--{
|
||||
"Title": "The Go Programming Language Specification",
|
||||
"Subtitle": "Version of April 10, 2013",
|
||||
"Subtitle": "Version of May 8, 2013",
|
||||
"Path": "/ref/spec"
|
||||
}-->
|
||||
|
||||
@@ -3835,7 +3835,7 @@ const Θ float64 = 3/2 // Θ == 1.0 (type float64, 3/2 is integer divisio
|
||||
const Π float64 = 3/2. // Π == 1.5 (type float64, 3/2. is float division)
|
||||
const d = 1 << 3.0 // d == 8 (untyped integer constant)
|
||||
const e = 1.0 << 3 // e == 8 (untyped integer constant)
|
||||
const f = int32(1) << 33 // f == 0 (type int32)
|
||||
const f = int32(1) << 33 // illegal (constant 8589934592 overflows int32)
|
||||
const g = float64(2) >> 1 // illegal (float64(2) is a typed floating-point constant)
|
||||
const h = "foo" > "bar" // h == true (untyped boolean constant)
|
||||
const j = true // j == true (untyped boolean constant)
|
||||
|
||||
@@ -108,7 +108,7 @@ For example:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
tar -C /usr/local -xzf go1.0.3.linux-amd64.tar.gz
|
||||
tar -C /usr/local -xzf go1.1.linux-amd64.tar.gz
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -84,41 +84,38 @@ Linux, Mac OS X, Windows, and more.
|
||||
|
||||
<div style="clear: both;"></div>
|
||||
|
||||
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
|
||||
<script type="text/javascript">
|
||||
google.load("feeds", "1");
|
||||
|
||||
function readableTime(t) {
|
||||
var m = ["January", "February", "March", "April", "May", "June", "July",
|
||||
"August", "September", "October", "November", "December"];
|
||||
var p = t.substring(0, t.indexOf("T")).split("-");
|
||||
var d = new Date(p[0], p[1]-1, p[2]);
|
||||
return d.getDate() + " " + m[d.getMonth()] + " " + d.getFullYear();
|
||||
}
|
||||
|
||||
function feedLoaded(result) {
|
||||
if (result.error) {
|
||||
console.log(result.error);
|
||||
return;
|
||||
}
|
||||
var blog = document.getElementById("blog");
|
||||
var read = blog.getElementsByClassName("read")[0];
|
||||
for (var i = 0; i < result.feed.entries.length && i < 2; i++) {
|
||||
var entry = result.feed.entries[i];
|
||||
for (var i = 0; i < result.length && i < 2; i++) {
|
||||
var entry = result[i];
|
||||
var title = document.createElement("a");
|
||||
title.className = "title";
|
||||
title.href = entry.link;
|
||||
title.innerHTML = entry.title;
|
||||
title.href = entry.Link;
|
||||
title.innerHTML = entry.Title;
|
||||
blog.insertBefore(title, read);
|
||||
var extract = document.createElement("div");
|
||||
extract.className = "extract";
|
||||
extract.innerHTML = entry.contentSnippet;
|
||||
extract.innerHTML = entry.Summary;
|
||||
blog.insertBefore(extract, read);
|
||||
var when = document.createElement("div");
|
||||
when.className = "when";
|
||||
var pub = entry.publishedDate.split(" ").slice(1,3).join(" ");
|
||||
when.innerHTML = "Published " + pub
|
||||
when.innerHTML = "Published " + readableTime(entry.Time);
|
||||
blog.insertBefore(when, read);
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
// Load blog feed.
|
||||
var feed = new google.feeds.Feed("http://blog.golang.org/feeds/posts/default");
|
||||
feed.load(feedLoaded);
|
||||
|
||||
$(function() {
|
||||
// Set up playground.
|
||||
playground({
|
||||
"codeEl": "#learn .code",
|
||||
@@ -128,7 +125,11 @@ function init() {
|
||||
"shareRedirect": "http://play.golang.org/p/",
|
||||
"toysEl": "#learn .toys select"
|
||||
});
|
||||
}
|
||||
|
||||
google.setOnLoadCallback(init);
|
||||
// Load blog feed.
|
||||
$('<script/>').attr('text', 'text/javascript')
|
||||
.attr('src', 'http://blog.golang.org/.json?jsonp=feedLoaded')
|
||||
.appendTo('body');
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
@@ -142,6 +142,10 @@ func testBlocking(t *testing.T) {
|
||||
// Test that the stack can be unwound through a call out and call back
|
||||
// into Go.
|
||||
func testCallbackCallers(t *testing.T) {
|
||||
if runtime.Compiler != "gc" {
|
||||
// The exact function names are not going to be the same.
|
||||
t.Skip("skipping for non-gc toolchain")
|
||||
}
|
||||
pc := make([]uintptr, 100)
|
||||
n := 0
|
||||
name := []string{
|
||||
|
||||
@@ -152,6 +152,7 @@ peep(void)
|
||||
case ALEAQ:
|
||||
if(regtyp(&p->to))
|
||||
if(p->from.sym != S)
|
||||
if(p->from.index == D_NONE || p->from.index == D_CONST)
|
||||
conprop(r);
|
||||
break;
|
||||
|
||||
|
||||
@@ -78,9 +78,9 @@ clearfat(Node *nl)
|
||||
c = w % 4; // bytes
|
||||
q = w / 4; // quads
|
||||
|
||||
gconreg(AMOVL, 0, D_AX);
|
||||
nodreg(&n1, types[tptr], D_DI);
|
||||
agen(nl, &n1);
|
||||
gconreg(AMOVL, 0, D_AX);
|
||||
|
||||
if(q >= 4) {
|
||||
gconreg(AMOVL, q, D_CX);
|
||||
|
||||
@@ -145,6 +145,7 @@ peep(void)
|
||||
case ALEAL:
|
||||
if(regtyp(&p->to))
|
||||
if(p->from.sym != S)
|
||||
if(p->from.index == D_NONE || p->from.index == D_CONST)
|
||||
conprop(r);
|
||||
break;
|
||||
|
||||
|
||||
@@ -809,6 +809,15 @@ func (p *Package) gccDefines(stdin []byte) string {
|
||||
func (p *Package) gccErrors(stdin []byte) string {
|
||||
// TODO(rsc): require failure
|
||||
args := p.gccCmd()
|
||||
|
||||
// GCC 4.8.0 has a bug: it sometimes does not apply
|
||||
// -Wunused-value to values that are macros defined in system
|
||||
// headers. See issue 5118. Adding -Wsystem-headers avoids
|
||||
// that problem. This will produce additional errors, but it
|
||||
// doesn't matter because we will ignore all errors that are
|
||||
// not marked for the cgo-test file.
|
||||
args = append(args, "-Wsystem-headers")
|
||||
|
||||
if *debugGcc {
|
||||
fmt.Fprintf(os.Stderr, "$ %s <<EOF\n", strings.Join(args, " "))
|
||||
os.Stderr.Write(stdin)
|
||||
|
||||
@@ -318,6 +318,9 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
|
||||
Type: gtype,
|
||||
}
|
||||
|
||||
// Builtins defined in the C prolog.
|
||||
inProlog := name == "CString" || name == "GoString" || name == "GoStringN" || name == "GoBytes"
|
||||
|
||||
if *gccgo {
|
||||
// Gccgo style hooks.
|
||||
fmt.Fprint(fgo2, "\n")
|
||||
@@ -331,8 +334,10 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
|
||||
|
||||
conf.Fprint(fgo2, fset, d)
|
||||
fmt.Fprint(fgo2, " {\n")
|
||||
fmt.Fprint(fgo2, "\tdefer syscall.CgocallDone()\n")
|
||||
fmt.Fprint(fgo2, "\tsyscall.Cgocall()\n")
|
||||
if !inProlog {
|
||||
fmt.Fprint(fgo2, "\tdefer syscall.CgocallDone()\n")
|
||||
fmt.Fprint(fgo2, "\tsyscall.Cgocall()\n")
|
||||
}
|
||||
if n.AddError {
|
||||
fmt.Fprint(fgo2, "\tsyscall.SetErrno(0)\n")
|
||||
}
|
||||
@@ -363,7 +368,11 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
|
||||
fmt.Fprint(fgo2, "}\n")
|
||||
|
||||
// declare the C function.
|
||||
fmt.Fprintf(fgo2, "//extern %s\n", n.C)
|
||||
if inProlog {
|
||||
fmt.Fprintf(fgo2, "//extern %s\n", n.C)
|
||||
} else {
|
||||
fmt.Fprintf(fgo2, "//extern _cgo%s%s\n", cPrefix, n.Mangle)
|
||||
}
|
||||
d.Name = ast.NewIdent(cname)
|
||||
if n.AddError {
|
||||
l := d.Type.Results.List
|
||||
@@ -377,8 +386,7 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) {
|
||||
conf.Fprint(fgo2, fset, d)
|
||||
fmt.Fprint(fgo2, "\n")
|
||||
|
||||
if name == "CString" || name == "GoString" || name == "GoStringN" || name == "GoBytes" {
|
||||
// The builtins are already defined in the C prolog.
|
||||
if inProlog {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -466,7 +474,7 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
|
||||
p.Written[name] = true
|
||||
|
||||
if *gccgo {
|
||||
// we don't use wrappers with gccgo.
|
||||
p.writeGccgoOutputFunc(fgcc, n)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -518,6 +526,54 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
|
||||
fmt.Fprintf(fgcc, "\n")
|
||||
}
|
||||
|
||||
// Write out a wrapper for a function when using gccgo. This is a
|
||||
// simple wrapper that just calls the real function. We only need a
|
||||
// wrapper to support static functions in the prologue--without a
|
||||
// wrapper, we can't refer to the function, since the reference is in
|
||||
// a different file.
|
||||
func (p *Package) writeGccgoOutputFunc(fgcc *os.File, n *Name) {
|
||||
if t := n.FuncType.Result; t != nil {
|
||||
fmt.Fprintf(fgcc, "%s\n", t.C.String())
|
||||
} else {
|
||||
fmt.Fprintf(fgcc, "void\n")
|
||||
}
|
||||
fmt.Fprintf(fgcc, "_cgo%s%s(", cPrefix, n.Mangle)
|
||||
for i, t := range n.FuncType.Params {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(fgcc, ", ")
|
||||
}
|
||||
c := t.Typedef
|
||||
if c == "" {
|
||||
c = t.C.String()
|
||||
}
|
||||
fmt.Fprintf(fgcc, "%s p%d", c, i)
|
||||
}
|
||||
fmt.Fprintf(fgcc, ")\n")
|
||||
fmt.Fprintf(fgcc, "{\n")
|
||||
fmt.Fprintf(fgcc, "\t")
|
||||
if t := n.FuncType.Result; t != nil {
|
||||
fmt.Fprintf(fgcc, "return ")
|
||||
// Cast to void* to avoid warnings due to omitted qualifiers.
|
||||
if c := t.C.String(); c[len(c)-1] == '*' {
|
||||
fmt.Fprintf(fgcc, "(void*)")
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(fgcc, "%s(", n.C)
|
||||
for i, t := range n.FuncType.Params {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(fgcc, ", ")
|
||||
}
|
||||
// Cast to void* to avoid warnings due to omitted qualifiers.
|
||||
if c := t.C.String(); c[len(c)-1] == '*' {
|
||||
fmt.Fprintf(fgcc, "(void*)")
|
||||
}
|
||||
fmt.Fprintf(fgcc, "p%d", i)
|
||||
}
|
||||
fmt.Fprintf(fgcc, ");\n")
|
||||
fmt.Fprintf(fgcc, "}\n")
|
||||
fmt.Fprintf(fgcc, "\n")
|
||||
}
|
||||
|
||||
// Write out the various stubs we need to support functions exported
|
||||
// from Go so that they are callable from C.
|
||||
func (p *Package) writeExports(fgo2, fc, fm *os.File) {
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# Copyright 2012 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.
|
||||
|
||||
include ../../Make.dist
|
||||
@@ -1,36 +0,0 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
/*
|
||||
|
||||
Cov is a rudimentary code coverage tool.
|
||||
|
||||
Usage:
|
||||
go tool cov [-lsv] [-g substring] [-m minlines] [6.out args]
|
||||
|
||||
Given a command to run, it runs the command while tracking which
|
||||
sections of code have been executed. When the command finishes,
|
||||
cov prints the line numbers of sections of code in the binary that
|
||||
were not executed. With no arguments it assumes the command "6.out".
|
||||
|
||||
|
||||
The options are:
|
||||
|
||||
-l
|
||||
print full path names instead of paths relative to the current directory
|
||||
-s
|
||||
show the source code that didn't execute, in addition to the line numbers.
|
||||
-v
|
||||
print debugging information during the run.
|
||||
-g substring
|
||||
restrict the coverage analysis to functions or files whose names contain substring
|
||||
-m minlines
|
||||
only report uncovered sections of code larger than minlines lines
|
||||
|
||||
The program is the same for all architectures: 386, amd64, and arm.
|
||||
|
||||
*/
|
||||
package main
|
||||
@@ -1,484 +0,0 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
/*
|
||||
* code coverage
|
||||
*/
|
||||
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bio.h>
|
||||
#include "tree.h"
|
||||
|
||||
#include <ureg_amd64.h>
|
||||
#include <mach.h>
|
||||
typedef struct Ureg Ureg;
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: cov [-lsv] [-g substring] [-m minlines] [6.out args...]\n");
|
||||
fprint(2, "-g specifies pattern of interesting functions or files\n");
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
typedef struct Range Range;
|
||||
struct Range
|
||||
{
|
||||
uvlong pc;
|
||||
uvlong epc;
|
||||
};
|
||||
|
||||
int chatty;
|
||||
int fd;
|
||||
int longnames;
|
||||
int pid;
|
||||
int doshowsrc;
|
||||
Map *mem;
|
||||
Map *text;
|
||||
Fhdr fhdr;
|
||||
char *substring;
|
||||
char cwd[1000];
|
||||
int ncwd;
|
||||
int minlines = -1000;
|
||||
|
||||
Tree breakpoints; // code ranges not run
|
||||
|
||||
/*
|
||||
* comparison for Range structures
|
||||
* they are "equal" if they overlap, so
|
||||
* that a search for [pc, pc+1) finds the
|
||||
* Range containing pc.
|
||||
*/
|
||||
int
|
||||
rangecmp(void *va, void *vb)
|
||||
{
|
||||
Range *a = va, *b = vb;
|
||||
if(a->epc <= b->pc)
|
||||
return 1;
|
||||
if(b->epc <= a->pc)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* remember that we ran the section of code [pc, epc).
|
||||
*/
|
||||
void
|
||||
ran(uvlong pc, uvlong epc)
|
||||
{
|
||||
Range key;
|
||||
Range *r;
|
||||
uvlong oldepc;
|
||||
|
||||
if(chatty)
|
||||
print("run %#llux-%#llux\n", pc, epc);
|
||||
|
||||
key.pc = pc;
|
||||
key.epc = pc+1;
|
||||
r = treeget(&breakpoints, &key);
|
||||
if(r == nil)
|
||||
sysfatal("unchecked breakpoint at %#llux+%d", pc, (int)(epc-pc));
|
||||
|
||||
// Might be that the tail of the sequence
|
||||
// was run already, so r->epc is before the end.
|
||||
// Adjust len.
|
||||
if(epc > r->epc)
|
||||
epc = r->epc;
|
||||
|
||||
if(r->pc == pc) {
|
||||
r->pc = epc;
|
||||
} else {
|
||||
// Chop r to before pc;
|
||||
// add new entry for after if needed.
|
||||
// Changing r->epc does not affect r's position in the tree.
|
||||
oldepc = r->epc;
|
||||
r->epc = pc;
|
||||
if(epc < oldepc) {
|
||||
Range *n;
|
||||
n = malloc(sizeof *n);
|
||||
if(n == nil)
|
||||
sysfatal("out of memory");
|
||||
n->pc = epc;
|
||||
n->epc = oldepc;
|
||||
treeput(&breakpoints, n, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
showsrc(char *file, int line1, int line2)
|
||||
{
|
||||
Biobuf *b;
|
||||
char *p;
|
||||
int n, stop;
|
||||
|
||||
if((b = Bopen(file, OREAD)) == nil) {
|
||||
print("\topen %s: %r\n", file);
|
||||
return;
|
||||
}
|
||||
|
||||
for(n=1; n<line1 && (p = Brdstr(b, '\n', 1)) != nil; n++)
|
||||
free(p);
|
||||
|
||||
// print up to five lines (this one and 4 more).
|
||||
// if there are more than five lines, print 4 and "..."
|
||||
stop = n+4;
|
||||
if(stop > line2)
|
||||
stop = line2;
|
||||
if(stop < line2)
|
||||
stop--;
|
||||
for(; n<=stop && (p = Brdstr(b, '\n', 1)) != nil; n++) {
|
||||
print(" %d %s\n", n, p);
|
||||
free(p);
|
||||
}
|
||||
if(n < line2)
|
||||
print(" ...\n");
|
||||
Bterm(b);
|
||||
}
|
||||
|
||||
/*
|
||||
* if s is in the current directory or below,
|
||||
* return the relative path.
|
||||
*/
|
||||
char*
|
||||
shortname(char *s)
|
||||
{
|
||||
if(!longnames && strlen(s) > ncwd && memcmp(s, cwd, ncwd) == 0 && s[ncwd] == '/')
|
||||
return s+ncwd+1;
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
* we've decided that [pc, epc) did not run.
|
||||
* do something about it.
|
||||
*/
|
||||
void
|
||||
missing(uvlong pc, uvlong epc)
|
||||
{
|
||||
char file[1000];
|
||||
int line1, line2;
|
||||
char buf[100];
|
||||
Symbol s;
|
||||
char *p;
|
||||
uvlong uv;
|
||||
|
||||
if(!findsym(pc, CTEXT, &s) || !fileline(file, sizeof file, pc)) {
|
||||
notfound:
|
||||
print("%#llux-%#llux\n", pc, epc);
|
||||
return;
|
||||
}
|
||||
p = strrchr(file, ':');
|
||||
*p++ = 0;
|
||||
line1 = atoi(p);
|
||||
for(uv=pc; uv<epc; ) {
|
||||
if(!fileline(file, sizeof file, epc-2))
|
||||
goto notfound;
|
||||
uv += machdata->instsize(text, uv);
|
||||
}
|
||||
p = strrchr(file, ':');
|
||||
*p++ = 0;
|
||||
line2 = atoi(p);
|
||||
|
||||
if(line2+1-line2 < minlines)
|
||||
return;
|
||||
|
||||
if(pc == s.value) {
|
||||
// never entered function
|
||||
print("%s:%d %s never called (%#llux-%#llux)\n", shortname(file), line1, s.name, pc, epc);
|
||||
return;
|
||||
}
|
||||
if(pc <= s.value+13) {
|
||||
// probably stub for stack growth.
|
||||
// check whether last instruction is call to morestack.
|
||||
// the -5 below is the length of
|
||||
// CALL sys.morestack.
|
||||
buf[0] = 0;
|
||||
machdata->das(text, epc-5, 0, buf, sizeof buf);
|
||||
if(strstr(buf, "morestack"))
|
||||
return;
|
||||
}
|
||||
|
||||
if(epc - pc == 5) {
|
||||
// check for CALL sys.panicindex
|
||||
buf[0] = 0;
|
||||
machdata->das(text, pc, 0, buf, sizeof buf);
|
||||
if(strstr(buf, "panicindex"))
|
||||
return;
|
||||
}
|
||||
|
||||
if(epc - pc == 2 || epc -pc == 3) {
|
||||
// check for XORL inside shift.
|
||||
// (on x86 have to implement large left or unsigned right shift with explicit zeroing).
|
||||
// f+90 0x00002c9f CMPL CX,$20
|
||||
// f+93 0x00002ca2 JCS f+97(SB)
|
||||
// f+95 0x00002ca4 XORL AX,AX <<<
|
||||
// f+97 0x00002ca6 SHLL CL,AX
|
||||
// f+99 0x00002ca8 MOVL $1,CX
|
||||
//
|
||||
// f+c8 0x00002cd7 CMPL CX,$40
|
||||
// f+cb 0x00002cda JCS f+d0(SB)
|
||||
// f+cd 0x00002cdc XORQ AX,AX <<<
|
||||
// f+d0 0x00002cdf SHLQ CL,AX
|
||||
// f+d3 0x00002ce2 MOVQ $1,CX
|
||||
buf[0] = 0;
|
||||
machdata->das(text, pc, 0, buf, sizeof buf);
|
||||
if(strncmp(buf, "XOR", 3) == 0) {
|
||||
machdata->das(text, epc, 0, buf, sizeof buf);
|
||||
if(strncmp(buf, "SHL", 3) == 0 || strncmp(buf, "SHR", 3) == 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(epc - pc == 3) {
|
||||
// check for SAR inside shift.
|
||||
// (on x86 have to implement large signed right shift as >>31).
|
||||
// f+36 0x00016216 CMPL CX,$20
|
||||
// f+39 0x00016219 JCS f+3e(SB)
|
||||
// f+3b 0x0001621b SARL $1f,AX <<<
|
||||
// f+3e 0x0001621e SARL CL,AX
|
||||
// f+40 0x00016220 XORL CX,CX
|
||||
// f+42 0x00016222 CMPL CX,AX
|
||||
buf[0] = 0;
|
||||
machdata->das(text, pc, 0, buf, sizeof buf);
|
||||
if(strncmp(buf, "SAR", 3) == 0) {
|
||||
machdata->das(text, epc, 0, buf, sizeof buf);
|
||||
if(strncmp(buf, "SAR", 3) == 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// show first instruction to make clear where we were.
|
||||
machdata->das(text, pc, 0, buf, sizeof buf);
|
||||
|
||||
if(line1 != line2)
|
||||
print("%s:%d,%d %#llux-%#llux %s\n",
|
||||
shortname(file), line1, line2, pc, epc, buf);
|
||||
else
|
||||
print("%s:%d %#llux-%#llux %s\n",
|
||||
shortname(file), line1, pc, epc, buf);
|
||||
if(doshowsrc)
|
||||
showsrc(file, line1, line2);
|
||||
}
|
||||
|
||||
/*
|
||||
* walk the tree, calling missing for each non-empty
|
||||
* section of missing code.
|
||||
*/
|
||||
void
|
||||
walktree(TreeNode *t)
|
||||
{
|
||||
Range *n;
|
||||
|
||||
if(t == nil)
|
||||
return;
|
||||
walktree(t->left);
|
||||
n = t->key;
|
||||
if(n->pc < n->epc)
|
||||
missing(n->pc, n->epc);
|
||||
walktree(t->right);
|
||||
}
|
||||
|
||||
/*
|
||||
* set a breakpoint all over [pc, epc)
|
||||
* and remember that we did.
|
||||
*/
|
||||
void
|
||||
breakpoint(uvlong pc, uvlong epc)
|
||||
{
|
||||
Range *r;
|
||||
|
||||
r = malloc(sizeof *r);
|
||||
if(r == nil)
|
||||
sysfatal("out of memory");
|
||||
r->pc = pc;
|
||||
r->epc = epc;
|
||||
treeput(&breakpoints, r, r);
|
||||
|
||||
for(; pc < epc; pc+=machdata->bpsize)
|
||||
put1(mem, pc, machdata->bpinst, machdata->bpsize);
|
||||
}
|
||||
|
||||
/*
|
||||
* install breakpoints over all text symbols
|
||||
* that match the pattern.
|
||||
*/
|
||||
void
|
||||
cover(void)
|
||||
{
|
||||
Symbol s;
|
||||
char *lastfn;
|
||||
uvlong lastpc;
|
||||
int i;
|
||||
char buf[200];
|
||||
|
||||
lastfn = nil;
|
||||
lastpc = 0;
|
||||
for(i=0; textsym(&s, i); i++) {
|
||||
switch(s.type) {
|
||||
case 'T':
|
||||
case 't':
|
||||
if(lastpc != 0) {
|
||||
breakpoint(lastpc, s.value);
|
||||
lastpc = 0;
|
||||
}
|
||||
// Ignore second entry for a given name;
|
||||
// that's the debugging blob.
|
||||
if(lastfn && strcmp(s.name, lastfn) == 0)
|
||||
break;
|
||||
lastfn = s.name;
|
||||
buf[0] = 0;
|
||||
fileline(buf, sizeof buf, s.value);
|
||||
if(substring == nil || strstr(buf, substring) || strstr(s.name, substring))
|
||||
lastpc = s.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uvlong
|
||||
rgetzero(Map *map, char *reg)
|
||||
{
|
||||
USED(map);
|
||||
USED(reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* remove the breakpoints at pc and successive instructions,
|
||||
* up to and including the first jump or other control flow transfer.
|
||||
*/
|
||||
void
|
||||
uncover(uvlong pc)
|
||||
{
|
||||
uchar buf[1000];
|
||||
int n, n1, n2;
|
||||
uvlong foll[2];
|
||||
|
||||
// Double-check that we stopped at a breakpoint.
|
||||
if(get1(mem, pc, buf, machdata->bpsize) < 0)
|
||||
sysfatal("read mem inst at %#llux: %r", pc);
|
||||
if(memcmp(buf, machdata->bpinst, machdata->bpsize) != 0)
|
||||
sysfatal("stopped at %#llux; not at breakpoint %d", pc, machdata->bpsize);
|
||||
|
||||
// Figure out how many bytes of straight-line code
|
||||
// there are in the text starting at pc.
|
||||
n = 0;
|
||||
while(n < sizeof buf) {
|
||||
n1 = machdata->instsize(text, pc+n);
|
||||
if(n+n1 > sizeof buf)
|
||||
break;
|
||||
n2 = machdata->foll(text, pc+n, rgetzero, foll);
|
||||
n += n1;
|
||||
if(n2 != 1 || foll[0] != pc+n)
|
||||
break;
|
||||
}
|
||||
|
||||
// Record that this section of code ran.
|
||||
ran(pc, pc+n);
|
||||
|
||||
// Put original instructions back.
|
||||
if(get1(text, pc, buf, n) < 0)
|
||||
sysfatal("get1: %r");
|
||||
if(put1(mem, pc, buf, n) < 0)
|
||||
sysfatal("put1: %r");
|
||||
}
|
||||
|
||||
int
|
||||
startprocess(char **argv)
|
||||
{
|
||||
int pid;
|
||||
|
||||
if((pid = fork()) < 0)
|
||||
sysfatal("fork: %r");
|
||||
if(pid == 0) {
|
||||
pid = getpid();
|
||||
if(ctlproc(pid, "hang") < 0)
|
||||
sysfatal("ctlproc hang: %r");
|
||||
exec(argv[0], argv);
|
||||
sysfatal("exec %s: %r", argv[0]);
|
||||
}
|
||||
if(ctlproc(pid, "attached") < 0 || ctlproc(pid, "waitstop") < 0)
|
||||
sysfatal("attach %d %s: %r", pid, argv[0]);
|
||||
return pid;
|
||||
}
|
||||
|
||||
int
|
||||
go(void)
|
||||
{
|
||||
uvlong pc;
|
||||
char buf[100];
|
||||
int n;
|
||||
|
||||
for(n = 0;; n++) {
|
||||
ctlproc(pid, "startstop");
|
||||
if(get8(mem, offsetof(Ureg, ip), &pc) < 0) {
|
||||
rerrstr(buf, sizeof buf);
|
||||
if(strstr(buf, "exited") || strstr(buf, "No such process"))
|
||||
return n;
|
||||
sysfatal("cannot read pc: %r");
|
||||
}
|
||||
pc--;
|
||||
if(put8(mem, offsetof(Ureg, ip), pc) < 0)
|
||||
sysfatal("cannot write pc: %r");
|
||||
uncover(pc);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int n;
|
||||
|
||||
ARGBEGIN{
|
||||
case 'g':
|
||||
substring = EARGF(usage());
|
||||
break;
|
||||
case 'l':
|
||||
longnames++;
|
||||
break;
|
||||
case 'n':
|
||||
minlines = atoi(EARGF(usage()));
|
||||
break;
|
||||
case 's':
|
||||
doshowsrc = 1;
|
||||
break;
|
||||
case 'v':
|
||||
chatty++;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}ARGEND
|
||||
|
||||
getwd(cwd, sizeof cwd);
|
||||
ncwd = strlen(cwd);
|
||||
|
||||
if(argc == 0) {
|
||||
*--argv = "6.out";
|
||||
}
|
||||
fd = open(argv[0], OREAD);
|
||||
if(fd < 0)
|
||||
sysfatal("open %s: %r", argv[0]);
|
||||
if(crackhdr(fd, &fhdr) <= 0)
|
||||
sysfatal("crackhdr: %r");
|
||||
machbytype(fhdr.type);
|
||||
if(syminit(fd, &fhdr) <= 0)
|
||||
sysfatal("syminit: %r");
|
||||
text = loadmap(nil, fd, &fhdr);
|
||||
if(text == nil)
|
||||
sysfatal("loadmap: %r");
|
||||
pid = startprocess(argv);
|
||||
mem = attachproc(pid, &fhdr);
|
||||
if(mem == nil)
|
||||
sysfatal("attachproc: %r");
|
||||
breakpoints.cmp = rangecmp;
|
||||
cover();
|
||||
n = go();
|
||||
walktree(breakpoints.root);
|
||||
if(chatty)
|
||||
print("%d breakpoints\n", n);
|
||||
detachproc(mem);
|
||||
exits(0);
|
||||
}
|
||||
|
||||
@@ -1,245 +0,0 @@
|
||||
// Renamed from Map to Tree to avoid conflict with libmach.
|
||||
|
||||
/*
|
||||
Copyright (c) 2003-2007 Russ Cox, Tom Bergan, Austin Clements,
|
||||
Massachusetts Institute of Technology
|
||||
Portions Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// Mutable map structure, but still based on
|
||||
// Okasaki, Red Black Trees in a Functional Setting, JFP 1999,
|
||||
// which is a lot easier than the traditional red-black
|
||||
// and plenty fast enough for me. (Also I could copy
|
||||
// and edit fmap.c.)
|
||||
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include "tree.h"
|
||||
|
||||
enum
|
||||
{
|
||||
Red = 0,
|
||||
Black = 1
|
||||
};
|
||||
|
||||
|
||||
// Red-black trees are binary trees with this property:
|
||||
// 1. No red node has a red parent.
|
||||
// 2. Every path from the root to a leaf contains the
|
||||
// same number of black nodes.
|
||||
|
||||
static TreeNode*
|
||||
rwTreeNode(TreeNode *p, int color, TreeNode *left, void *key, void *value, TreeNode *right)
|
||||
{
|
||||
if(p == nil)
|
||||
p = malloc(sizeof *p);
|
||||
if(p == nil)
|
||||
sysfatal("out of memory");
|
||||
p->color = color;
|
||||
p->left = left;
|
||||
p->key = key;
|
||||
p->value = value;
|
||||
p->right = right;
|
||||
return p;
|
||||
}
|
||||
|
||||
static TreeNode*
|
||||
balance(TreeNode *m0)
|
||||
{
|
||||
void *xk, *xv, *yk, *yv, *zk, *zv;
|
||||
TreeNode *a, *b, *c, *d;
|
||||
TreeNode *m1, *m2;
|
||||
int color;
|
||||
TreeNode *left, *right;
|
||||
void *key, *value;
|
||||
|
||||
color = m0->color;
|
||||
left = m0->left;
|
||||
key = m0->key;
|
||||
value = m0->value;
|
||||
right = m0->right;
|
||||
|
||||
// Okasaki notation: (T is mkTreeNode, B is Black, R is Red, x, y, z are key-value.
|
||||
//
|
||||
// balance B (T R (T R a x b) y c) z d
|
||||
// balance B (T R a x (T R b y c)) z d
|
||||
// balance B a x (T R (T R b y c) z d)
|
||||
// balance B a x (T R b y (T R c z d))
|
||||
//
|
||||
// = T R (T B a x b) y (T B c z d)
|
||||
|
||||
if(color == Black){
|
||||
if(left && left->color == Red){
|
||||
if(left->left && left->left->color == Red){
|
||||
a = left->left->left;
|
||||
xk = left->left->key;
|
||||
xv = left->left->value;
|
||||
b = left->left->right;
|
||||
yk = left->key;
|
||||
yv = left->value;
|
||||
c = left->right;
|
||||
zk = key;
|
||||
zv = value;
|
||||
d = right;
|
||||
m1 = left;
|
||||
m2 = left->left;
|
||||
goto hard;
|
||||
}else if(left->right && left->right->color == Red){
|
||||
a = left->left;
|
||||
xk = left->key;
|
||||
xv = left->value;
|
||||
b = left->right->left;
|
||||
yk = left->right->key;
|
||||
yv = left->right->value;
|
||||
c = left->right->right;
|
||||
zk = key;
|
||||
zv = value;
|
||||
d = right;
|
||||
m1 = left;
|
||||
m2 = left->right;
|
||||
goto hard;
|
||||
}
|
||||
}else if(right && right->color == Red){
|
||||
if(right->left && right->left->color == Red){
|
||||
a = left;
|
||||
xk = key;
|
||||
xv = value;
|
||||
b = right->left->left;
|
||||
yk = right->left->key;
|
||||
yv = right->left->value;
|
||||
c = right->left->right;
|
||||
zk = right->key;
|
||||
zv = right->value;
|
||||
d = right->right;
|
||||
m1 = right;
|
||||
m2 = right->left;
|
||||
goto hard;
|
||||
}else if(right->right && right->right->color == Red){
|
||||
a = left;
|
||||
xk = key;
|
||||
xv = value;
|
||||
b = right->left;
|
||||
yk = right->key;
|
||||
yv = right->value;
|
||||
c = right->right->left;
|
||||
zk = right->right->key;
|
||||
zv = right->right->value;
|
||||
d = right->right->right;
|
||||
m1 = right;
|
||||
m2 = right->right;
|
||||
goto hard;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rwTreeNode(m0, color, left, key, value, right);
|
||||
|
||||
hard:
|
||||
return rwTreeNode(m0, Red, rwTreeNode(m1, Black, a, xk, xv, b),
|
||||
yk, yv, rwTreeNode(m2, Black, c, zk, zv, d));
|
||||
}
|
||||
|
||||
static TreeNode*
|
||||
ins0(TreeNode *p, void *k, void *v, TreeNode *rw)
|
||||
{
|
||||
if(p == nil)
|
||||
return rwTreeNode(rw, Red, nil, k, v, nil);
|
||||
if(p->key == k){
|
||||
if(rw)
|
||||
return rwTreeNode(rw, p->color, p->left, k, v, p->right);
|
||||
p->value = v;
|
||||
return p;
|
||||
}
|
||||
if(p->key < k)
|
||||
p->left = ins0(p->left, k, v, rw);
|
||||
else
|
||||
p->right = ins0(p->right, k, v, rw);
|
||||
return balance(p);
|
||||
}
|
||||
|
||||
static TreeNode*
|
||||
ins1(Tree *m, TreeNode *p, void *k, void *v, TreeNode *rw)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(p == nil)
|
||||
return rwTreeNode(rw, Red, nil, k, v, nil);
|
||||
i = m->cmp(p->key, k);
|
||||
if(i == 0){
|
||||
if(rw)
|
||||
return rwTreeNode(rw, p->color, p->left, k, v, p->right);
|
||||
p->value = v;
|
||||
return p;
|
||||
}
|
||||
if(i < 0)
|
||||
p->left = ins1(m, p->left, k, v, rw);
|
||||
else
|
||||
p->right = ins1(m, p->right, k, v, rw);
|
||||
return balance(p);
|
||||
}
|
||||
|
||||
void
|
||||
treeputelem(Tree *m, void *key, void *val, TreeNode *rw)
|
||||
{
|
||||
if(m->cmp)
|
||||
m->root = ins1(m, m->root, key, val, rw);
|
||||
else
|
||||
m->root = ins0(m->root, key, val, rw);
|
||||
}
|
||||
|
||||
void
|
||||
treeput(Tree *m, void *key, void *val)
|
||||
{
|
||||
treeputelem(m, key, val, nil);
|
||||
}
|
||||
|
||||
void*
|
||||
treeget(Tree *m, void *key)
|
||||
{
|
||||
int i;
|
||||
TreeNode *p;
|
||||
|
||||
p = m->root;
|
||||
if(m->cmp){
|
||||
for(;;){
|
||||
if(p == nil)
|
||||
return nil;
|
||||
i = m->cmp(p->key, key);
|
||||
if(i < 0)
|
||||
p = p->left;
|
||||
else if(i > 0)
|
||||
p = p->right;
|
||||
else
|
||||
return p->value;
|
||||
}
|
||||
}else{
|
||||
for(;;){
|
||||
if(p == nil)
|
||||
return nil;
|
||||
if(p->key == key)
|
||||
return p->value;
|
||||
if(p->key < key)
|
||||
p = p->left;
|
||||
else
|
||||
p = p->right;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
// Renamed from Map to Tree to avoid conflict with libmach.
|
||||
|
||||
/*
|
||||
Copyright (c) 2003-2007 Russ Cox, Tom Bergan, Austin Clements,
|
||||
Massachusetts Institute of Technology
|
||||
Portions Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
typedef struct Tree Tree;
|
||||
typedef struct TreeNode TreeNode;
|
||||
struct Tree
|
||||
{
|
||||
int (*cmp)(void*, void*);
|
||||
TreeNode *root;
|
||||
};
|
||||
|
||||
struct TreeNode
|
||||
{
|
||||
int color;
|
||||
TreeNode *left;
|
||||
void *key;
|
||||
void *value;
|
||||
TreeNode *right;
|
||||
};
|
||||
|
||||
void *treeget(Tree*, void*);
|
||||
void treeput(Tree*, void*, void*);
|
||||
void treeputelem(Tree*, void*, void*, TreeNode*);
|
||||
@@ -280,7 +280,7 @@ typecheckpartialcall(Node *fn, Node *sym)
|
||||
static Node*
|
||||
makepartialcall(Node *fn, Type *t0, Node *meth)
|
||||
{
|
||||
Node *ptr, *n, *fld, *call, *xtype, *xfunc, *cv;
|
||||
Node *ptr, *n, *fld, *call, *xtype, *xfunc, *cv, *savecurfn;
|
||||
Type *rcvrtype, *basetype, *t;
|
||||
NodeList *body, *l, *callargs, *retargs;
|
||||
char *p;
|
||||
@@ -304,6 +304,9 @@ makepartialcall(Node *fn, Type *t0, Node *meth)
|
||||
if(sym->flags & SymUniq)
|
||||
return sym->def;
|
||||
sym->flags |= SymUniq;
|
||||
|
||||
savecurfn = curfn;
|
||||
curfn = N;
|
||||
|
||||
xtype = nod(OTFUNC, N, N);
|
||||
i = 0;
|
||||
@@ -311,6 +314,7 @@ makepartialcall(Node *fn, Type *t0, Node *meth)
|
||||
callargs = nil;
|
||||
ddd = 0;
|
||||
xfunc = nod(ODCLFUNC, N, N);
|
||||
curfn = xfunc;
|
||||
for(t = getinargx(t0)->type; t; t = t->down) {
|
||||
snprint(namebuf, sizeof namebuf, "a%d", i++);
|
||||
n = newname(lookup(namebuf));
|
||||
@@ -385,6 +389,7 @@ makepartialcall(Node *fn, Type *t0, Node *meth)
|
||||
typecheck(&xfunc, Etop);
|
||||
sym->def = xfunc;
|
||||
xtop = list(xtop, xfunc);
|
||||
curfn = savecurfn;
|
||||
|
||||
return xfunc;
|
||||
}
|
||||
|
||||
@@ -161,16 +161,25 @@ reexportdep(Node *n)
|
||||
case OCONV:
|
||||
case OCONVIFACE:
|
||||
case OCONVNOP:
|
||||
case ORUNESTR:
|
||||
case OARRAYBYTESTR:
|
||||
case OARRAYRUNESTR:
|
||||
case OSTRARRAYBYTE:
|
||||
case OSTRARRAYRUNE:
|
||||
case ODOTTYPE:
|
||||
case ODOTTYPE2:
|
||||
case OSTRUCTLIT:
|
||||
case OARRAYLIT:
|
||||
case OPTRLIT:
|
||||
case OMAKEMAP:
|
||||
case OMAKESLICE:
|
||||
case OMAKECHAN:
|
||||
t = n->type;
|
||||
if(!t->sym && t->type)
|
||||
t = t->type;
|
||||
if(t && t->sym && t->sym->def && !exportedsym(t->sym)) {
|
||||
if(debug['E'])
|
||||
print("reexport type for convnop %S\n", t->sym);
|
||||
print("reexport type for expression %S\n", t->sym);
|
||||
exportlist = list(exportlist, t->sym->def);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1218,7 +1218,7 @@ exprfmt(Fmt *f, Node *n, int prec)
|
||||
}
|
||||
if(fmtmode == FExp && ptrlit)
|
||||
// typecheck has overwritten OIND by OTYPE with pointer type.
|
||||
return fmtprint(f, "&%T{ %,H }", n->right->type->type, n->list);
|
||||
return fmtprint(f, "(&%T{ %,H })", n->right->type->type, n->list);
|
||||
return fmtprint(f, "(%N{ %,H })", n->right, n->list);
|
||||
|
||||
case OPTRLIT:
|
||||
|
||||
@@ -282,6 +282,7 @@ struct Node
|
||||
NodeList* cvars; // closure params
|
||||
NodeList* dcl; // autodcl for this func/closure
|
||||
NodeList* inl; // copy of the body for use in inlining
|
||||
NodeList* inldcl; // copy of dcl for use in inlining
|
||||
|
||||
// OLITERAL/OREGISTER
|
||||
Val val;
|
||||
|
||||
@@ -146,6 +146,7 @@ caninl(Node *fn)
|
||||
|
||||
fn->nname->inl = fn->nbody;
|
||||
fn->nbody = inlcopylist(fn->nname->inl);
|
||||
fn->nname->inldcl = inlcopylist(fn->nname->defn->dcl);
|
||||
|
||||
// hack, TODO, check for better way to link method nodes back to the thing with the ->inl
|
||||
// this is so export can find the body of a method
|
||||
@@ -558,8 +559,8 @@ mkinlcall1(Node **np, Node *fn, int isddd)
|
||||
|
||||
//dumplist("ninit pre", ninit);
|
||||
|
||||
if (fn->defn) // local function
|
||||
dcl = fn->defn->dcl;
|
||||
if(fn->defn) // local function
|
||||
dcl = fn->inldcl;
|
||||
else // imported function
|
||||
dcl = fn->dcl;
|
||||
|
||||
|
||||
@@ -50,9 +50,10 @@ init1(Node *n, NodeList **out)
|
||||
case PFUNC:
|
||||
break;
|
||||
default:
|
||||
if(isblank(n) && n->defn != N && n->defn->initorder == InitNotStarted) {
|
||||
n->defn->initorder = InitDone;
|
||||
*out = list(*out, n->defn);
|
||||
if(isblank(n) && n->curfn == N && n->defn != N && n->defn->initorder == InitNotStarted) {
|
||||
// blank names initialization is part of init() but not
|
||||
// when they are inside a function.
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -62,7 +63,7 @@ init1(Node *n, NodeList **out)
|
||||
if(n->initorder == InitPending) {
|
||||
if(n->class == PFUNC)
|
||||
return;
|
||||
|
||||
|
||||
// if there have already been errors printed,
|
||||
// those errors probably confused us and
|
||||
// there might not be a loop. let the user
|
||||
@@ -127,8 +128,8 @@ init1(Node *n, NodeList **out)
|
||||
init2(n->defn->right, out);
|
||||
if(debug['j'])
|
||||
print("%S\n", n->sym);
|
||||
if(!staticinit(n, out)) {
|
||||
if(debug['%']) dump("nonstatic", n->defn);
|
||||
if(isblank(n) || !staticinit(n, out)) {
|
||||
if(debug['%']) dump("nonstatic", n->defn);
|
||||
*out = list(*out, n->defn);
|
||||
}
|
||||
} else if(0) {
|
||||
@@ -149,6 +150,7 @@ if(debug['%']) dump("nonstatic", n->defn);
|
||||
n->defn->initorder = InitDone;
|
||||
for(l=n->defn->rlist; l; l=l->next)
|
||||
init1(l->n, out);
|
||||
if(debug['%']) dump("nonstatic", n->defn);
|
||||
*out = list(*out, n->defn);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1338,6 +1338,13 @@ walkexpr(Node **np, NodeList **init)
|
||||
fatal("missing switch %O", n->op);
|
||||
|
||||
ret:
|
||||
// Expressions that are constant at run time but not
|
||||
// considered const by the language spec are not turned into
|
||||
// constants until walk. For example, if n is y%1 == 0, the
|
||||
// walk of y%1 may have replaced it by 0.
|
||||
// Check whether n with its updated args is itself now a constant.
|
||||
evconst(n);
|
||||
|
||||
ullmancalc(n);
|
||||
|
||||
if(debug['w'] && n != N)
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# Copyright 2012 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.
|
||||
|
||||
include ../../Make.dist
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
/*
|
||||
|
||||
Prof is a rudimentary real-time profiler.
|
||||
|
||||
Given a command to run or the process id (pid) of a command already
|
||||
running, it samples the program's state at regular intervals and reports
|
||||
on its behavior. With no options, it prints a histogram of the locations
|
||||
in the code that were sampled during execution.
|
||||
|
||||
Since it is a real-time profiler, unlike a traditional profiler it samples
|
||||
the program's state even when it is not running, such as when it is
|
||||
asleep or waiting for I/O. Each thread contributes equally to the
|
||||
statistics.
|
||||
|
||||
Usage:
|
||||
go tool prof -p pid [-t total_secs] [-d delta_msec] [6.out args ...]
|
||||
|
||||
The output modes (default -h) are:
|
||||
|
||||
-P file.prof:
|
||||
Write the profile information to file.prof, in the format used by pprof.
|
||||
At the moment, this only works on Linux amd64 binaries and requires that the
|
||||
binary be written using 6l -e to produce ELF debug info.
|
||||
See http://code.google.com/p/google-perftools for details.
|
||||
-h: histograms
|
||||
How many times a sample occurred at each location.
|
||||
-f: dynamic functions
|
||||
At each sample period, print the name of the executing function.
|
||||
-l: dynamic file and line numbers
|
||||
At each sample period, print the file and line number of the executing instruction.
|
||||
-r: dynamic registers
|
||||
At each sample period, print the register contents.
|
||||
-s: dynamic function stack traces
|
||||
At each sample period, print the symbolic stack trace.
|
||||
|
||||
Flag -t sets the maximum real time to sample, in seconds, and -d
|
||||
sets the sampling interval in milliseconds. The default is to sample
|
||||
every 100ms until the program completes.
|
||||
|
||||
It is installed as go tool prof and is architecture-independent.
|
||||
|
||||
*/
|
||||
package main
|
||||
@@ -1,910 +0,0 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
// +build !plan9
|
||||
|
||||
#include <u.h>
|
||||
#include <time.h>
|
||||
#include <libc.h>
|
||||
#include <bio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#define Ureg Ureg_amd64
|
||||
#include <ureg_amd64.h>
|
||||
#undef Ureg
|
||||
#define Ureg Ureg_x86
|
||||
#include <ureg_x86.h>
|
||||
#undef Ureg
|
||||
#include <mach.h>
|
||||
|
||||
char* file = "6.out";
|
||||
static Fhdr fhdr;
|
||||
int have_syms;
|
||||
int fd;
|
||||
struct Ureg_amd64 ureg_amd64;
|
||||
struct Ureg_x86 ureg_x86;
|
||||
int total_sec = 0;
|
||||
int delta_msec = 100;
|
||||
int nsample;
|
||||
int nsamplethread;
|
||||
|
||||
// pprof data, stored as sequences of N followed by N PC values.
|
||||
// See http://code.google.com/p/google-perftools .
|
||||
uvlong *ppdata; // traces
|
||||
Biobuf* pproffd; // file descriptor to write trace info
|
||||
long ppstart; // start position of current trace
|
||||
long nppdata; // length of data
|
||||
long ppalloc; // size of allocated data
|
||||
char ppmapdata[10*1024]; // the map information for the output file
|
||||
|
||||
// output formats
|
||||
int pprof; // print pprof output to named file
|
||||
int functions; // print functions
|
||||
int histograms; // print histograms
|
||||
int linenums; // print file and line numbers rather than function names
|
||||
int registers; // print registers
|
||||
int stacks; // print stack traces
|
||||
|
||||
int pid; // main process pid
|
||||
|
||||
int nthread; // number of threads
|
||||
int thread[32]; // thread pids
|
||||
Map *map[32]; // thread maps
|
||||
|
||||
void
|
||||
Usage(void)
|
||||
{
|
||||
fprint(2, "Usage: prof -p pid [-t total_secs] [-d delta_msec]\n");
|
||||
fprint(2, " prof [-t total_secs] [-d delta_msec] 6.out args ...\n");
|
||||
fprint(2, "\tformats (default -h):\n");
|
||||
fprint(2, "\t\t-P file.prof: write [c]pprof output to file.prof\n");
|
||||
fprint(2, "\t\t-h: histograms\n");
|
||||
fprint(2, "\t\t-f: dynamic functions\n");
|
||||
fprint(2, "\t\t-l: dynamic file and line numbers\n");
|
||||
fprint(2, "\t\t-r: dynamic registers\n");
|
||||
fprint(2, "\t\t-s: dynamic function stack traces\n");
|
||||
fprint(2, "\t\t-hs: include stack info in histograms\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
typedef struct PC PC;
|
||||
struct PC {
|
||||
uvlong pc;
|
||||
uvlong callerpc;
|
||||
unsigned int count;
|
||||
PC* next;
|
||||
};
|
||||
|
||||
enum {
|
||||
Ncounters = 256
|
||||
};
|
||||
|
||||
PC *counters[Ncounters];
|
||||
|
||||
// Set up by setarch() to make most of the code architecture-independent.
|
||||
typedef struct Arch Arch;
|
||||
struct Arch {
|
||||
char* name;
|
||||
void (*regprint)(void);
|
||||
int (*getregs)(Map*);
|
||||
int (*getPC)(Map*);
|
||||
int (*getSP)(Map*);
|
||||
uvlong (*uregPC)(void);
|
||||
uvlong (*uregSP)(void);
|
||||
void (*ppword)(uvlong w);
|
||||
};
|
||||
|
||||
void
|
||||
amd64_regprint(void)
|
||||
{
|
||||
fprint(2, "ax\t0x%llux\n", ureg_amd64.ax);
|
||||
fprint(2, "bx\t0x%llux\n", ureg_amd64.bx);
|
||||
fprint(2, "cx\t0x%llux\n", ureg_amd64.cx);
|
||||
fprint(2, "dx\t0x%llux\n", ureg_amd64.dx);
|
||||
fprint(2, "si\t0x%llux\n", ureg_amd64.si);
|
||||
fprint(2, "di\t0x%llux\n", ureg_amd64.di);
|
||||
fprint(2, "bp\t0x%llux\n", ureg_amd64.bp);
|
||||
fprint(2, "r8\t0x%llux\n", ureg_amd64.r8);
|
||||
fprint(2, "r9\t0x%llux\n", ureg_amd64.r9);
|
||||
fprint(2, "r10\t0x%llux\n", ureg_amd64.r10);
|
||||
fprint(2, "r11\t0x%llux\n", ureg_amd64.r11);
|
||||
fprint(2, "r12\t0x%llux\n", ureg_amd64.r12);
|
||||
fprint(2, "r13\t0x%llux\n", ureg_amd64.r13);
|
||||
fprint(2, "r14\t0x%llux\n", ureg_amd64.r14);
|
||||
fprint(2, "r15\t0x%llux\n", ureg_amd64.r15);
|
||||
fprint(2, "ds\t0x%llux\n", ureg_amd64.ds);
|
||||
fprint(2, "es\t0x%llux\n", ureg_amd64.es);
|
||||
fprint(2, "fs\t0x%llux\n", ureg_amd64.fs);
|
||||
fprint(2, "gs\t0x%llux\n", ureg_amd64.gs);
|
||||
fprint(2, "type\t0x%llux\n", ureg_amd64.type);
|
||||
fprint(2, "error\t0x%llux\n", ureg_amd64.error);
|
||||
fprint(2, "pc\t0x%llux\n", ureg_amd64.ip);
|
||||
fprint(2, "cs\t0x%llux\n", ureg_amd64.cs);
|
||||
fprint(2, "flags\t0x%llux\n", ureg_amd64.flags);
|
||||
fprint(2, "sp\t0x%llux\n", ureg_amd64.sp);
|
||||
fprint(2, "ss\t0x%llux\n", ureg_amd64.ss);
|
||||
}
|
||||
|
||||
int
|
||||
amd64_getregs(Map *map)
|
||||
{
|
||||
int i;
|
||||
union {
|
||||
uvlong regs[1];
|
||||
struct Ureg_amd64 ureg;
|
||||
} u;
|
||||
|
||||
for(i = 0; i < sizeof ureg_amd64; i+=8) {
|
||||
if(get8(map, (uvlong)i, &u.regs[i/8]) < 0)
|
||||
return -1;
|
||||
}
|
||||
ureg_amd64 = u.ureg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
amd64_getPC(Map *map)
|
||||
{
|
||||
uvlong x;
|
||||
int r;
|
||||
|
||||
r = get8(map, offsetof(struct Ureg_amd64, ip), &x);
|
||||
ureg_amd64.ip = x;
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
amd64_getSP(Map *map)
|
||||
{
|
||||
uvlong x;
|
||||
int r;
|
||||
|
||||
r = get8(map, offsetof(struct Ureg_amd64, sp), &x);
|
||||
ureg_amd64.sp = x;
|
||||
return r;
|
||||
}
|
||||
|
||||
uvlong
|
||||
amd64_uregPC(void)
|
||||
{
|
||||
return ureg_amd64.ip;
|
||||
}
|
||||
|
||||
uvlong
|
||||
amd64_uregSP(void)
|
||||
{
|
||||
return ureg_amd64.sp;
|
||||
}
|
||||
|
||||
void
|
||||
amd64_ppword(uvlong w)
|
||||
{
|
||||
uchar buf[8];
|
||||
|
||||
buf[0] = w;
|
||||
buf[1] = w >> 8;
|
||||
buf[2] = w >> 16;
|
||||
buf[3] = w >> 24;
|
||||
buf[4] = w >> 32;
|
||||
buf[5] = w >> 40;
|
||||
buf[6] = w >> 48;
|
||||
buf[7] = w >> 56;
|
||||
Bwrite(pproffd, buf, 8);
|
||||
}
|
||||
|
||||
void
|
||||
x86_regprint(void)
|
||||
{
|
||||
fprint(2, "ax\t0x%ux\n", ureg_x86.ax);
|
||||
fprint(2, "bx\t0x%ux\n", ureg_x86.bx);
|
||||
fprint(2, "cx\t0x%ux\n", ureg_x86.cx);
|
||||
fprint(2, "dx\t0x%ux\n", ureg_x86.dx);
|
||||
fprint(2, "si\t0x%ux\n", ureg_x86.si);
|
||||
fprint(2, "di\t0x%ux\n", ureg_x86.di);
|
||||
fprint(2, "bp\t0x%ux\n", ureg_x86.bp);
|
||||
fprint(2, "ds\t0x%ux\n", ureg_x86.ds);
|
||||
fprint(2, "es\t0x%ux\n", ureg_x86.es);
|
||||
fprint(2, "fs\t0x%ux\n", ureg_x86.fs);
|
||||
fprint(2, "gs\t0x%ux\n", ureg_x86.gs);
|
||||
fprint(2, "cs\t0x%ux\n", ureg_x86.cs);
|
||||
fprint(2, "flags\t0x%ux\n", ureg_x86.flags);
|
||||
fprint(2, "pc\t0x%ux\n", ureg_x86.pc);
|
||||
fprint(2, "sp\t0x%ux\n", ureg_x86.sp);
|
||||
fprint(2, "ss\t0x%ux\n", ureg_x86.ss);
|
||||
}
|
||||
|
||||
int
|
||||
x86_getregs(Map *map)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < sizeof ureg_x86; i+=4) {
|
||||
if(get4(map, (uvlong)i, &((uint32*)&ureg_x86)[i/4]) < 0)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
x86_getPC(Map* map)
|
||||
{
|
||||
return get4(map, offsetof(struct Ureg_x86, pc), &ureg_x86.pc);
|
||||
}
|
||||
|
||||
int
|
||||
x86_getSP(Map* map)
|
||||
{
|
||||
return get4(map, offsetof(struct Ureg_x86, sp), &ureg_x86.sp);
|
||||
}
|
||||
|
||||
uvlong
|
||||
x86_uregPC(void)
|
||||
{
|
||||
return (uvlong)ureg_x86.pc;
|
||||
}
|
||||
|
||||
uvlong
|
||||
x86_uregSP(void)
|
||||
{
|
||||
return (uvlong)ureg_x86.sp;
|
||||
}
|
||||
|
||||
void
|
||||
x86_ppword(uvlong w)
|
||||
{
|
||||
uchar buf[4];
|
||||
|
||||
buf[0] = w;
|
||||
buf[1] = w >> 8;
|
||||
buf[2] = w >> 16;
|
||||
buf[3] = w >> 24;
|
||||
Bwrite(pproffd, buf, 4);
|
||||
}
|
||||
|
||||
Arch archtab[] = {
|
||||
{
|
||||
"amd64",
|
||||
amd64_regprint,
|
||||
amd64_getregs,
|
||||
amd64_getPC,
|
||||
amd64_getSP,
|
||||
amd64_uregPC,
|
||||
amd64_uregSP,
|
||||
amd64_ppword,
|
||||
},
|
||||
{
|
||||
"386",
|
||||
x86_regprint,
|
||||
x86_getregs,
|
||||
x86_getPC,
|
||||
x86_getSP,
|
||||
x86_uregPC,
|
||||
x86_uregSP,
|
||||
x86_ppword,
|
||||
},
|
||||
{
|
||||
nil
|
||||
}
|
||||
};
|
||||
|
||||
Arch *arch;
|
||||
|
||||
int
|
||||
setarch(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(mach != nil) {
|
||||
for(i = 0; archtab[i].name != nil; i++) {
|
||||
if (strcmp(mach->name, archtab[i].name) == 0) {
|
||||
arch = &archtab[i];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
getthreads(void)
|
||||
{
|
||||
int i, j, curn, found;
|
||||
Map *curmap[nelem(map)];
|
||||
int curthread[nelem(map)];
|
||||
static int complained = 0;
|
||||
|
||||
curn = procthreadpids(pid, curthread, nelem(curthread));
|
||||
if(curn <= 0)
|
||||
return curn;
|
||||
|
||||
if(curn > nelem(map)) {
|
||||
if(complained == 0) {
|
||||
fprint(2, "prof: too many threads; limiting to %d\n", nthread, nelem(map));
|
||||
complained = 1;
|
||||
}
|
||||
curn = nelem(map);
|
||||
}
|
||||
if(curn == nthread && memcmp(thread, curthread, curn*sizeof(*thread)) == 0)
|
||||
return curn; // no changes
|
||||
|
||||
// Number of threads has changed (might be the init case).
|
||||
// A bit expensive but rare enough not to bother being clever.
|
||||
for(i = 0; i < curn; i++) {
|
||||
found = 0;
|
||||
for(j = 0; j < nthread; j++) {
|
||||
if(curthread[i] == thread[j]) {
|
||||
found = 1;
|
||||
curmap[i] = map[j];
|
||||
map[j] = nil;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found)
|
||||
continue;
|
||||
|
||||
// map new thread
|
||||
curmap[i] = attachproc(curthread[i], &fhdr);
|
||||
if(curmap[i] == nil) {
|
||||
fprint(2, "prof: can't attach to %d: %r\n", curthread[i]);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for(j = 0; j < nthread; j++)
|
||||
if(map[j] != nil)
|
||||
detachproc(map[j]);
|
||||
|
||||
nthread = curn;
|
||||
memmove(thread, curthread, nthread*sizeof thread[0]);
|
||||
memmove(map, curmap, sizeof map);
|
||||
return nthread;
|
||||
}
|
||||
|
||||
int
|
||||
sample(Map *map)
|
||||
{
|
||||
static int n;
|
||||
|
||||
n++;
|
||||
if(registers) {
|
||||
if(arch->getregs(map) < 0)
|
||||
goto bad;
|
||||
} else {
|
||||
// we need only two registers
|
||||
if(arch->getPC(map) < 0)
|
||||
goto bad;
|
||||
if(arch->getSP(map) < 0)
|
||||
goto bad;
|
||||
}
|
||||
return 1;
|
||||
bad:
|
||||
if(n == 1)
|
||||
fprint(2, "prof: can't read registers: %r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
addtohistogram(uvlong pc, uvlong callerpc, uvlong sp)
|
||||
{
|
||||
int h;
|
||||
PC *x;
|
||||
|
||||
USED(sp);
|
||||
|
||||
h = (pc + callerpc*101) % Ncounters;
|
||||
for(x = counters[h]; x != NULL; x = x->next) {
|
||||
if(x->pc == pc && x->callerpc == callerpc) {
|
||||
x->count++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
x = malloc(sizeof(PC));
|
||||
if(x == nil)
|
||||
sysfatal("out of memory");
|
||||
x->pc = pc;
|
||||
x->callerpc = callerpc;
|
||||
x->count = 1;
|
||||
x->next = counters[h];
|
||||
counters[h] = x;
|
||||
}
|
||||
|
||||
void
|
||||
addppword(uvlong pc)
|
||||
{
|
||||
if(pc == 0) {
|
||||
return;
|
||||
}
|
||||
if(nppdata == ppalloc) {
|
||||
ppalloc = (1000+nppdata)*2;
|
||||
ppdata = realloc(ppdata, ppalloc * sizeof ppdata[0]);
|
||||
if(ppdata == nil) {
|
||||
fprint(2, "prof: realloc failed: %r\n");
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
ppdata[nppdata++] = pc;
|
||||
}
|
||||
|
||||
void
|
||||
startpptrace(void)
|
||||
{
|
||||
ppstart = nppdata;
|
||||
addppword(~0);
|
||||
}
|
||||
|
||||
void
|
||||
endpptrace(void)
|
||||
{
|
||||
ppdata[ppstart] = nppdata-ppstart-1;
|
||||
}
|
||||
|
||||
uvlong nextpc;
|
||||
|
||||
void
|
||||
xptrace(Map *map, uvlong pc, uvlong sp, Symbol *sym)
|
||||
{
|
||||
USED(map);
|
||||
|
||||
char buf[1024];
|
||||
if(sym == nil){
|
||||
fprint(2, "syms\n");
|
||||
return;
|
||||
}
|
||||
if(histograms)
|
||||
addtohistogram(nextpc, pc, sp);
|
||||
if(!histograms || stacks > 1 || pprof) {
|
||||
if(nextpc == 0)
|
||||
nextpc = sym->value;
|
||||
if(stacks){
|
||||
fprint(2, "%s(", sym->name);
|
||||
fprint(2, ")");
|
||||
if(nextpc != sym->value)
|
||||
fprint(2, "+%#llux ", nextpc - sym->value);
|
||||
if(have_syms && linenums && fileline(buf, sizeof buf, pc)) {
|
||||
fprint(2, " %s", buf);
|
||||
}
|
||||
fprint(2, "\n");
|
||||
}
|
||||
if (pprof) {
|
||||
addppword(nextpc);
|
||||
}
|
||||
}
|
||||
nextpc = pc;
|
||||
}
|
||||
|
||||
void
|
||||
stacktracepcsp(Map *map, uvlong pc, uvlong sp)
|
||||
{
|
||||
nextpc = pc;
|
||||
if(pprof){
|
||||
startpptrace();
|
||||
}
|
||||
if(machdata->ctrace==nil)
|
||||
fprint(2, "no machdata->ctrace\n");
|
||||
else if(machdata->ctrace(map, pc, sp, 0, xptrace) <= 0)
|
||||
fprint(2, "no stack frame: pc=%#p sp=%#p\n", pc, sp);
|
||||
else {
|
||||
addtohistogram(nextpc, 0, sp);
|
||||
if(stacks)
|
||||
fprint(2, "\n");
|
||||
}
|
||||
if(pprof){
|
||||
endpptrace();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
printpc(Map *map, uvlong pc, uvlong sp)
|
||||
{
|
||||
char buf[1024];
|
||||
if(registers)
|
||||
arch->regprint();
|
||||
if(have_syms > 0 && linenums && fileline(buf, sizeof buf, pc))
|
||||
fprint(2, "%s\n", buf);
|
||||
if(have_syms > 0 && functions) {
|
||||
symoff(buf, sizeof(buf), pc, CANY);
|
||||
fprint(2, "%s\n", buf);
|
||||
}
|
||||
if(stacks || pprof){
|
||||
stacktracepcsp(map, pc, sp);
|
||||
}
|
||||
else if(histograms){
|
||||
addtohistogram(pc, 0, sp);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ppmaps(void)
|
||||
{
|
||||
int fd, n;
|
||||
char tmp[100];
|
||||
Seg *seg;
|
||||
|
||||
// If it's Linux, the info is in /proc/$pid/maps
|
||||
snprint(tmp, sizeof tmp, "/proc/%d/maps", pid);
|
||||
fd = open(tmp, 0);
|
||||
if(fd >= 0) {
|
||||
n = read(fd, ppmapdata, sizeof ppmapdata - 1);
|
||||
close(fd);
|
||||
if(n < 0) {
|
||||
fprint(2, "prof: can't read %s: %r\n", tmp);
|
||||
exit(2);
|
||||
}
|
||||
ppmapdata[n] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// It's probably a mac. Synthesize an entry for the text file.
|
||||
// The register segment may come first but it has a zero offset, so grab the first non-zero offset segment.
|
||||
for(n = 0; n < 3; n++){
|
||||
seg = &map[0]->seg[n];
|
||||
if(seg->b == 0) {
|
||||
continue;
|
||||
}
|
||||
snprint(ppmapdata, sizeof ppmapdata,
|
||||
"%.16x-%.16x r-xp %d 00:00 34968549 %s\n",
|
||||
seg->b, seg->e, seg->f, "/home/r/6.out"
|
||||
);
|
||||
return;
|
||||
}
|
||||
fprint(2, "prof: no text segment in maps for %s\n", file);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
void
|
||||
samples(void)
|
||||
{
|
||||
int i, pid, msec;
|
||||
struct timespec req;
|
||||
int getmaps;
|
||||
|
||||
req.tv_sec = delta_msec/1000;
|
||||
req.tv_nsec = 1000000*(delta_msec % 1000);
|
||||
getmaps = 0;
|
||||
if(pprof)
|
||||
getmaps= 1;
|
||||
for(msec = 0; total_sec <= 0 || msec < 1000*total_sec; msec += delta_msec) {
|
||||
nsample++;
|
||||
nsamplethread += nthread;
|
||||
for(i = 0; i < nthread; i++) {
|
||||
pid = thread[i];
|
||||
if(ctlproc(pid, "stop") < 0)
|
||||
return;
|
||||
if(!sample(map[i])) {
|
||||
ctlproc(pid, "start");
|
||||
return;
|
||||
}
|
||||
printpc(map[i], arch->uregPC(), arch->uregSP());
|
||||
ctlproc(pid, "start");
|
||||
}
|
||||
nanosleep(&req, NULL);
|
||||
getthreads();
|
||||
if(nthread == 0)
|
||||
break;
|
||||
if(getmaps) {
|
||||
getmaps = 0;
|
||||
ppmaps();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct Func Func;
|
||||
struct Func
|
||||
{
|
||||
Func *next;
|
||||
Symbol s;
|
||||
uint onstack;
|
||||
uint leaf;
|
||||
};
|
||||
|
||||
Func *func[257];
|
||||
int nfunc;
|
||||
|
||||
Func*
|
||||
findfunc(uvlong pc)
|
||||
{
|
||||
Func *f;
|
||||
uint h;
|
||||
Symbol s;
|
||||
|
||||
if(pc == 0)
|
||||
return nil;
|
||||
|
||||
if(!findsym(pc, CTEXT, &s))
|
||||
return nil;
|
||||
|
||||
h = s.value % nelem(func);
|
||||
for(f = func[h]; f != NULL; f = f->next)
|
||||
if(f->s.value == s.value)
|
||||
return f;
|
||||
|
||||
f = malloc(sizeof *f);
|
||||
if(f == nil)
|
||||
sysfatal("out of memory");
|
||||
memset(f, 0, sizeof *f);
|
||||
f->s = s;
|
||||
f->next = func[h];
|
||||
func[h] = f;
|
||||
nfunc++;
|
||||
return f;
|
||||
}
|
||||
|
||||
int
|
||||
compareleaf(const void *va, const void *vb)
|
||||
{
|
||||
Func *a, *b;
|
||||
|
||||
a = *(Func**)va;
|
||||
b = *(Func**)vb;
|
||||
if(a->leaf != b->leaf)
|
||||
return b->leaf - a->leaf;
|
||||
if(a->onstack != b->onstack)
|
||||
return b->onstack - a->onstack;
|
||||
return strcmp(a->s.name, b->s.name);
|
||||
}
|
||||
|
||||
void
|
||||
dumphistogram(void)
|
||||
{
|
||||
int i, h, n;
|
||||
PC *x;
|
||||
Func *f, **ff;
|
||||
|
||||
if(!histograms)
|
||||
return;
|
||||
|
||||
// assign counts to functions.
|
||||
for(h = 0; h < Ncounters; h++) {
|
||||
for(x = counters[h]; x != NULL; x = x->next) {
|
||||
f = findfunc(x->pc);
|
||||
if(f) {
|
||||
f->onstack += x->count;
|
||||
f->leaf += x->count;
|
||||
}
|
||||
f = findfunc(x->callerpc);
|
||||
if(f)
|
||||
f->leaf -= x->count;
|
||||
}
|
||||
}
|
||||
|
||||
// build array
|
||||
ff = malloc(nfunc*sizeof ff[0]);
|
||||
if(ff == nil)
|
||||
sysfatal("out of memory");
|
||||
n = 0;
|
||||
for(h = 0; h < nelem(func); h++)
|
||||
for(f = func[h]; f != NULL; f = f->next)
|
||||
ff[n++] = f;
|
||||
|
||||
// sort by leaf counts
|
||||
qsort(ff, nfunc, sizeof ff[0], compareleaf);
|
||||
|
||||
// print.
|
||||
fprint(2, "%d samples (avg %.1g threads)\n", nsample, (double)nsamplethread/nsample);
|
||||
for(i = 0; i < nfunc; i++) {
|
||||
f = ff[i];
|
||||
fprint(2, "%6.2f%%\t", 100.0*(double)f->leaf/nsample);
|
||||
if(stacks)
|
||||
fprint(2, "%6.2f%%\t", 100.0*(double)f->onstack/nsample);
|
||||
fprint(2, "%s\n", f->s.name);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct Trace Trace;
|
||||
struct Trace {
|
||||
int count;
|
||||
int npc;
|
||||
uvlong *pc;
|
||||
Trace *next;
|
||||
};
|
||||
|
||||
void
|
||||
dumppprof(void)
|
||||
{
|
||||
uvlong i, n, *p, *e;
|
||||
int ntrace;
|
||||
Trace *trace, *tp, *up, *prev;
|
||||
|
||||
if(!pprof)
|
||||
return;
|
||||
e = ppdata + nppdata;
|
||||
// Create list of traces. First, count the traces
|
||||
ntrace = 0;
|
||||
for(p = ppdata; p < e;) {
|
||||
n = *p++;
|
||||
p += n;
|
||||
if(n == 0)
|
||||
continue;
|
||||
ntrace++;
|
||||
}
|
||||
if(ntrace <= 0)
|
||||
return;
|
||||
// Allocate and link the traces together.
|
||||
trace = malloc(ntrace * sizeof(Trace));
|
||||
if(trace == nil)
|
||||
sysfatal("out of memory");
|
||||
tp = trace;
|
||||
for(p = ppdata; p < e;) {
|
||||
n = *p++;
|
||||
if(n == 0)
|
||||
continue;
|
||||
tp->count = 1;
|
||||
tp->npc = n;
|
||||
tp->pc = p;
|
||||
tp->next = tp+1;
|
||||
tp++;
|
||||
p += n;
|
||||
}
|
||||
trace[ntrace-1].next = nil;
|
||||
// Eliminate duplicates. Lousy algorithm, although not as bad as it looks because
|
||||
// the list collapses fast.
|
||||
for(tp = trace; tp != nil; tp = tp->next) {
|
||||
prev = tp;
|
||||
for(up = tp->next; up != nil; up = up->next) {
|
||||
if(up->npc == tp->npc && memcmp(up->pc, tp->pc, up->npc*sizeof up->pc[0]) == 0) {
|
||||
tp->count++;
|
||||
prev->next = up->next;
|
||||
} else {
|
||||
prev = up;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Write file.
|
||||
// See http://code.google.com/p/google-perftools/source/browse/trunk/doc/cpuprofile-fileformat.html
|
||||
// 1) Header
|
||||
arch->ppword(0); // must be zero
|
||||
arch->ppword(3); // 3 words follow in header
|
||||
arch->ppword(0); // must be zero
|
||||
arch->ppword(delta_msec * 1000); // sampling period in microseconds
|
||||
arch->ppword(0); // must be zero (padding)
|
||||
// 2) One record for each trace.
|
||||
for(tp = trace; tp != nil; tp = tp->next) {
|
||||
arch->ppword(tp->count);
|
||||
arch->ppword(tp->npc);
|
||||
for(i = 0; i < tp->npc; i++) {
|
||||
arch->ppword(tp->pc[i]);
|
||||
}
|
||||
}
|
||||
// 3) Binary trailer
|
||||
arch->ppword(0); // must be zero
|
||||
arch->ppword(1); // must be one
|
||||
arch->ppword(0); // must be zero
|
||||
// 4) Mapped objects.
|
||||
Bwrite(pproffd, ppmapdata, strlen(ppmapdata));
|
||||
// 5) That's it.
|
||||
Bterm(pproffd);
|
||||
}
|
||||
|
||||
int
|
||||
startprocess(char **argv)
|
||||
{
|
||||
int pid;
|
||||
|
||||
if((pid = fork()) == 0) {
|
||||
pid = getpid();
|
||||
if(ctlproc(pid, "hang") < 0){
|
||||
fprint(2, "prof: child process could not hang\n");
|
||||
exits(0);
|
||||
}
|
||||
execv(argv[0], argv);
|
||||
fprint(2, "prof: could not exec %s: %r\n", argv[0]);
|
||||
exits(0);
|
||||
}
|
||||
|
||||
if(pid == -1) {
|
||||
fprint(2, "prof: could not fork\n");
|
||||
exit(1);
|
||||
}
|
||||
if(ctlproc(pid, "attached") < 0 || ctlproc(pid, "waitstop") < 0) {
|
||||
fprint(2, "prof: could not attach to child process: %r\n");
|
||||
exit(1);
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
void
|
||||
detach(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < nthread; i++)
|
||||
detachproc(map[i]);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
char *ppfile;
|
||||
|
||||
ARGBEGIN{
|
||||
case 'P':
|
||||
pprof =1;
|
||||
ppfile = EARGF(Usage());
|
||||
pproffd = Bopen(ppfile, OWRITE);
|
||||
if(pproffd == nil) {
|
||||
fprint(2, "prof: cannot open %s: %r\n", ppfile);
|
||||
exit(2);
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
delta_msec = atoi(EARGF(Usage()));
|
||||
break;
|
||||
case 't':
|
||||
total_sec = atoi(EARGF(Usage()));
|
||||
break;
|
||||
case 'p':
|
||||
pid = atoi(EARGF(Usage()));
|
||||
break;
|
||||
case 'f':
|
||||
functions = 1;
|
||||
break;
|
||||
case 'h':
|
||||
histograms = 1;
|
||||
break;
|
||||
case 'l':
|
||||
linenums = 1;
|
||||
break;
|
||||
case 'r':
|
||||
registers = 1;
|
||||
break;
|
||||
case 's':
|
||||
stacks++;
|
||||
break;
|
||||
default:
|
||||
Usage();
|
||||
}ARGEND
|
||||
if(pid <= 0 && argc == 0)
|
||||
Usage();
|
||||
if(functions+linenums+registers+stacks+pprof == 0)
|
||||
histograms = 1;
|
||||
if(!machbyname("amd64")) {
|
||||
fprint(2, "prof: no amd64 support\n", pid);
|
||||
exit(1);
|
||||
}
|
||||
if(argc > 0)
|
||||
file = argv[0];
|
||||
else if(pid) {
|
||||
file = proctextfile(pid);
|
||||
if (file == NULL) {
|
||||
fprint(2, "prof: can't find file for pid %d: %r\n", pid);
|
||||
fprint(2, "prof: on Darwin, need to provide file name explicitly\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
fd = open(file, 0);
|
||||
if(fd < 0) {
|
||||
fprint(2, "prof: can't open %s: %r\n", file);
|
||||
exit(1);
|
||||
}
|
||||
if(crackhdr(fd, &fhdr)) {
|
||||
have_syms = syminit(fd, &fhdr);
|
||||
if(!have_syms) {
|
||||
fprint(2, "prof: no symbols for %s: %r\n", file);
|
||||
}
|
||||
} else {
|
||||
fprint(2, "prof: crack header for %s: %r\n", file);
|
||||
exit(1);
|
||||
}
|
||||
if(pid <= 0)
|
||||
pid = startprocess(argv);
|
||||
attachproc(pid, &fhdr); // initializes thread list
|
||||
if(setarch() < 0) {
|
||||
detach();
|
||||
fprint(2, "prof: can't identify binary architecture for pid %d\n", pid);
|
||||
exit(1);
|
||||
}
|
||||
if(getthreads() <= 0) {
|
||||
detach();
|
||||
fprint(2, "prof: can't find threads for pid %d\n", pid);
|
||||
exit(1);
|
||||
}
|
||||
for(i = 0; i < nthread; i++)
|
||||
ctlproc(thread[i], "start");
|
||||
samples();
|
||||
detach();
|
||||
dumphistogram();
|
||||
dumppprof();
|
||||
exit(0);
|
||||
}
|
||||
@@ -585,23 +585,28 @@ func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
}
|
||||
var m int
|
||||
for {
|
||||
if b.Available() == 0 {
|
||||
if err1 := b.Flush(); err1 != nil {
|
||||
return n, err1
|
||||
}
|
||||
}
|
||||
m, err = r.Read(b.buf[b.n:])
|
||||
if m == 0 {
|
||||
break
|
||||
}
|
||||
b.n += m
|
||||
n += int64(m)
|
||||
if b.Available() == 0 {
|
||||
if err1 := b.Flush(); err1 != nil {
|
||||
return n, err1
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
// If we filled the buffer exactly, flush pre-emptively.
|
||||
if b.Available() == 0 {
|
||||
err = b.Flush()
|
||||
} else {
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
@@ -847,6 +847,10 @@ func TestWriterReadFrom(t *testing.T) {
|
||||
t.Errorf("ws[%d],rs[%d]: w.ReadFrom(r) = %d, %v, want %d, nil", wi, ri, n, err, len(input))
|
||||
continue
|
||||
}
|
||||
if err := w.Flush(); err != nil {
|
||||
t.Errorf("Flush returned %v", err)
|
||||
continue
|
||||
}
|
||||
if got, want := b.String(), string(input); got != want {
|
||||
t.Errorf("ws[%d], rs[%d]:\ngot %q\nwant %q\n", wi, ri, got, want)
|
||||
}
|
||||
@@ -1003,6 +1007,24 @@ func TestReaderClearError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Test for golang.org/issue/5947
|
||||
func TestWriterReadFromWhileFull(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
w := NewWriterSize(buf, 10)
|
||||
|
||||
// Fill buffer exactly.
|
||||
n, err := w.Write([]byte("0123456789"))
|
||||
if n != 10 || err != nil {
|
||||
t.Fatalf("Write returned (%v, %v), want (10, nil)", n, err)
|
||||
}
|
||||
|
||||
// Use ReadFrom to read in some data.
|
||||
n2, err := w.ReadFrom(strings.NewReader("abcdef"))
|
||||
if n2 != 6 || err != nil {
|
||||
t.Fatalf("ReadFrom returned (%v, %v), want (6, nil)", n, err)
|
||||
}
|
||||
}
|
||||
|
||||
// An onlyReader only implements io.Reader, no matter what other methods the underlying implementation may have.
|
||||
type onlyReader struct {
|
||||
r io.Reader
|
||||
|
||||
@@ -10,14 +10,23 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var testingIssue5349 bool // used during tests
|
||||
|
||||
// resolveAndDialChannel is the simple pure-Go implementation of
|
||||
// resolveAndDial, still used on operating systems where the deadline
|
||||
// hasn't been pushed down into the pollserver. (Plan 9 and some old
|
||||
// versions of Windows)
|
||||
func resolveAndDialChannel(net, addr string, localAddr Addr, deadline time.Time) (Conn, error) {
|
||||
timeout := deadline.Sub(time.Now())
|
||||
if timeout < 0 {
|
||||
timeout = 0
|
||||
var timeout time.Duration
|
||||
if !deadline.IsZero() {
|
||||
timeout = deadline.Sub(time.Now())
|
||||
}
|
||||
if timeout <= 0 {
|
||||
ra, err := resolveAddr("dial", net, addr, noDeadline)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dial(net, addr, localAddr, ra, noDeadline)
|
||||
}
|
||||
t := time.NewTimer(timeout)
|
||||
defer t.Stop()
|
||||
@@ -28,6 +37,9 @@ func resolveAndDialChannel(net, addr string, localAddr Addr, deadline time.Time)
|
||||
ch := make(chan pair, 1)
|
||||
resolvedAddr := make(chan Addr, 1)
|
||||
go func() {
|
||||
if testingIssue5349 {
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
ra, err := resolveAddr("dial", net, addr, noDeadline)
|
||||
if err != nil {
|
||||
ch <- pair{nil, err}
|
||||
|
||||
11
src/pkg/net/dial_gen_test.go
Normal file
11
src/pkg/net/dial_gen_test.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
// +build windows plan9
|
||||
|
||||
package net
|
||||
|
||||
func init() {
|
||||
testingIssue5349 = true
|
||||
}
|
||||
@@ -3032,6 +3032,25 @@ func TestSliceOf(t *testing.T) {
|
||||
checkSameType(t, Zero(SliceOf(TypeOf(T1(1)))).Interface(), []T1{})
|
||||
}
|
||||
|
||||
func TestSliceOverflow(t *testing.T) {
|
||||
// check that MakeSlice panics when size of slice overflows uint
|
||||
const S = 1e6
|
||||
s := uint(S)
|
||||
l := (1<<(unsafe.Sizeof((*byte)(nil))*8)-1)/s + 1
|
||||
if l*s >= s {
|
||||
t.Fatal("slice size does not overflow")
|
||||
}
|
||||
var x [S]byte
|
||||
st := SliceOf(TypeOf(x))
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err == nil {
|
||||
t.Fatal("slice overflow does not panic")
|
||||
}
|
||||
}()
|
||||
MakeSlice(st, int(l), int(l))
|
||||
}
|
||||
|
||||
func TestSliceOfGC(t *testing.T) {
|
||||
type T *uintptr
|
||||
tt := TypeOf(T(nil))
|
||||
|
||||
@@ -809,16 +809,27 @@ sellock(Select *sel)
|
||||
static void
|
||||
selunlock(Select *sel)
|
||||
{
|
||||
uint32 i;
|
||||
Hchan *c, *c0;
|
||||
int32 i, n, r;
|
||||
Hchan *c;
|
||||
|
||||
c = nil;
|
||||
for(i=sel->ncase; i-->0;) {
|
||||
c0 = sel->lockorder[i];
|
||||
if(c0 && c0 != c) {
|
||||
c = c0;
|
||||
runtime·unlock(c);
|
||||
}
|
||||
// We must be very careful here to not touch sel after we have unlocked
|
||||
// the last lock, because sel can be freed right after the last unlock.
|
||||
// Consider the following situation.
|
||||
// First M calls runtime·park() in runtime·selectgo() passing the sel.
|
||||
// Once runtime·park() has unlocked the last lock, another M makes
|
||||
// the G that calls select runnable again and schedules it for execution.
|
||||
// When the G runs on another M, it locks all the locks and frees sel.
|
||||
// Now if the first M touches sel, it will access freed memory.
|
||||
n = (int32)sel->ncase;
|
||||
r = 0;
|
||||
// skip the default case
|
||||
if(n>0 && sel->lockorder[0] == nil)
|
||||
r = 1;
|
||||
for(i = n-1; i >= r; i--) {
|
||||
c = sel->lockorder[i];
|
||||
if(i>0 && sel->lockorder[i-1] == c)
|
||||
continue; // will unlock it on the next iteration
|
||||
runtime·unlock(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,3 +97,55 @@ func TestGcHashmapIndirection(t *testing.T) {
|
||||
m[a] = T{}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGcArraySlice(t *testing.T) {
|
||||
type X struct {
|
||||
buf [1]byte
|
||||
nextbuf []byte
|
||||
next *X
|
||||
}
|
||||
var head *X
|
||||
for i := 0; i < 10; i++ {
|
||||
p := &X{}
|
||||
p.buf[0] = 42
|
||||
p.next = head
|
||||
if head != nil {
|
||||
p.nextbuf = head.buf[:]
|
||||
}
|
||||
head = p
|
||||
runtime.GC()
|
||||
}
|
||||
for p := head; p != nil; p = p.next {
|
||||
if p.buf[0] != 42 {
|
||||
t.Fatal("corrupted heap")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGcRescan(t *testing.T) {
|
||||
type X struct {
|
||||
c chan error
|
||||
nextx *X
|
||||
}
|
||||
type Y struct {
|
||||
X
|
||||
nexty *Y
|
||||
p *int
|
||||
}
|
||||
var head *Y
|
||||
for i := 0; i < 10; i++ {
|
||||
p := &Y{}
|
||||
p.c = make(chan error)
|
||||
p.nextx = &head.X
|
||||
p.nexty = head
|
||||
p.p = new(int)
|
||||
*p.p = 42
|
||||
head = p
|
||||
runtime.GC()
|
||||
}
|
||||
for p := head; p != nil; p = p.nexty {
|
||||
if *p.p != 42 {
|
||||
t.Fatal("corrupted heap")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -687,42 +687,14 @@ reflect·unsafe_Typeof(Eface e, Eface ret)
|
||||
void
|
||||
reflect·unsafe_New(Type *t, void *ret)
|
||||
{
|
||||
uint32 flag;
|
||||
|
||||
flag = t->kind&KindNoPointers ? FlagNoPointers : 0;
|
||||
ret = runtime·mallocgc(t->size, flag, 1, 1);
|
||||
|
||||
if(UseSpanType && !flag) {
|
||||
if(false) {
|
||||
runtime·printf("unsafe_New %S: %p\n", *t->string, ret);
|
||||
}
|
||||
runtime·settype(ret, (uintptr)t | TypeInfo_SingleObject);
|
||||
}
|
||||
|
||||
ret = runtime·cnew(t);
|
||||
FLUSH(&ret);
|
||||
}
|
||||
|
||||
void
|
||||
reflect·unsafe_NewArray(Type *t, intgo n, void *ret)
|
||||
{
|
||||
uint64 size;
|
||||
|
||||
size = n*t->size;
|
||||
if(size == 0)
|
||||
ret = (byte*)&runtime·zerobase;
|
||||
else if(t->kind&KindNoPointers)
|
||||
ret = runtime·mallocgc(size, FlagNoPointers, 1, 1);
|
||||
else {
|
||||
ret = runtime·mallocgc(size, 0, 1, 1);
|
||||
|
||||
if(UseSpanType) {
|
||||
if(false) {
|
||||
runtime·printf("unsafe_NewArray [%D]%S: %p\n", (int64)n, *t->string, ret);
|
||||
}
|
||||
runtime·settype(ret, (uintptr)t | TypeInfo_Array);
|
||||
}
|
||||
}
|
||||
|
||||
ret = runtime·cnewarray(t, n);
|
||||
FLUSH(&ret);
|
||||
}
|
||||
|
||||
|
||||
@@ -717,9 +717,8 @@ runtime·new(Type *typ, uint8 *ret)
|
||||
ret = runtime·mallocgc(typ->size, flag, 1, 1);
|
||||
|
||||
if(UseSpanType && !flag) {
|
||||
if(false) {
|
||||
if(false)
|
||||
runtime·printf("new %S: %p\n", *typ->string, ret);
|
||||
}
|
||||
runtime·settype(ret, (uintptr)typ | TypeInfo_SingleObject);
|
||||
}
|
||||
}
|
||||
@@ -727,36 +726,45 @@ runtime·new(Type *typ, uint8 *ret)
|
||||
FLUSH(&ret);
|
||||
}
|
||||
|
||||
// same as runtime·new, but callable from C
|
||||
void*
|
||||
runtime·cnew(Type *typ)
|
||||
static void*
|
||||
cnew(Type *typ, intgo n, int32 objtyp)
|
||||
{
|
||||
uint32 flag;
|
||||
void *ret;
|
||||
|
||||
if(raceenabled)
|
||||
m->racepc = runtime·getcallerpc(&typ);
|
||||
|
||||
if(typ->size == 0) {
|
||||
if((objtyp&(PtrSize-1)) != objtyp)
|
||||
runtime·throw("runtime: invalid objtyp");
|
||||
if(n < 0 || (typ->size > 0 && n > MaxMem/typ->size))
|
||||
runtime·panicstring("runtime: allocation size out of range");
|
||||
if(typ->size == 0 || n == 0) {
|
||||
// All 0-length allocations use this pointer.
|
||||
// The language does not require the allocations to
|
||||
// have distinct values.
|
||||
ret = (uint8*)&runtime·zerobase;
|
||||
} else {
|
||||
flag = typ->kind&KindNoPointers ? FlagNoPointers : 0;
|
||||
ret = runtime·mallocgc(typ->size, flag, 1, 1);
|
||||
|
||||
if(UseSpanType && !flag) {
|
||||
if(false) {
|
||||
runtime·printf("new %S: %p\n", *typ->string, ret);
|
||||
}
|
||||
runtime·settype(ret, (uintptr)typ | TypeInfo_SingleObject);
|
||||
}
|
||||
return &runtime·zerobase;
|
||||
}
|
||||
flag = typ->kind&KindNoPointers ? FlagNoPointers : 0;
|
||||
ret = runtime·mallocgc(typ->size*n, flag, 1, 1);
|
||||
if(UseSpanType && !flag) {
|
||||
if(false)
|
||||
runtime·printf("cnew [%D]%S: %p\n", (int64)n, *typ->string, ret);
|
||||
runtime·settype(ret, (uintptr)typ | objtyp);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// same as runtime·new, but callable from C
|
||||
void*
|
||||
runtime·cnew(Type *typ)
|
||||
{
|
||||
return cnew(typ, 1, TypeInfo_SingleObject);
|
||||
}
|
||||
|
||||
void*
|
||||
runtime·cnewarray(Type *typ, intgo n)
|
||||
{
|
||||
return cnew(typ, n, TypeInfo_Array);
|
||||
}
|
||||
|
||||
func GC() {
|
||||
runtime·gc(1);
|
||||
}
|
||||
|
||||
@@ -461,6 +461,7 @@ bool runtime·blockspecial(void*);
|
||||
void runtime·setblockspecial(void*, bool);
|
||||
void runtime·purgecachedstats(MCache*);
|
||||
void* runtime·cnew(Type*);
|
||||
void* runtime·cnewarray(Type*, intgo);
|
||||
|
||||
void runtime·settype(void*, uintptr);
|
||||
void runtime·settype_flush(M*, bool);
|
||||
|
||||
@@ -623,7 +623,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
|
||||
byte *b, *arena_start, *arena_used;
|
||||
uintptr n, i, end_b, elemsize, size, ti, objti, count, type;
|
||||
uintptr *pc, precise_type, nominal_size;
|
||||
uintptr *map_ret, mapkey_size, mapval_size, mapkey_ti, mapval_ti, *chan_ret;
|
||||
uintptr *map_ret, mapkey_size, mapval_size, mapkey_ti, mapval_ti, *chan_ret, chancap;
|
||||
void *obj;
|
||||
Type *t;
|
||||
Slice *sliceptr;
|
||||
@@ -799,7 +799,11 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
|
||||
sliceptr = (Slice*)(stack_top.b + pc[1]);
|
||||
if(sliceptr->cap != 0) {
|
||||
obj = sliceptr->array;
|
||||
objti = pc[2] | PRECISE | LOOP;
|
||||
// Can't use slice element type for scanning,
|
||||
// because if it points to an array embedded
|
||||
// in the beginning of a struct,
|
||||
// we will scan the whole struct as the slice.
|
||||
// So just obtain type info from heap.
|
||||
}
|
||||
pc += 3;
|
||||
break;
|
||||
@@ -1058,13 +1062,13 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
|
||||
if(!(chantype->elem->kind & KindNoPointers)) {
|
||||
// Channel's buffer follows Hchan immediately in memory.
|
||||
// Size of buffer (cap(c)) is second int in the chan struct.
|
||||
n = ((uintgo*)chan)[1];
|
||||
if(n > 0) {
|
||||
chancap = ((uintgo*)chan)[1];
|
||||
if(chancap > 0) {
|
||||
// TODO(atom): split into two chunks so that only the
|
||||
// in-use part of the circular buffer is scanned.
|
||||
// (Channel routines zero the unused part, so the current
|
||||
// code does not lead to leaks, it's just a little inefficient.)
|
||||
*objbufpos++ = (Obj){(byte*)chan+runtime·Hchansize, n*chantype->elem->size,
|
||||
*objbufpos++ = (Obj){(byte*)chan+runtime·Hchansize, chancap*chantype->elem->size,
|
||||
(uintptr)chantype->elem->gc | PRECISE | LOOP};
|
||||
if(objbufpos == objbuf_end)
|
||||
flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj);
|
||||
|
||||
@@ -1232,6 +1232,7 @@ static void
|
||||
goexit0(G *gp)
|
||||
{
|
||||
gp->status = Gdead;
|
||||
gp->fnstart = nil;
|
||||
gp->m = nil;
|
||||
gp->lockedm = nil;
|
||||
m->curg = nil;
|
||||
@@ -1370,6 +1371,8 @@ runtime·exitsyscall(void)
|
||||
runtime·unlock(&runtime·sched);
|
||||
if(p) {
|
||||
acquirep(p);
|
||||
m->p->tick++;
|
||||
g->status = Grunning;
|
||||
g->gcstack = (uintptr)nil;
|
||||
g->gcsp = (uintptr)nil;
|
||||
return;
|
||||
|
||||
9
src/pkg/runtime/race/doc.go
Normal file
9
src/pkg/runtime/race/doc.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright 2013 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 race implements data race detection logic.
|
||||
// No public interface is provided.
|
||||
// For details about the race detector see
|
||||
// http://golang.org/doc/articles/race_detector.html
|
||||
package race
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
// +build race,linux,amd64 race,darwin,amd64 race,windows,amd64
|
||||
|
||||
// Package race provides low-level facilities for data race detection.
|
||||
package race
|
||||
|
||||
/*
|
||||
|
||||
@@ -48,27 +48,9 @@ uintptr runtime·zerobase;
|
||||
static void
|
||||
makeslice1(SliceType *t, intgo len, intgo cap, Slice *ret)
|
||||
{
|
||||
uintptr size;
|
||||
|
||||
size = cap*t->elem->size;
|
||||
|
||||
ret->len = len;
|
||||
ret->cap = cap;
|
||||
|
||||
if(size == 0)
|
||||
ret->array = (byte*)&runtime·zerobase;
|
||||
else if((t->elem->kind&KindNoPointers))
|
||||
ret->array = runtime·mallocgc(size, FlagNoPointers, 1, 1);
|
||||
else {
|
||||
ret->array = runtime·mallocgc(size, 0, 1, 1);
|
||||
|
||||
if(UseSpanType) {
|
||||
if(false) {
|
||||
runtime·printf("new slice [%D]%S: %p\n", (int64)cap, *t->elem->string, ret->array);
|
||||
}
|
||||
runtime·settype(ret->array, (uintptr)t->elem | TypeInfo_Array);
|
||||
}
|
||||
}
|
||||
ret->array = runtime·cnewarray(t->elem, cap);
|
||||
}
|
||||
|
||||
// appendslice(type *Type, x, y, []T) []T
|
||||
|
||||
@@ -131,6 +131,11 @@ runtime·deltimer(Timer *t)
|
||||
{
|
||||
int32 i;
|
||||
|
||||
// Dereference t so that any panic happens before the lock is held.
|
||||
// Discard result, because t might be moving in the heap.
|
||||
i = t->i;
|
||||
USED(i);
|
||||
|
||||
runtime·lock(&timers);
|
||||
|
||||
// t may not be registered anymore and may have
|
||||
|
||||
41
src/pkg/syscall/rlimit_linux_test.go
Normal file
41
src/pkg/syscall/rlimit_linux_test.go
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2013 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 syscall_test
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRlimit(t *testing.T) {
|
||||
var rlimit, zero syscall.Rlimit
|
||||
err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimit)
|
||||
if err != nil {
|
||||
t.Fatalf("Getrlimit: save failed: %v", err)
|
||||
}
|
||||
if zero == rlimit {
|
||||
t.Fatalf("Getrlimit: save failed: got zero value %#v", rlimit)
|
||||
}
|
||||
set := rlimit
|
||||
set.Cur = set.Max - 1
|
||||
err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &set)
|
||||
if err != nil {
|
||||
t.Fatalf("Setrlimit: set failed: %#v %v", set, err)
|
||||
}
|
||||
var get syscall.Rlimit
|
||||
err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &get)
|
||||
if err != nil {
|
||||
t.Fatalf("Getrlimit: get failed: %v", err)
|
||||
}
|
||||
set = rlimit
|
||||
set.Cur = set.Max - 1
|
||||
if set != get {
|
||||
t.Fatalf("Rlimit: change failed: wanted %#v got %#v", set, get)
|
||||
}
|
||||
err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rlimit)
|
||||
if err != nil {
|
||||
t.Fatalf("Setrlimit: restore failed: %#v %v", rlimit, err)
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ const rlimInf32 = ^uint32(0)
|
||||
const rlimInf64 = ^uint64(0)
|
||||
|
||||
func Getrlimit(resource int, rlim *Rlimit) (err error) {
|
||||
err = prlimit(0, resource, rlim, nil)
|
||||
err = prlimit(0, resource, nil, rlim)
|
||||
if err != ENOSYS {
|
||||
return err
|
||||
}
|
||||
@@ -106,7 +106,7 @@ func Getrlimit(resource int, rlim *Rlimit) (err error) {
|
||||
//sysnb setrlimit(resource int, rlim *rlimit32) (err error) = SYS_SETRLIMIT
|
||||
|
||||
func Setrlimit(resource int, rlim *Rlimit) (err error) {
|
||||
err = prlimit(0, resource, nil, rlim)
|
||||
err = prlimit(0, resource, rlim, nil)
|
||||
if err != ENOSYS {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ const rlimInf32 = ^uint32(0)
|
||||
const rlimInf64 = ^uint64(0)
|
||||
|
||||
func Getrlimit(resource int, rlim *Rlimit) (err error) {
|
||||
err = prlimit(0, resource, rlim, nil)
|
||||
err = prlimit(0, resource, nil, rlim)
|
||||
if err != ENOSYS {
|
||||
return err
|
||||
}
|
||||
@@ -147,7 +147,7 @@ func Getrlimit(resource int, rlim *Rlimit) (err error) {
|
||||
//sysnb setrlimit(resource int, rlim *rlimit32) (err error) = SYS_SETRLIMIT
|
||||
|
||||
func Setrlimit(resource int, rlim *Rlimit) (err error) {
|
||||
err = prlimit(0, resource, nil, rlim)
|
||||
err = prlimit(0, resource, rlim, nil)
|
||||
if err != ENOSYS {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -314,3 +314,23 @@ func TestOverflowSleep(t *testing.T) {
|
||||
t.Fatalf("negative timeout didn't fire")
|
||||
}
|
||||
}
|
||||
|
||||
// Test that a panic while deleting a timer does not leave
|
||||
// the timers mutex held, deadlocking a ticker.Stop in a defer.
|
||||
func TestIssue5745(t *testing.T) {
|
||||
ticker := NewTicker(Hour)
|
||||
defer func() {
|
||||
// would deadlock here before the fix due to
|
||||
// lock taken before the segfault.
|
||||
ticker.Stop()
|
||||
|
||||
if r := recover(); r == nil {
|
||||
t.Error("Expected panic, but none happened.")
|
||||
}
|
||||
}()
|
||||
|
||||
// cause a panic due to a segfault
|
||||
var timer *Timer
|
||||
timer.Stop()
|
||||
t.Error("Should be unreachable.")
|
||||
}
|
||||
|
||||
@@ -59,3 +59,18 @@ func F7() int {
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func F8() int {
|
||||
if a := (&T{1, 1}); a != nil {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func F9() int {
|
||||
var a *T
|
||||
if a = (&T{1, 1}); a != nil {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import "./a"
|
||||
func main() {
|
||||
for _, f := range []func() int{
|
||||
a.F1, a.F2, a.F3, a.F4,
|
||||
a.F5, a.F6, a.F7} {
|
||||
a.F5, a.F6, a.F7, a.F8, a.F9} {
|
||||
if f() > 1 {
|
||||
panic("f() > 1")
|
||||
}
|
||||
|
||||
18
test/fixedbugs/issue5244.go
Normal file
18
test/fixedbugs/issue5244.go
Normal file
@@ -0,0 +1,18 @@
|
||||
// run
|
||||
|
||||
// Copyright 2013 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.
|
||||
|
||||
// Issue 5244: the init order computation uses the wrong
|
||||
// order for top-level blank identifier assignments.
|
||||
// The example used to panic because it tries calling a
|
||||
// nil function instead of assigning to f before.
|
||||
|
||||
package main
|
||||
|
||||
var f = func() int { return 1 }
|
||||
var _ = f() + g()
|
||||
var g = func() int { return 2 }
|
||||
|
||||
func main() {}
|
||||
27
test/fixedbugs/issue5470.dir/a.go
Normal file
27
test/fixedbugs/issue5470.dir/a.go
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright 2013 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 a
|
||||
|
||||
type Foo interface {
|
||||
Hi() string
|
||||
}
|
||||
|
||||
func Test1() Foo { return make(tst1) }
|
||||
|
||||
type tst1 map[string]bool
|
||||
|
||||
func (r tst1) Hi() string { return "Hi!" }
|
||||
|
||||
func Test2() Foo { return make(tst2, 0) }
|
||||
|
||||
type tst2 []string
|
||||
|
||||
func (r tst2) Hi() string { return "Hi!" }
|
||||
|
||||
func Test3() Foo { return make(tst3) }
|
||||
|
||||
type tst3 chan string
|
||||
|
||||
func (r tst3) Hi() string { return "Hi!" }
|
||||
13
test/fixedbugs/issue5470.dir/b.go
Normal file
13
test/fixedbugs/issue5470.dir/b.go
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2013 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 b
|
||||
|
||||
import "./a"
|
||||
|
||||
func main() {
|
||||
a.Test1()
|
||||
a.Test2()
|
||||
a.Test3()
|
||||
}
|
||||
10
test/fixedbugs/issue5470.go
Normal file
10
test/fixedbugs/issue5470.go
Normal file
@@ -0,0 +1,10 @@
|
||||
// compiledir
|
||||
|
||||
// Copyright 2013 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.
|
||||
|
||||
// Issue 5470: exported data for inlining may miss
|
||||
// the type argument of make.
|
||||
|
||||
package ignored
|
||||
58
test/fixedbugs/issue5493.go
Normal file
58
test/fixedbugs/issue5493.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// run
|
||||
|
||||
// Copyright 2013 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 (
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
const N = 10
|
||||
var count int64
|
||||
|
||||
func run() error {
|
||||
f1 := func() {}
|
||||
f2 := func() {
|
||||
func() {
|
||||
f1()
|
||||
}()
|
||||
}
|
||||
runtime.SetFinalizer(&f1, func(f *func()) {
|
||||
atomic.AddInt64(&count, -1)
|
||||
})
|
||||
go f2()
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Does not work on 32-bits due to partially conservative GC.
|
||||
// Try to enable when we have fully precise GC.
|
||||
if runtime.GOARCH != "amd64" {
|
||||
return
|
||||
}
|
||||
count = N
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(N)
|
||||
for i := 0; i < N; i++ {
|
||||
go func() {
|
||||
run()
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
for i := 0; i < 2*N; i++ {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
runtime.GC()
|
||||
}
|
||||
if count != 0 {
|
||||
println(count, "out of", N, "finalizer are called")
|
||||
panic("not all finalizers are called")
|
||||
}
|
||||
}
|
||||
|
||||
34
test/fixedbugs/issue5515.go
Normal file
34
test/fixedbugs/issue5515.go
Normal file
@@ -0,0 +1,34 @@
|
||||
// run
|
||||
|
||||
// Copyright 2013 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.
|
||||
|
||||
// issue 5515: miscompilation doing inlining in generated method wrapper
|
||||
|
||||
package main
|
||||
|
||||
type T uint32
|
||||
|
||||
func main() {
|
||||
b := make([]T, 8)
|
||||
b[0] = 0xdeadbeef
|
||||
rs := Slice(b)
|
||||
sort(rs)
|
||||
}
|
||||
|
||||
type Slice []T
|
||||
|
||||
func (s Slice) Swap(i, j int) {
|
||||
tmp := s[i]
|
||||
s[i] = s[j]
|
||||
s[j] = tmp
|
||||
}
|
||||
|
||||
type Interface interface {
|
||||
Swap(i, j int)
|
||||
}
|
||||
|
||||
func sort(data Interface) {
|
||||
data.Swap(0, 4)
|
||||
}
|
||||
36
test/fixedbugs/issue5607.go
Normal file
36
test/fixedbugs/issue5607.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// run
|
||||
|
||||
// Copyright 2013 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.
|
||||
|
||||
// Issue 5607: generation of init() function incorrectly
|
||||
// uses initializers of blank variables inside closures.
|
||||
|
||||
package main
|
||||
|
||||
var Test = func() {
|
||||
var mymap = map[string]string{"a": "b"}
|
||||
|
||||
var innerTest = func() {
|
||||
// Used to crash trying to compile this line as
|
||||
// part of init() (funcdepth mismatch).
|
||||
var _, x = mymap["a"]
|
||||
println(x)
|
||||
}
|
||||
innerTest()
|
||||
}
|
||||
|
||||
var Test2 = func() {
|
||||
// The following initializer should not be part of init()
|
||||
// The compiler used to generate a call to Panic() in init().
|
||||
var _, x = Panic()
|
||||
_ = x
|
||||
}
|
||||
|
||||
func Panic() (int, int) {
|
||||
panic("omg")
|
||||
return 1, 2
|
||||
}
|
||||
|
||||
func main() {}
|
||||
16
test/fixedbugs/issue5614.dir/rethinkgo.go
Normal file
16
test/fixedbugs/issue5614.dir/rethinkgo.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package rethinkgo
|
||||
|
||||
type Session struct {
|
||||
}
|
||||
|
||||
func (s *Session) Run(query Exp) *int { return nil }
|
||||
|
||||
type List []interface{}
|
||||
|
||||
type Exp struct {
|
||||
args []interface{}
|
||||
}
|
||||
|
||||
func (e Exp) UseOutdated(useOutdated bool) Exp {
|
||||
return Exp{args: List{e, useOutdated}}
|
||||
}
|
||||
7
test/fixedbugs/issue5614.dir/x.go
Normal file
7
test/fixedbugs/issue5614.dir/x.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package x
|
||||
|
||||
import "./rethinkgo"
|
||||
|
||||
var S *rethinkgo.Session
|
||||
|
||||
|
||||
5
test/fixedbugs/issue5614.dir/y.go
Normal file
5
test/fixedbugs/issue5614.dir/y.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package y
|
||||
|
||||
import "./x"
|
||||
|
||||
var T = x.S
|
||||
11
test/fixedbugs/issue5614.go
Normal file
11
test/fixedbugs/issue5614.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// compiledir
|
||||
|
||||
// Copyright 2013 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.
|
||||
|
||||
// Issue 5614: exported data for inlining may miss
|
||||
// named types when used in implicit conversion to
|
||||
// their underlying type.
|
||||
|
||||
package ignored
|
||||
29
test/fixedbugs/issue5753.go
Normal file
29
test/fixedbugs/issue5753.go
Normal file
@@ -0,0 +1,29 @@
|
||||
// run
|
||||
|
||||
// Copyright 2013 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.
|
||||
|
||||
// issue 5753: bad typecheck info causes escape analysis to
|
||||
// not run on method thunks.
|
||||
|
||||
package main
|
||||
|
||||
type Thing struct{}
|
||||
|
||||
func (t *Thing) broken(s string) []string {
|
||||
foo := [1]string{s}
|
||||
return foo[:]
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := &Thing{}
|
||||
|
||||
f := t.broken
|
||||
s := f("foo")
|
||||
_ = f("bar")
|
||||
if s[0] != "foo" {
|
||||
panic(`s[0] != "foo"`)
|
||||
}
|
||||
|
||||
}
|
||||
60
test/fixedbugs/issue5755.dir/a.go
Normal file
60
test/fixedbugs/issue5755.dir/a.go
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright 2013 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 a
|
||||
|
||||
type I interface {
|
||||
F()
|
||||
}
|
||||
|
||||
type foo1 []byte
|
||||
type foo2 []rune
|
||||
type foo3 []uint8
|
||||
type foo4 []int32
|
||||
type foo5 string
|
||||
type foo6 string
|
||||
type foo7 string
|
||||
type foo8 string
|
||||
type foo9 string
|
||||
|
||||
func (f foo1) F() { return }
|
||||
func (f foo2) F() { return }
|
||||
func (f foo3) F() { return }
|
||||
func (f foo4) F() { return }
|
||||
func (f foo5) F() { return }
|
||||
func (f foo6) F() { return }
|
||||
func (f foo7) F() { return }
|
||||
func (f foo8) F() { return }
|
||||
func (f foo9) F() { return }
|
||||
|
||||
func Test1(s string) I { return foo1(s) }
|
||||
func Test2(s string) I { return foo2(s) }
|
||||
func Test3(s string) I { return foo3(s) }
|
||||
func Test4(s string) I { return foo4(s) }
|
||||
func Test5(s []byte) I { return foo5(s) }
|
||||
func Test6(s []rune) I { return foo6(s) }
|
||||
func Test7(s []uint8) I { return foo7(s) }
|
||||
func Test8(s []int32) I { return foo8(s) }
|
||||
func Test9(s int) I { return foo9(s) }
|
||||
|
||||
type bar map[int]int
|
||||
|
||||
func (b bar) F() { return }
|
||||
|
||||
func TestBar() I { return bar{1: 2} }
|
||||
|
||||
type baz int
|
||||
|
||||
func IsBaz(x interface{}) bool { _, ok := x.(baz); return ok }
|
||||
|
||||
type baz2 int
|
||||
|
||||
func IsBaz2(x interface{}) bool {
|
||||
switch x.(type) {
|
||||
case baz2:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
23
test/fixedbugs/issue5755.dir/main.go
Normal file
23
test/fixedbugs/issue5755.dir/main.go
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2013 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 "./a"
|
||||
|
||||
func main() {
|
||||
a.Test1("frumious")
|
||||
a.Test2("frumious")
|
||||
a.Test3("frumious")
|
||||
a.Test4("frumious")
|
||||
|
||||
a.Test5(nil)
|
||||
a.Test6(nil)
|
||||
a.Test7(nil)
|
||||
a.Test8(nil)
|
||||
a.Test9(0)
|
||||
|
||||
a.TestBar()
|
||||
a.IsBaz(nil)
|
||||
}
|
||||
10
test/fixedbugs/issue5755.go
Normal file
10
test/fixedbugs/issue5755.go
Normal file
@@ -0,0 +1,10 @@
|
||||
// compiledir
|
||||
|
||||
// Copyright 2013 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.
|
||||
|
||||
// Issue 5755: exported data for inlining may miss
|
||||
// named types when used in string conversions.
|
||||
|
||||
package ignored
|
||||
27
test/fixedbugs/issue5809.go
Normal file
27
test/fixedbugs/issue5809.go
Normal file
@@ -0,0 +1,27 @@
|
||||
// run
|
||||
|
||||
// Copyright 2013 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.
|
||||
|
||||
// issue 5809: 6g and 8g attempted to constant propagate indexed LEA
|
||||
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
const d16 = "0123456789ABCDEF"
|
||||
k := 0x1234
|
||||
var x [4]byte
|
||||
|
||||
x[0] = d16[k>>12&0xf]
|
||||
x[1] = d16[k>>8&0xf]
|
||||
x[2] = d16[k>>4&0xf]
|
||||
x[3] = d16[k&0xf]
|
||||
|
||||
if x != [4]byte{'1','2','3','4'} {
|
||||
fmt.Println(x)
|
||||
panic("x != [4]byte{'1','2','3','4'}")
|
||||
}
|
||||
}
|
||||
18
test/fixedbugs/issue5820.go
Normal file
18
test/fixedbugs/issue5820.go
Normal file
@@ -0,0 +1,18 @@
|
||||
// run
|
||||
|
||||
// Copyright 2013 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.
|
||||
|
||||
// issue 5820: register clobber when clearfat and 64 bit arithmetic is interleaved.
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
array := make([][]int, 2)
|
||||
index := uint64(1)
|
||||
array[index] = nil
|
||||
if array[1] != nil {
|
||||
panic("array[1] != nil")
|
||||
}
|
||||
}
|
||||
16
test/fixedbugs/issue5841.go
Normal file
16
test/fixedbugs/issue5841.go
Normal file
@@ -0,0 +1,16 @@
|
||||
// build
|
||||
|
||||
// Copyright 2013 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.
|
||||
|
||||
// Issue 5841: 8g produces invalid CMPL $0, $0.
|
||||
// Similar to issue 5002, used to fail at link time.
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var y int
|
||||
if y%1 == 0 {
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user