Compare commits

...

11 Commits

Author SHA1 Message Date
Andrew Gerrand
205f850cea go1.1 2013-05-13 13:03:09 -07:00
Andrew Gerrand
6c7631126f [release-branch.go1.1] runtime/race: improve public documentation
««« CL 9144050 / d29da2ced72b
runtime/race: improve public documentation
Move the documentation from race.go to doc.go, because
race.go uses +build race, so it's not normally parsed by go doc.
Rephrase the documentation for end users, provide link to race
detector manual.
Fixes #5444.

R=golang-dev, minux.ma, adg, r
CC=golang-dev
https://golang.org/cl/9144050
»»»

R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/9387043
2013-05-13 09:35:18 -07:00
Andrew Gerrand
9294a08f1b [release-branch.go1.1] doc: add a "New packages" section to the 1.1 release notes.
««« CL 9344044 / 880991592ded
doc: add a "New packages" section to the 1.1 release notes.

R=adg, r
CC=golang-dev
https://golang.org/cl/9344044
»»»

R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/9386043
2013-05-13 09:24:34 -07:00
Andrew Gerrand
46a6097aa7 go1.1rc3
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/9315044
2013-05-08 16:04:56 -07:00
Andrew Gerrand
91504a0e0d [release-branch.go1.1] runtime: fix crash in select
««« CL 9311043 / 53bc96b4c0c7
runtime: fix crash in select
runtime.park() can access freed select descriptor
due to a racing free in another thread.
See the comment for details.

Slightly modified version of dvyukov's CL 9259045.

No test yet.  Before this CL, the test described in issue 5422
would fail about every 40 times for me.  With this CL, I ran
the test 5900 times with no failures.

Fixes #5422.

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/9311043
»»»

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/9304044
2013-05-08 16:02:59 -07:00
Andrew Gerrand
836b670612 [release-branch.go1.1] spec: fix incorrect example
««« CL 9305043 / 87762a7629b4
spec: fix incorrect example

Fixes #5430.

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/9305043
»»»

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/9308044
2013-05-08 16:02:02 -07:00
Andrew Gerrand
0a98e78c1f [release-branch.go1.1] doc: pull front page featured articles using new blog JSON feed
««« CL 9288045 / 5785ebd7acfb
doc: pull front page featured articles using new blog JSON feed

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/9288045
»»»

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/9050046
2013-05-08 16:01:09 -07:00
Andrew Gerrand
c785d6af66 [release-branch.go1.1] cmd/cgo: pass -Wsystem-headers when looking for errors
««« CL 9120045 / e4f62df3e6c9
cmd/cgo: pass -Wsystem-headers when looking for errors

This works around a bug in GCC 4.8.0.

Fixes #5118.

R=golang-dev, r, minux.ma
CC=golang-dev
https://golang.org/cl/9120045
»»»

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/9259047
2013-05-08 16:00:15 -07:00
Andrew Gerrand
99aa2da7ea [release-branch.go1.1] net: fix dial race on plan9 and windows
««« CL 9159043 / f1ddc3ce3dfe
net: fix dial race on plan9 and windows

Fixes #5349.

R=golang-dev, lucio.dere, dsymonds, bradfitz, iant, adg, dave, r
CC=golang-dev
https://golang.org/cl/9159043
»»»

R=golang-dev
CC=golang-dev
https://golang.org/cl/9315043
2013-05-08 15:58:43 -07:00
Andrew Gerrand
0f1a18b773 [release-branch.go1.1] effective_go.html: be more accepting in the guidelines for interface names
««« CL 9274043 / bbe324079abe
effective_go.html: be more accepting in the guidelines for interface names
Fixes #5421.

R=golang-dev, bradfitz, adg
CC=golang-dev
https://golang.org/cl/9274043
»»»

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/9137045
2013-05-08 15:57:44 -07:00
Andrew Gerrand
1c5438aae8 go1.1rc2 2013-05-06 17:33:44 -07:00
19 changed files with 122 additions and 1833 deletions

1
VERSION Normal file
View File

@@ -0,0 +1 @@
go1.1

View File

@@ -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>

View File

@@ -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

View File

@@ -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 &lt;&lt; 3.0 // d == 8 (untyped integer constant)
const e = 1.0 &lt;&lt; 3 // e == 8 (untyped integer constant)
const f = int32(1) &lt;&lt; 33 // f == 0 (type int32)
const f = int32(1) &lt;&lt; 33 // illegal (constant 8589934592 overflows int32)
const g = float64(2) &gt;&gt; 1 // illegal (float64(2) is a typed floating-point constant)
const h = "foo" &gt; "bar" // h == true (untyped boolean constant)
const j = true // j == true (untyped boolean constant)

View File

@@ -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>

View File

@@ -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)

View 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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;
}
}
}

View File

@@ -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*);

View 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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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}

View 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
}

View File

@@ -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);
}
}

View 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

View File

@@ -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
/*