Commit 6e2ae0a1 authored by Russ Cox's avatar Russ Cox

runtime/pprof: support OS X CPU profiling

Work around profiling kernel bug with signal masks.
Still broken on 64-bit Snow Leopard kernel,
but I think we can ignore that one and let people
upgrade to Lion.

Add new trivial tools addr2line and objdump to take
the place of the GNU tools of the same name, since
those are not installed on OS X.

Adapt pprof to invoke 'go tool addr2line' and
'go tool objdump' if the system tools do not exist.

Clean up disassembly of base register on amd64.

Fixes #2008.

R=golang-dev, bradfitz, mikioh.mikioh, r, iant
CC=golang-dev
https://golang.org/cl/5697066
parent c10f5085
#! /usr/bin/env perl #! /usr/bin/env perl
# This is a copy of http://google-perftools.googlecode.com/svn/trunk/src/pprof # This is a copy of http://google-perftools.googlecode.com/svn/trunk/src/pprof
# with local modifications to handle generation of SVG images and # with local modifications to handle generation of SVG images and
# the Go-style pprof paths. These modifications will probably filter # the Go-style pprof paths. These modifications will probably filter
# back into the official source before long. # back into the official source before long.
# It's convenient to have a copy here because we need just the one # It's convenient to have a copy here because we need just the one
...@@ -9,11 +9,11 @@ ...@@ -9,11 +9,11 @@
# Copyright (c) 1998-2007, Google Inc. # Copyright (c) 1998-2007, Google Inc.
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are # modification, are permitted provided that the following conditions are
# met: # met:
# #
# * Redistributions of source code must retain the above copyright # * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer. # notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above # * Redistributions in binary form must reproduce the above
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
# * Neither the name of Google Inc. nor the names of its # * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from # contributors may be used to endorse or promote products derived from
# this software without specific prior written permission. # this software without specific prior written permission.
# #
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
...@@ -1234,6 +1234,13 @@ sub Disassemble { ...@@ -1234,6 +1234,13 @@ sub Disassemble {
my $cmd = sprintf("$objdump -C -d -l --no-show-raw-insn " . my $cmd = sprintf("$objdump -C -d -l --no-show-raw-insn " .
"--start-address=0x$start_addr " . "--start-address=0x$start_addr " .
"--stop-address=0x$end_addr $prog"); "--stop-address=0x$end_addr $prog");
if (system("$objdump --help >/dev/null 2>&1") != 0) {
# objdump must not exist. Fall back to go tool objdump.
$objdump = "go tool objdump";
$cmd = "$objdump $prog 0x$start_addr 0x$end_addr";
}
open(OBJDUMP, "$cmd |") || error("$objdump: $!\n"); open(OBJDUMP, "$cmd |") || error("$objdump: $!\n");
my @result = (); my @result = ();
my $filename = ""; my $filename = "";
...@@ -1305,10 +1312,10 @@ sub PrintListing { ...@@ -1305,10 +1312,10 @@ sub PrintListing {
my $cumulative = shift; my $cumulative = shift;
my $list_opts = shift; my $list_opts = shift;
my $html = shift; my $html = shift;
my $output = \*STDOUT; my $output = \*STDOUT;
my $fname = ""; my $fname = "";
if ($html) { if ($html) {
# Arrange to write the output to a temporary file # Arrange to write the output to a temporary file
...@@ -1323,7 +1330,7 @@ sub PrintListing { ...@@ -1323,7 +1330,7 @@ sub PrintListing {
printf $output ("<div class=\"legend\">%s<br>Total: %s %s</div>\n", printf $output ("<div class=\"legend\">%s<br>Total: %s %s</div>\n",
$main::prog, Unparse($total), Units()); $main::prog, Unparse($total), Units());
} }
my $listed = 0; my $listed = 0;
foreach my $lib (@{$libs}) { foreach my $lib (@{$libs}) {
my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts); my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts);
...@@ -2221,7 +2228,7 @@ function handleMouseWheel(evt) { ...@@ -2221,7 +2228,7 @@ function handleMouseWheel(evt) {
z = 0.1; z = 0.1;
if(z > 10.0) if(z > 10.0)
z = 10.0; z = 10.0;
var g = svgDoc.getElementById("viewport"); var g = svgDoc.getElementById("viewport");
var p = getEventPoint(evt); var p = getEventPoint(evt);
...@@ -4391,6 +4398,12 @@ sub MapToSymbols { ...@@ -4391,6 +4398,12 @@ sub MapToSymbols {
$cmd = "$addr2line --demangle -f -C -e $image"; $cmd = "$addr2line --demangle -f -C -e $image";
} }
if (system("$addr2line --help >/dev/null 2>&1") != 0) {
# addr2line must not exist. Fall back to go tool addr2line.
$addr2line = "go tool addr2line";
$cmd = "$addr2line $image";
}
# If "addr2line" isn't installed on the system at all, just use # If "addr2line" isn't installed on the system at all, just use
# nm to get what info we can (function names, but not line numbers). # nm to get what info we can (function names, but not line numbers).
if (system("$addr2line --help >/dev/null 2>&1") != 0) { if (system("$addr2line --help >/dev/null 2>&1") != 0) {
...@@ -4434,7 +4447,7 @@ sub MapToSymbols { ...@@ -4434,7 +4447,7 @@ sub MapToSymbols {
if ($debug) { if ($debug) {
print("----\n"); print("----\n");
system("cat $main::tmpfile_sym"); system("cat $main::tmpfile_sym");
print("----\n"); print("---- $cmd\n");
system("$cmd <$main::tmpfile_sym"); system("$cmd <$main::tmpfile_sym");
print("----\n"); print("----\n");
} }
...@@ -4544,7 +4557,7 @@ sub ShortFunctionName { ...@@ -4544,7 +4557,7 @@ sub ShortFunctionName {
# Trim overly long symbols found in disassembler output # Trim overly long symbols found in disassembler output
sub CleanDisassembly { sub CleanDisassembly {
my $d = shift; my $d = shift;
while ($d =~ s/(?<!\.)\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax) while ($d =~ s/(?<!\.)\([^()%A-Z]*\)(\s*const)?//g) { } # Argument types, not (%rax)
while ($d =~ s/(\w+)<[^<>]*>/$1/g) { } # Remove template arguments while ($d =~ s/(\w+)<[^<>]*>/$1/g) { } # Remove template arguments
return $d; return $d;
} }
...@@ -4625,7 +4638,7 @@ sub ConfigureTool { ...@@ -4625,7 +4638,7 @@ sub ConfigureTool {
my $dirname = $`; # this is everything up to and including the last slash my $dirname = $`; # this is everything up to and including the last slash
if (-x "$dirname$tool") { if (-x "$dirname$tool") {
$path = "$dirname$tool"; $path = "$dirname$tool";
} else { } else {
$path = $tool; $path = $tool;
} }
} }
......
// 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.
/*
* addr2line simulation - only enough to make pprof work on Macs
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
void
usage(void)
{
fprint(2, "usage: addr2line binary\n");
fprint(2, "reads addresses from standard input and writes two lines for each:\n");
fprint(2, "\tfunction name\n");
fprint(2, "\tfile:line\n");
exits("usage");
}
void
main(int argc, char **argv)
{
int fd;
char *p;
uvlong pc;
Symbol s;
Fhdr fhdr;
Biobuf bin, bout;
char file[1024];
ARGBEGIN{
default:
usage();
}ARGEND
if(argc != 1)
usage();
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");
Binit(&bin, 0, OREAD);
Binit(&bout, 1, OWRITE);
for(;;) {
p = Brdline(&bin, '\n');
if(p == nil)
break;
p[Blinelen(&bin)-1] = '\0';
pc = strtoull(p, 0, 16);
if(!findsym(pc, CTEXT, &s))
s.name = "??";
if(!fileline(file, sizeof file, pc))
strcpy(file, "??:0");
Bprint(&bout, "%s\n%s\n", s.name, file);
}
Bflush(&bout);
exits(0);
}
...@@ -59,7 +59,7 @@ int ...@@ -59,7 +59,7 @@ int
find(char *p, char **l, int n) find(char *p, char **l, int n)
{ {
int i; int i;
for(i=0; i<n; i++) for(i=0; i<n; i++)
if(streq(p, l[i])) if(streq(p, l[i]))
return i; return i;
...@@ -73,7 +73,7 @@ init(void) ...@@ -73,7 +73,7 @@ init(void)
char *p; char *p;
int i; int i;
Buf b; Buf b;
binit(&b); binit(&b);
xgetenv(&b, "GOROOT"); xgetenv(&b, "GOROOT");
...@@ -126,7 +126,7 @@ init(void) ...@@ -126,7 +126,7 @@ init(void)
xsetenv("GOROOT", goroot); xsetenv("GOROOT", goroot);
xsetenv("GOARCH", goarch); xsetenv("GOARCH", goarch);
xsetenv("GOOS", goos); xsetenv("GOOS", goos);
// Make the environment more predictable. // Make the environment more predictable.
xsetenv("LANG", "C"); xsetenv("LANG", "C");
xsetenv("LANGUAGE", "en_US.UTF8"); xsetenv("LANGUAGE", "en_US.UTF8");
...@@ -170,13 +170,13 @@ findgoversion(void) ...@@ -170,13 +170,13 @@ findgoversion(void)
int i, nrev; int i, nrev;
Buf b, path, bmore, branch; Buf b, path, bmore, branch;
Vec tags; Vec tags;
binit(&b); binit(&b);
binit(&path); binit(&path);
binit(&bmore); binit(&bmore);
binit(&branch); binit(&branch);
vinit(&tags); vinit(&tags);
// The $GOROOT/VERSION file takes priority, for distributions // The $GOROOT/VERSION file takes priority, for distributions
// without the Mercurial repo. // without the Mercurial repo.
bpathf(&path, "%s/VERSION", goroot); bpathf(&path, "%s/VERSION", goroot);
...@@ -232,14 +232,14 @@ findgoversion(void) ...@@ -232,14 +232,14 @@ findgoversion(void)
bprintf(&b, "branch.%s", bstr(&branch)); bprintf(&b, "branch.%s", bstr(&branch));
tag = btake(&b); tag = btake(&b);
} }
if(rev[0]) { if(rev[0]) {
// Tag is before the revision we're building. // Tag is before the revision we're building.
// Add extra information. // Add extra information.
run(&bmore, goroot, CheckExit, "hg", "log", "--template", " +{node|short}", "-r", rev, nil); run(&bmore, goroot, CheckExit, "hg", "log", "--template", " +{node|short}", "-r", rev, nil);
chomp(&bmore); chomp(&bmore);
} }
bprintf(&b, "%s", tag); bprintf(&b, "%s", tag);
if(bmore.len > 0) if(bmore.len > 0)
bwriteb(&b, &bmore); bwriteb(&b, &bmore);
...@@ -249,14 +249,14 @@ findgoversion(void) ...@@ -249,14 +249,14 @@ findgoversion(void)
done: done:
p = btake(&b); p = btake(&b);
bfree(&b); bfree(&b);
bfree(&path); bfree(&path);
bfree(&bmore); bfree(&bmore);
bfree(&branch); bfree(&branch);
vfree(&tags); vfree(&tags);
return p; return p;
} }
...@@ -325,7 +325,7 @@ setup(void) ...@@ -325,7 +325,7 @@ setup(void)
xremoveall(p); xremoveall(p);
xmkdirall(p); xmkdirall(p);
} }
// Create object directory. // Create object directory.
// We keep it in pkg/ so that all the generated binaries // We keep it in pkg/ so that all the generated binaries
// are in one tree. If pkg/obj/libgc.a exists, it is a dreg from // are in one tree. If pkg/obj/libgc.a exists, it is a dreg from
...@@ -393,7 +393,7 @@ static char *proto_gccargs[] = { ...@@ -393,7 +393,7 @@ static char *proto_gccargs[] = {
static Vec gccargs; static Vec gccargs;
// deptab lists changes to the default dependencies for a given prefix. // deptab lists changes to the default dependencies for a given prefix.
// deps ending in /* read the whole directory; deps beginning with - // deps ending in /* read the whole directory; deps beginning with -
// exclude files with that prefix. // exclude files with that prefix.
static struct { static struct {
char *prefix; // prefix of target char *prefix; // prefix of target
...@@ -559,7 +559,7 @@ install(char *dir) ...@@ -559,7 +559,7 @@ install(char *dir)
vinit(&clean); vinit(&clean);
vinit(&lib); vinit(&lib);
vinit(&extra); vinit(&extra);
// path = full path to dir. // path = full path to dir.
bpathf(&path, "%s/src/%s", goroot, dir); bpathf(&path, "%s/src/%s", goroot, dir);
name = lastelem(dir); name = lastelem(dir);
...@@ -599,7 +599,7 @@ install(char *dir) ...@@ -599,7 +599,7 @@ install(char *dir)
exe = ""; exe = "";
if(streq(gohostos, "windows")) if(streq(gohostos, "windows"))
exe = ".exe"; exe = ".exe";
// Start final link command line. // Start final link command line.
// Note: code below knows that link.p[targ] is the target. // Note: code below knows that link.p[targ] is the target.
if(islib) { if(islib) {
...@@ -696,13 +696,13 @@ install(char *dir) ...@@ -696,13 +696,13 @@ install(char *dir)
} }
files.len = n; files.len = n;
continue; continue;
} }
vadd(&files, p); vadd(&files, p);
} }
} }
} }
vuniq(&files); vuniq(&files);
// Convert to absolute paths. // Convert to absolute paths.
for(i=0; i<files.len; i++) { for(i=0; i<files.len; i++) {
if(!isabs(files.p[i])) { if(!isabs(files.p[i])) {
...@@ -740,11 +740,11 @@ install(char *dir) ...@@ -740,11 +740,11 @@ install(char *dir)
files.p[n++] = files.p[i]; files.p[n++] = files.p[i];
} }
files.len = n; files.len = n;
for(i=0; i<lib.len && !stale; i++) for(i=0; i<lib.len && !stale; i++)
if(mtime(lib.p[i]) > ttarg) if(mtime(lib.p[i]) > ttarg)
stale = 1; stale = 1;
if(!stale) if(!stale)
goto out; goto out;
...@@ -792,9 +792,9 @@ install(char *dir) ...@@ -792,9 +792,9 @@ install(char *dir)
copy(bpathf(&b, "%s/zasm_GOOS_GOARCH.h", workdir), copy(bpathf(&b, "%s/zasm_GOOS_GOARCH.h", workdir),
bpathf(&b1, "%s/zasm_%s_%s.h", bstr(&path), goos, goarch), 0); bpathf(&b1, "%s/zasm_%s_%s.h", bstr(&path), goos, goarch), 0);
} }
// Generate .c files from .goc files. // Generate .c files from .goc files.
if(streq(dir, "pkg/runtime")) { if(streq(dir, "pkg/runtime")) {
for(i=0; i<files.len; i++) { for(i=0; i<files.len; i++) {
p = files.p[i]; p = files.p[i];
if(!hassuffix(p, ".goc")) if(!hassuffix(p, ".goc"))
...@@ -808,7 +808,7 @@ install(char *dir) ...@@ -808,7 +808,7 @@ install(char *dir)
} }
vuniq(&files); vuniq(&files);
} }
if((!streq(goos, gohostos) || !streq(goarch, gohostarch)) && isgo) { if((!streq(goos, gohostos) || !streq(goarch, gohostarch)) && isgo) {
// We've generated the right files; the go command can do the build. // We've generated the right files; the go command can do the build.
if(vflag > 1) if(vflag > 1)
...@@ -833,13 +833,13 @@ install(char *dir) ...@@ -833,13 +833,13 @@ install(char *dir)
vadd(&compile, "-m32"); vadd(&compile, "-m32");
if(streq(dir, "lib9")) if(streq(dir, "lib9"))
vadd(&compile, "-DPLAN9PORT"); vadd(&compile, "-DPLAN9PORT");
vadd(&compile, "-I"); vadd(&compile, "-I");
vadd(&compile, bpathf(&b, "%s/include", goroot)); vadd(&compile, bpathf(&b, "%s/include", goroot));
vadd(&compile, "-I"); vadd(&compile, "-I");
vadd(&compile, bstr(&path)); vadd(&compile, bstr(&path));
// lib9/goos.c gets the default constants hard-coded. // lib9/goos.c gets the default constants hard-coded.
if(streq(name, "goos.c")) { if(streq(name, "goos.c")) {
vadd(&compile, bprintf(&b, "-DGOOS=\"%s\"", goos)); vadd(&compile, bprintf(&b, "-DGOOS=\"%s\"", goos));
...@@ -849,7 +849,7 @@ install(char *dir) ...@@ -849,7 +849,7 @@ install(char *dir)
vadd(&compile, bprintf(&b, "-DGOROOT=\"%s\"", bstr(&b1))); vadd(&compile, bprintf(&b, "-DGOROOT=\"%s\"", bstr(&b1)));
vadd(&compile, bprintf(&b, "-DGOVERSION=\"%s\"", goversion)); vadd(&compile, bprintf(&b, "-DGOVERSION=\"%s\"", goversion));
} }
// gc/lex.c records the GOEXPERIMENT setting used during the build. // gc/lex.c records the GOEXPERIMENT setting used during the build.
if(streq(name, "lex.c")) { if(streq(name, "lex.c")) {
xgetenv(&b, "GOEXPERIMENT"); xgetenv(&b, "GOEXPERIMENT");
...@@ -867,7 +867,7 @@ install(char *dir) ...@@ -867,7 +867,7 @@ install(char *dir)
vadd(&compile, workdir); vadd(&compile, workdir);
vadd(&compile, bprintf(&b, "-DGOOS_%s", goos)); vadd(&compile, bprintf(&b, "-DGOOS_%s", goos));
vadd(&compile, bprintf(&b, "-DGOARCH_%s", goarch)); vadd(&compile, bprintf(&b, "-DGOARCH_%s", goarch));
} }
bpathf(&b, "%s/%s", workdir, lastelem(files.p[i])); bpathf(&b, "%s/%s", workdir, lastelem(files.p[i]));
doclean = 1; doclean = 1;
...@@ -893,7 +893,7 @@ install(char *dir) ...@@ -893,7 +893,7 @@ install(char *dir)
vadd(&clean, bstr(&b)); vadd(&clean, bstr(&b));
} }
bgwait(); bgwait();
if(isgo) { if(isgo) {
// The last loop was compiling individual files. // The last loop was compiling individual files.
// Hand the Go files to the compiler en masse. // Hand the Go files to the compiler en masse.
...@@ -905,16 +905,16 @@ install(char *dir) ...@@ -905,16 +905,16 @@ install(char *dir)
vadd(&compile, bstr(&b)); vadd(&compile, bstr(&b));
vadd(&clean, bstr(&b)); vadd(&clean, bstr(&b));
vadd(&link, bstr(&b)); vadd(&link, bstr(&b));
vadd(&compile, "-p"); vadd(&compile, "-p");
if(hasprefix(dir, "pkg/")) if(hasprefix(dir, "pkg/"))
vadd(&compile, dir+4); vadd(&compile, dir+4);
else else
vadd(&compile, "main"); vadd(&compile, "main");
if(streq(dir, "pkg/runtime")) if(streq(dir, "pkg/runtime"))
vadd(&compile, "-+"); vadd(&compile, "-+");
vcopy(&compile, go.p, go.len); vcopy(&compile, go.p, go.len);
runv(nil, bstr(&path), CheckExit, &compile); runv(nil, bstr(&path), CheckExit, &compile);
...@@ -980,7 +980,7 @@ shouldbuild(char *file, char *dir) ...@@ -980,7 +980,7 @@ shouldbuild(char *file, char *dir)
int i, j, ret; int i, j, ret;
Buf b; Buf b;
Vec lines, fields; Vec lines, fields;
// Check file name for GOOS or GOARCH. // Check file name for GOOS or GOARCH.
name = lastelem(file); name = lastelem(file);
for(i=0; i<nelem(okgoos); i++) for(i=0; i<nelem(okgoos); i++)
...@@ -989,11 +989,11 @@ shouldbuild(char *file, char *dir) ...@@ -989,11 +989,11 @@ shouldbuild(char *file, char *dir)
for(i=0; i<nelem(okgoarch); i++) for(i=0; i<nelem(okgoarch); i++)
if(contains(name, okgoarch[i]) && !streq(okgoarch[i], goarch)) if(contains(name, okgoarch[i]) && !streq(okgoarch[i], goarch))
return 0; return 0;
// Omit test files. // Omit test files.
if(contains(name, "_test")) if(contains(name, "_test"))
return 0; return 0;
// cmd/go/doc.go has a giant /* */ comment before // cmd/go/doc.go has a giant /* */ comment before
// it gets to the important detail that it is not part of // it gets to the important detail that it is not part of
// package main. We don't parse those comments, // package main. We don't parse those comments,
...@@ -1046,7 +1046,7 @@ out: ...@@ -1046,7 +1046,7 @@ out:
bfree(&b); bfree(&b);
vfree(&lines); vfree(&lines);
vfree(&fields); vfree(&fields);
return ret; return ret;
} }
...@@ -1055,7 +1055,7 @@ static void ...@@ -1055,7 +1055,7 @@ static void
copy(char *dst, char *src, int exec) copy(char *dst, char *src, int exec)
{ {
Buf b; Buf b;
if(vflag > 1) if(vflag > 1)
xprintf("cp %s %s\n", src, dst); xprintf("cp %s %s\n", src, dst);
...@@ -1070,11 +1070,13 @@ static char *buildorder[] = { ...@@ -1070,11 +1070,13 @@ static char *buildorder[] = {
"lib9", "lib9",
"libbio", "libbio",
"libmach", "libmach",
"misc/pprof", "misc/pprof",
"cmd/addr2line",
"cmd/cov", "cmd/cov",
"cmd/nm", "cmd/nm",
"cmd/objdump",
"cmd/pack", "cmd/pack",
"cmd/prof", "cmd/prof",
...@@ -1122,12 +1124,12 @@ static char *buildorder[] = { ...@@ -1122,12 +1124,12 @@ static char *buildorder[] = {
"pkg/go/scanner", "pkg/go/scanner",
"pkg/go/ast", "pkg/go/ast",
"pkg/go/parser", "pkg/go/parser",
"pkg/go/build",
"pkg/os/exec", "pkg/os/exec",
"pkg/net/url", "pkg/net/url",
"pkg/text/template/parse", "pkg/text/template/parse",
"pkg/text/template", "pkg/text/template",
"pkg/go/doc", "pkg/go/doc",
"pkg/go/build",
"cmd/go", "cmd/go",
}; };
...@@ -1147,11 +1149,13 @@ static char *cleantab[] = { ...@@ -1147,11 +1149,13 @@ static char *cleantab[] = {
"cmd/8c", "cmd/8c",
"cmd/8g", "cmd/8g",
"cmd/8l", "cmd/8l",
"cmd/addr2line",
"cmd/cc", "cmd/cc",
"cmd/cov", "cmd/cov",
"cmd/gc", "cmd/gc",
"cmd/go", "cmd/go",
"cmd/nm", "cmd/nm",
"cmd/objdump",
"cmd/pack", "cmd/pack",
"cmd/prof", "cmd/prof",
"lib9", "lib9",
...@@ -1204,11 +1208,11 @@ clean(void) ...@@ -1204,11 +1208,11 @@ clean(void)
int i, j, k; int i, j, k;
Buf b, path; Buf b, path;
Vec dir; Vec dir;
binit(&b); binit(&b);
binit(&path); binit(&path);
vinit(&dir); vinit(&dir);
for(i=0; i<nelem(cleantab); i++) { for(i=0; i<nelem(cleantab); i++) {
bpathf(&path, "%s/src/%s", goroot, cleantab[i]); bpathf(&path, "%s/src/%s", goroot, cleantab[i]);
xreaddir(&dir, bstr(&path)); xreaddir(&dir, bstr(&path));
...@@ -1227,7 +1231,7 @@ clean(void) ...@@ -1227,7 +1231,7 @@ clean(void)
if(rebuildall) { if(rebuildall) {
// Remove object tree. // Remove object tree.
xremoveall(bpathf(&b, "%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch)); xremoveall(bpathf(&b, "%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch));
// Remove installed packages and tools. // Remove installed packages and tools.
xremoveall(bpathf(&b, "%s/pkg/%s_%s", goroot, gohostos, gohostarch)); xremoveall(bpathf(&b, "%s/pkg/%s_%s", goroot, gohostos, gohostarch));
xremoveall(bpathf(&b, "%s/pkg/%s_%s", goroot, goos, goarch)); xremoveall(bpathf(&b, "%s/pkg/%s_%s", goroot, goos, goarch));
...@@ -1294,7 +1298,7 @@ cmdenv(int argc, char **argv) ...@@ -1294,7 +1298,7 @@ cmdenv(int argc, char **argv)
if(argc > 0) if(argc > 0)
usage(); usage();
xprintf(format, "GOROOT", goroot); xprintf(format, "GOROOT", goroot);
xprintf(format, "GOBIN", gobin); xprintf(format, "GOBIN", gobin);
xprintf(format, "GOARCH", goarch); xprintf(format, "GOARCH", goarch);
...@@ -1346,7 +1350,7 @@ cmdbootstrap(int argc, char **argv) ...@@ -1346,7 +1350,7 @@ cmdbootstrap(int argc, char **argv)
clean(); clean();
goversion = findgoversion(); goversion = findgoversion();
setup(); setup();
// For the main bootstrap, building for host os/arch. // For the main bootstrap, building for host os/arch.
oldgoos = goos; oldgoos = goos;
oldgoarch = goarch; oldgoarch = goarch;
...@@ -1356,7 +1360,7 @@ cmdbootstrap(int argc, char **argv) ...@@ -1356,7 +1360,7 @@ cmdbootstrap(int argc, char **argv)
gochar = gohostchar; gochar = gohostchar;
xsetenv("GOARCH", goarch); xsetenv("GOARCH", goarch);
xsetenv("GOOS", goos); xsetenv("GOOS", goos);
for(i=0; i<nelem(buildorder); i++) { for(i=0; i<nelem(buildorder); i++) {
install(bprintf(&b, buildorder[i], gohostchar)); install(bprintf(&b, buildorder[i], gohostchar));
if(!streq(oldgochar, gohostchar) && xstrstr(buildorder[i], "%s")) if(!streq(oldgochar, gohostchar) && xstrstr(buildorder[i], "%s"))
...@@ -1381,7 +1385,7 @@ defaulttarg(void) ...@@ -1381,7 +1385,7 @@ defaulttarg(void)
{ {
char *p; char *p;
Buf pwd, src, real_src; Buf pwd, src, real_src;
binit(&pwd); binit(&pwd);
binit(&src); binit(&src);
binit(&real_src); binit(&real_src);
...@@ -1404,7 +1408,7 @@ defaulttarg(void) ...@@ -1404,7 +1408,7 @@ defaulttarg(void)
bfree(&pwd); bfree(&pwd);
bfree(&src); bfree(&src);
bfree(&real_src); bfree(&real_src);
return p; return p;
} }
...@@ -1421,7 +1425,7 @@ cmdinstall(int argc, char **argv) ...@@ -1421,7 +1425,7 @@ cmdinstall(int argc, char **argv)
default: default:
usage(); usage();
}ARGEND }ARGEND
if(argc == 0) if(argc == 0)
install(defaulttarg()); install(defaulttarg());
...@@ -1469,7 +1473,7 @@ cmdbanner(int argc, char **argv) ...@@ -1469,7 +1473,7 @@ cmdbanner(int argc, char **argv)
binit(&b); binit(&b);
binit(&b1); binit(&b1);
binit(&search); binit(&search);
xprintf("\n"); xprintf("\n");
xprintf("---\n"); xprintf("---\n");
xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot); xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot);
...@@ -1490,7 +1494,7 @@ cmdbanner(int argc, char **argv) ...@@ -1490,7 +1494,7 @@ cmdbanner(int argc, char **argv)
"On OS X the debuggers must be installed setgrp procmod.\n" "On OS X the debuggers must be installed setgrp procmod.\n"
"Read and run ./sudo.bash to install the debuggers.\n"); "Read and run ./sudo.bash to install the debuggers.\n");
} }
if(!streq(goroot_final, goroot)) { if(!streq(goroot_final, goroot)) {
xprintf("\n" xprintf("\n"
"The binaries expect %s to be copied or moved to %s\n", "The binaries expect %s to be copied or moved to %s\n",
......
// 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.
/*
* objdump simulation - only enough to make pprof work on Macs
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
void
usage(void)
{
fprint(2, "usage: objdump binary start stop\n");
fprint(2, "Disassembles binary from PC start up to stop.\n");
exits("usage");
}
void
main(int argc, char **argv)
{
int fd, n;
uvlong pc, start, stop;
Fhdr fhdr;
Biobuf bout;
char buf[1024];
Map *text;
ARGBEGIN{
default:
usage();
}ARGEND
if(argc != 3)
usage();
start = strtoull(argv[1], 0, 16);
stop = strtoull(argv[2], 0, 16);
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");
Binit(&bout, 1, OWRITE);
for(pc=start; pc<stop; ) {
if(fileline(buf, sizeof buf, pc))
Bprint(&bout, "%s\n", buf);
buf[0] = '\0';
machdata->das(text, pc, 0, buf, sizeof buf);
Bprint(&bout, " %llx: %s\n", pc, buf);
n = machdata->instsize(text, pc);
if(n <= 0)
break;
pc += n;
}
Bflush(&bout);
exits(0);
}
// Inferno libmach/8db.c // Inferno libmach/8db.c
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/8db.c // http://code.google.com/p/inferno-os/source/browse/utils/libmach/8db.c
// //
// Copyright © 1994-1999 Lucent Technologies Inc. // Copyright © 1994-1999 Lucent Technologies Inc.
// Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net). // Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
// Portions Copyright © 1997-1999 Vita Nuova Limited. // Portions Copyright © 1997-1999 Vita Nuova Limited.
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com). // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
// Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others. // Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
// Portions Copyright © 2009 The Go Authors. All rights reserved. // Portions Copyright © 2009 The Go Authors. All rights reserved.
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
...@@ -2088,18 +2088,23 @@ immediate(Instr *ip, vlong val) ...@@ -2088,18 +2088,23 @@ immediate(Instr *ip, vlong val)
static void static void
pea(Instr *ip) pea(Instr *ip)
{ {
int base;
base = ip->base;
if(base >= 0 && (ip->rex & REXB))
base += 8;
if (ip->mod == 3) { if (ip->mod == 3) {
if (ip->osize == 'B') if (ip->osize == 'B')
bprint(ip, (ip->rex & REXB? breg64: breg)[(uchar)ip->base]); bprint(ip, (ip->rex & REXB? breg64: breg)[(uchar)ip->base]);
else if(ip->rex & REXB)
bprint(ip, "%s%s", ANAME(ip), reg[ip->base+8]);
else else
bprint(ip, "%s%s", ANAME(ip), reg[(uchar)ip->base]); bprint(ip, "%s%s", ANAME(ip), reg[base]);
return; return;
} }
if (ip->segment) if (ip->segment)
bprint(ip, ip->segment); bprint(ip, ip->segment);
if (ip->asize == 'E' && ip->base == SP) if (ip->asize == 'E' && base == SP)
plocal(ip); plocal(ip);
else { else {
if (ip->base < 0) if (ip->base < 0)
......
...@@ -118,8 +118,12 @@ runtime·notewakeup(Note *n) ...@@ -118,8 +118,12 @@ runtime·notewakeup(Note *n)
void void
runtime·notesleep(Note *n) runtime·notesleep(Note *n)
{ {
if(m->profilehz > 0)
runtime·setprof(false);
while(runtime·atomicload(&n->key) == 0) while(runtime·atomicload(&n->key) == 0)
runtime·futexsleep(&n->key, 0, -1); runtime·futexsleep(&n->key, 0, -1);
if(m->profilehz > 0)
runtime·setprof(true);
} }
void void
...@@ -135,14 +139,18 @@ runtime·notetsleep(Note *n, int64 ns) ...@@ -135,14 +139,18 @@ runtime·notetsleep(Note *n, int64 ns)
if(runtime·atomicload(&n->key) != 0) if(runtime·atomicload(&n->key) != 0)
return; return;
if(m->profilehz > 0)
runtime·setprof(false);
deadline = runtime·nanotime() + ns; deadline = runtime·nanotime() + ns;
for(;;) { for(;;) {
runtime·futexsleep(&n->key, 0, ns); runtime·futexsleep(&n->key, 0, ns);
if(runtime·atomicload(&n->key) != 0) if(runtime·atomicload(&n->key) != 0)
return; break;
now = runtime·nanotime(); now = runtime·nanotime();
if(now >= deadline) if(now >= deadline)
return; break;
ns = deadline - now; ns = deadline - now;
} }
if(m->profilehz > 0)
runtime·setprof(true);
} }
...@@ -154,7 +154,11 @@ runtime·notesleep(Note *n) ...@@ -154,7 +154,11 @@ runtime·notesleep(Note *n)
return; return;
} }
// Queued. Sleep. // Queued. Sleep.
if(m->profilehz > 0)
runtime·setprof(false);
runtime·semasleep(-1); runtime·semasleep(-1);
if(m->profilehz > 0)
runtime·setprof(true);
} }
void void
...@@ -178,12 +182,16 @@ runtime·notetsleep(Note *n, int64 ns) ...@@ -178,12 +182,16 @@ runtime·notetsleep(Note *n, int64 ns)
return; return;
} }
if(m->profilehz > 0)
runtime·setprof(false);
deadline = runtime·nanotime() + ns; deadline = runtime·nanotime() + ns;
for(;;) { for(;;) {
// Registered. Sleep. // Registered. Sleep.
if(runtime·semasleep(ns) >= 0) { if(runtime·semasleep(ns) >= 0) {
// Acquired semaphore, semawakeup unregistered us. // Acquired semaphore, semawakeup unregistered us.
// Done. // Done.
if(m->profilehz > 0)
runtime·setprof(true);
return; return;
} }
...@@ -196,6 +204,9 @@ runtime·notetsleep(Note *n, int64 ns) ...@@ -196,6 +204,9 @@ runtime·notetsleep(Note *n, int64 ns)
ns = deadline - now; ns = deadline - now;
} }
if(m->profilehz > 0)
runtime·setprof(true);
// Deadline arrived. Still registered. Semaphore not acquired. // Deadline arrived. Still registered. Semaphore not acquired.
// Want to give up and return, but have to unregister first, // Want to give up and return, but have to unregister first,
// so that any notewakeup racing with the return does not // so that any notewakeup racing with the return does not
......
...@@ -38,4 +38,7 @@ void runtime·raisesigpipe(void); ...@@ -38,4 +38,7 @@ void runtime·raisesigpipe(void);
#define NSIG 32 #define NSIG 32
#define SI_USER 0 /* empirically true, but not what headers say */ #define SI_USER 0 /* empirically true, but not what headers say */
#define SIG_BLOCK 1
#define SIG_UNBLOCK 2
#define SIG_SETMASK 3 #define SIG_SETMASK 3
...@@ -9,7 +9,6 @@ struct sigaction; ...@@ -9,7 +9,6 @@ struct sigaction;
void runtime·sigaction(int32, struct sigaction*, struct sigaction*); void runtime·sigaction(int32, struct sigaction*, struct sigaction*);
void runtime·sigprocmask(Sigset *, Sigset *); void runtime·sigprocmask(Sigset *, Sigset *);
void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool); void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool);
void runtiem·setitimerval(int32, Itimerval*, Itimerval*);
void runtime·setitimer(int32, Itimerval*, Itimerval*); void runtime·setitimer(int32, Itimerval*, Itimerval*);
int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr); int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr);
......
...@@ -12,7 +12,6 @@ void runtime·sigaltstack(Sigaltstack*, Sigaltstack*); ...@@ -12,7 +12,6 @@ void runtime·sigaltstack(Sigaltstack*, Sigaltstack*);
void runtime·sigaction(int32, struct sigaction*, struct sigaction*); void runtime·sigaction(int32, struct sigaction*, struct sigaction*);
void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool); void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool);
void runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp); void runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp);
void runtime·setitimerval(int32, Itimerval*, Itimerval*);
void runtime·setitimer(int32, Itimerval*, Itimerval*); void runtime·setitimer(int32, Itimerval*, Itimerval*);
int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr); int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr);
......
...@@ -12,7 +12,6 @@ void runtime·sigaltstack(Sigaltstack*, Sigaltstack*); ...@@ -12,7 +12,6 @@ void runtime·sigaltstack(Sigaltstack*, Sigaltstack*);
void runtime·sigaction(int32, struct sigaction*, struct sigaction*); void runtime·sigaction(int32, struct sigaction*, struct sigaction*);
void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool); void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool);
void runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp); void runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp);
void runtime·setitimerval(int32, Itimerval*, Itimerval*);
void runtime·setitimer(int32, Itimerval*, Itimerval*); void runtime·setitimer(int32, Itimerval*, Itimerval*);
int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr); int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr);
......
...@@ -20,8 +20,8 @@ import ( ...@@ -20,8 +20,8 @@ import (
"text/tabwriter" "text/tabwriter"
) )
// BUG(rsc): CPU profiling is broken on OS X, due to an Apple kernel bug. // BUG(rsc): A bug in the OS X Snow Leopard 64-bit kernel prevents
// For details, see http://code.google.com/p/go/source/detail?r=35b716c94225. // CPU profiling from giving accurate results on that system.
// A Profile is a collection of stack traces showing the call sequences // A Profile is a collection of stack traces showing the call sequences
// that led to instances of a particular event, such as allocation. // that led to instances of a particular event, such as allocation.
...@@ -156,7 +156,7 @@ func (p *Profile) Count() int { ...@@ -156,7 +156,7 @@ func (p *Profile) Count() int {
} }
// Add adds the current execution stack to the profile, associated with value. // Add adds the current execution stack to the profile, associated with value.
// Add stores value in an internal map, so value must be suitable for use as // Add stores value in an internal map, so value must be suitable for use as
// a map key and will not be garbage collected until the corresponding // a map key and will not be garbage collected until the corresponding
// call to Remove. Add panics if the profile already contains a stack for value. // call to Remove. Add panics if the profile already contains a stack for value.
// //
......
...@@ -7,6 +7,7 @@ package pprof_test ...@@ -7,6 +7,7 @@ package pprof_test
import ( import (
"bytes" "bytes"
"hash/crc32" "hash/crc32"
"os/exec"
"runtime" "runtime"
. "runtime/pprof" . "runtime/pprof"
"strings" "strings"
...@@ -17,8 +18,15 @@ import ( ...@@ -17,8 +18,15 @@ import (
func TestCPUProfile(t *testing.T) { func TestCPUProfile(t *testing.T) {
switch runtime.GOOS { switch runtime.GOOS {
case "darwin": case "darwin":
// see Apple Bug Report #9177434 (copied into change description) out, err := exec.Command("uname", "-a").CombinedOutput()
return if err != nil {
t.Fatal(err)
}
vers := string(out)
t.Logf("uname -a: %v", vers)
if strings.Contains(vers, "Darwin Kernel Version 10.8.0") && strings.Contains(vers, "root:xnu-1504.15.3~1/RELEASE_X86_64") {
t.Logf("skipping test on known-broken kernel (64-bit Snow Leopard)")
}
case "plan9": case "plan9":
// unimplemented // unimplemented
return return
......
...@@ -338,7 +338,7 @@ mcommoninit(M *m) ...@@ -338,7 +338,7 @@ mcommoninit(M *m)
m->mcache = runtime·allocmcache(); m->mcache = runtime·allocmcache();
runtime·callers(1, m->createstack, nelem(m->createstack)); runtime·callers(1, m->createstack, nelem(m->createstack));
// Add to runtime·allm so garbage collector doesn't free m // Add to runtime·allm so garbage collector doesn't free m
// when it is just in a register or thread-local storage. // when it is just in a register or thread-local storage.
m->alllink = runtime·allm; m->alllink = runtime·allm;
...@@ -728,7 +728,6 @@ runtime·mstart(void) ...@@ -728,7 +728,6 @@ runtime·mstart(void)
// so other calls can reuse this stack space. // so other calls can reuse this stack space.
runtime·gosave(&m->g0->sched); runtime·gosave(&m->g0->sched);
m->g0->sched.pc = (void*)-1; // make sure it is never used m->g0->sched.pc = (void*)-1; // make sure it is never used
runtime·asminit(); runtime·asminit();
runtime·minit(); runtime·minit();
schedule(nil); schedule(nil);
...@@ -916,6 +915,9 @@ runtime·entersyscall(void) ...@@ -916,6 +915,9 @@ runtime·entersyscall(void)
{ {
uint32 v; uint32 v;
if(m->profilehz > 0)
runtime·setprof(false);
// Leave SP around for gc and traceback. // Leave SP around for gc and traceback.
runtime·gosave(&g->sched); runtime·gosave(&g->sched);
g->gcsp = g->sched.sp; g->gcsp = g->sched.sp;
...@@ -979,6 +981,9 @@ runtime·exitsyscall(void) ...@@ -979,6 +981,9 @@ runtime·exitsyscall(void)
// Garbage collector isn't running (since we are), // Garbage collector isn't running (since we are),
// so okay to clear gcstack. // so okay to clear gcstack.
g->gcstack = nil; g->gcstack = nil;
if(m->profilehz > 0)
runtime·setprof(true);
return; return;
} }
......
...@@ -730,3 +730,13 @@ bool runtime·showframe(Func*); ...@@ -730,3 +730,13 @@ bool runtime·showframe(Func*);
void runtime·ifaceE2I(struct InterfaceType*, Eface, Iface*); void runtime·ifaceE2I(struct InterfaceType*, Eface, Iface*);
uintptr runtime·memlimit(void); uintptr runtime·memlimit(void);
// If appropriate, ask the operating system to control whether this
// thread should receive profiling signals. This is only necessary on OS X.
// An operating system should not deliver a profiling signal to a
// thread that is not actually executing (what good is that?), but that's
// what OS X prefers to do. When profiling is turned on, we mask
// away the profiling signal when threads go to sleep, so that OS X
// is forced to deliver the signal to a thread that's actually running.
// This is a no-op on other systems.
void runtime·setprof(bool);
...@@ -40,7 +40,8 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) ...@@ -40,7 +40,8 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
r = &mc->ss; r = &mc->ss;
if(sig == SIGPROF) { if(sig == SIGPROF) {
runtime·sigprof((uint8*)r->eip, (uint8*)r->esp, nil, gp); if(gp != m->g0 && gp != m->gsignal)
runtime·sigprof((uint8*)r->eip, (uint8*)r->esp, nil, gp);
return; return;
} }
...@@ -58,7 +59,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) ...@@ -58,7 +59,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
if(pc[0] == 0xF6 || pc[0] == 0xF7) if(pc[0] == 0xF6 || pc[0] == 0xF7)
info->si_code = FPE_INTDIV; info->si_code = FPE_INTDIV;
} }
// Make it look like a call to the signal func. // Make it look like a call to the signal func.
// Have to pass arguments out of band since // Have to pass arguments out of band since
// augmenting the stack frame would break // augmenting the stack frame would break
......
...@@ -48,7 +48,8 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) ...@@ -48,7 +48,8 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
r = &mc->ss; r = &mc->ss;
if(sig == SIGPROF) { if(sig == SIGPROF) {
runtime·sigprof((uint8*)r->rip, (uint8*)r->rsp, nil, gp); if(gp != m->g0 && gp != m->gsignal)
runtime·sigprof((uint8*)r->rip, (uint8*)r->rsp, nil, gp);
return; return;
} }
...@@ -68,7 +69,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) ...@@ -68,7 +69,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
if(pc[0] == 0xF6 || pc[0] == 0xF7) if(pc[0] == 0xF6 || pc[0] == 0xF7)
info->si_code = FPE_INTDIV; info->si_code = FPE_INTDIV;
} }
// Make it look like a call to the signal func. // Make it look like a call to the signal func.
// Have to pass arguments out of band since // Have to pass arguments out of band since
// augmenting the stack frame would break // augmenting the stack frame would break
...@@ -77,7 +78,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) ...@@ -77,7 +78,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
gp->sigcode0 = info->si_code; gp->sigcode0 = info->si_code;
gp->sigcode1 = (uintptr)info->si_addr; gp->sigcode1 = (uintptr)info->si_addr;
gp->sigpc = r->rip; gp->sigpc = r->rip;
// Only push runtime·sigpanic if r->rip != 0. // Only push runtime·sigpanic if r->rip != 0.
// If r->rip == 0, probably panicked because of a // If r->rip == 0, probably panicked because of a
// call to a nil func. Not pushing that onto sp will // call to a nil func. Not pushing that onto sp will
...@@ -92,7 +93,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp) ...@@ -92,7 +93,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
r->rip = (uintptr)runtime·sigpanic; r->rip = (uintptr)runtime·sigpanic;
return; return;
} }
if(info->si_code == SI_USER || (t->flags & SigNotify)) if(info->si_code == SI_USER || (t->flags & SigNotify))
if(runtime·sigsend(sig)) if(runtime·sigsend(sig))
return; return;
......
...@@ -55,17 +55,17 @@ void ...@@ -55,17 +55,17 @@ void
runtime·resetcpuprofiler(int32 hz) runtime·resetcpuprofiler(int32 hz)
{ {
Itimerval it; Itimerval it;
runtime·memclr((byte*)&it, sizeof it); runtime·memclr((byte*)&it, sizeof it);
if(hz == 0) { if(hz == 0) {
runtime·setitimer(ITIMER_PROF, &it, nil); runtime·setitimer(ITIMER_PROF, &it, nil);
runtime·setsig(SIGPROF, SIG_IGN, true); runtime·setprof(false);
} else { } else {
runtime·setsig(SIGPROF, runtime·sighandler, true);
it.it_interval.tv_sec = 0; it.it_interval.tv_sec = 0;
it.it_interval.tv_usec = 1000000 / hz; it.it_interval.tv_usec = 1000000 / hz;
it.it_value = it.it_interval; it.it_value = it.it_interval;
runtime·setitimer(ITIMER_PROF, &it, nil); runtime·setitimer(ITIMER_PROF, &it, nil);
runtime·setprof(true);
} }
m->profilehz = hz; m->profilehz = hz;
} }
......
...@@ -100,14 +100,14 @@ TEXT runtime·nanotime(SB), 7, $32 ...@@ -100,14 +100,14 @@ TEXT runtime·nanotime(SB), 7, $32
IMULL $1000, BX IMULL $1000, BX
ADDL BX, AX ADDL BX, AX
ADCL $0, DX ADCL $0, DX
MOVL ret+0(FP), DI MOVL ret+0(FP), DI
MOVL AX, 0(DI) MOVL AX, 0(DI)
MOVL DX, 4(DI) MOVL DX, 4(DI)
RET RET
TEXT runtime·sigprocmask(SB),7,$0 TEXT runtime·sigprocmask(SB),7,$0
MOVL $48, AX MOVL $329, AX // pthread_sigmask (on OS X, sigprocmask==entire process)
INT $0x80 INT $0x80
JAE 2(PC) JAE 2(PC)
CALL runtime·notok(SB) CALL runtime·notok(SB)
......
...@@ -96,7 +96,7 @@ TEXT runtime·sigprocmask(SB),7,$0 ...@@ -96,7 +96,7 @@ TEXT runtime·sigprocmask(SB),7,$0
MOVL 8(SP), DI MOVL 8(SP), DI
MOVQ 16(SP), SI MOVQ 16(SP), SI
MOVQ 24(SP), DX MOVQ 24(SP), DX
MOVL $(0x2000000+48), AX // syscall entry MOVL $(0x2000000+329), AX // pthread_sigmask (on OS X, sigprocmask==entire process)
SYSCALL SYSCALL
JCC 2(PC) JCC 2(PC)
CALL runtime·notok(SB) CALL runtime·notok(SB)
......
...@@ -11,6 +11,7 @@ extern SigTab runtime·sigtab[]; ...@@ -11,6 +11,7 @@ extern SigTab runtime·sigtab[];
static Sigset sigset_all = ~(Sigset)0; static Sigset sigset_all = ~(Sigset)0;
static Sigset sigset_none; static Sigset sigset_none;
static Sigset sigset_prof = 1<<(SIGPROF-1);
static void static void
unimplemented(int8 *name) unimplemented(int8 *name)
...@@ -23,7 +24,14 @@ unimplemented(int8 *name) ...@@ -23,7 +24,14 @@ unimplemented(int8 *name)
int32 int32
runtime·semasleep(int64 ns) runtime·semasleep(int64 ns)
{ {
return runtime·mach_semacquire(m->waitsema, ns); int32 v;
if(m->profilehz > 0)
runtime·setprof(false);
v = runtime·mach_semacquire(m->waitsema, ns);
if(m->profilehz > 0)
runtime·setprof(true);
return v;
} }
void void
...@@ -84,7 +92,7 @@ runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void)) ...@@ -84,7 +92,7 @@ runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void))
runtime·sigprocmask(SIG_SETMASK, &sigset_all, &oset); runtime·sigprocmask(SIG_SETMASK, &sigset_all, &oset);
errno = runtime·bsdthread_create(stk, m, g, fn); errno = runtime·bsdthread_create(stk, m, g, fn);
runtime·sigprocmask(SIG_SETMASK, &oset, nil); runtime·sigprocmask(SIG_SETMASK, &oset, nil);
if(errno < 0) { if(errno < 0) {
runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\n", runtime·mcount(), -errno); runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\n", runtime·mcount(), -errno);
runtime·throw("runtime.newosproc"); runtime·throw("runtime.newosproc");
...@@ -98,7 +106,11 @@ runtime·minit(void) ...@@ -98,7 +106,11 @@ runtime·minit(void)
// Initialize signal handling. // Initialize signal handling.
m->gsignal = runtime·malg(32*1024); // OS X wants >=8K, Linux >=2K m->gsignal = runtime·malg(32*1024); // OS X wants >=8K, Linux >=2K
runtime·signalstack(m->gsignal->stackguard - StackGuard, 32*1024); runtime·signalstack(m->gsignal->stackguard - StackGuard, 32*1024);
runtime·sigprocmask(SIG_SETMASK, &sigset_none, nil);
if(m->profilehz > 0)
runtime·sigprocmask(SIG_SETMASK, &sigset_none, nil);
else
runtime·sigprocmask(SIG_SETMASK, &sigset_prof, nil);
} }
// Mach IPC, to get at semaphores // Mach IPC, to get at semaphores
...@@ -434,3 +446,34 @@ runtime·memlimit(void) ...@@ -434,3 +446,34 @@ runtime·memlimit(void)
// the limit. // the limit.
return 0; return 0;
} }
// NOTE(rsc): On OS X, when the CPU profiling timer expires, the SIGPROF
// signal is not guaranteed to be sent to the thread that was executing to
// cause it to expire. It can and often does go to a sleeping thread, which is
// not interesting for our profile. This is filed Apple Bug Report #9177434,
// copied to http://code.google.com/p/go/source/detail?r=35b716c94225.
// To work around this bug, we disable receipt of the profiling signal on
// a thread while in blocking system calls. This forces the kernel to deliver
// the profiling signal to an executing thread.
//
// The workaround fails on OS X machines using a 64-bit Snow Leopard kernel.
// In that configuration, the kernel appears to want to deliver SIGPROF to the
// sleeping threads regardless of signal mask and, worse, does not deliver
// the signal until the thread wakes up on its own.
//
// If necessary, we can switch to using ITIMER_REAL for OS X and handle
// the kernel-generated SIGALRM by generating our own SIGALRMs to deliver
// to all the running threads. SIGALRM does not appear to be affected by
// the 64-bit Snow Leopard bug. However, as of this writing Mountain Lion
// is in preview, making Snow Leopard two versions old, so it is unclear how
// much effort we need to spend on one buggy kernel.
// Control whether profiling signal can be delivered to this thread.
void
runtime·setprof(bool on)
{
if(on)
runtime·sigprocmask(SIG_UNBLOCK, &sigset_prof, nil);
else
runtime·sigprocmask(SIG_BLOCK, &sigset_prof, nil);
}
...@@ -189,3 +189,9 @@ runtime·memlimit(void) ...@@ -189,3 +189,9 @@ runtime·memlimit(void)
return rl.rlim_cur - used; return rl.rlim_cur - used;
} }
void
runtime·setprof(bool on)
{
USED(on);
}
...@@ -228,7 +228,7 @@ runtime·memlimit(void) ...@@ -228,7 +228,7 @@ runtime·memlimit(void)
Rlimit rl; Rlimit rl;
extern byte text[], end[]; extern byte text[], end[];
uintptr used; uintptr used;
if(runtime·getrlimit(RLIMIT_AS, &rl) != 0) if(runtime·getrlimit(RLIMIT_AS, &rl) != 0)
return 0; return 0;
if(rl.rlim_cur >= 0x7fffffff) if(rl.rlim_cur >= 0x7fffffff)
...@@ -249,3 +249,9 @@ runtime·memlimit(void) ...@@ -249,3 +249,9 @@ runtime·memlimit(void)
return rl.rlim_cur - used; return rl.rlim_cur - used;
} }
void
runtime·setprof(bool on)
{
USED(on);
}
...@@ -207,3 +207,9 @@ runtime·memlimit(void) ...@@ -207,3 +207,9 @@ runtime·memlimit(void)
{ {
return 0; return 0;
} }
void
runtime·setprof(bool on)
{
USED(on);
}
...@@ -207,3 +207,9 @@ runtime·memlimit(void) ...@@ -207,3 +207,9 @@ runtime·memlimit(void)
{ {
return 0; return 0;
} }
void
runtime·setprof(bool on)
{
USED(on);
}
...@@ -87,7 +87,7 @@ runtime·nanotime(void) ...@@ -87,7 +87,7 @@ runtime·nanotime(void)
// The naïve implementation (without the cached // The naïve implementation (without the cached
// file descriptor) is roughly four times slower // file descriptor) is roughly four times slower
// in 9vx on a 2.16 GHz Intel Core 2 Duo. // in 9vx on a 2.16 GHz Intel Core 2 Duo.
if(fd < 0 && (fd = runtime·open((byte*)"/dev/bintime", OREAD|OCEXEC)) < 0) if(fd < 0 && (fd = runtime·open((byte*)"/dev/bintime", OREAD|OCEXEC)) < 0)
return 0; return 0;
if(runtime·pread(fd, b, sizeof b, 0) != sizeof b) if(runtime·pread(fd, b, sizeof b, 0) != sizeof b)
...@@ -241,3 +241,9 @@ runtime·memlimit(void) ...@@ -241,3 +241,9 @@ runtime·memlimit(void)
{ {
return 0; return 0;
} }
void
runtime·setprof(bool on)
{
USED(on);
}
...@@ -226,7 +226,7 @@ void ...@@ -226,7 +226,7 @@ void
time·now(int64 sec, int32 usec) time·now(int64 sec, int32 usec)
{ {
int64 ns; int64 ns;
ns = runtime·nanotime(); ns = runtime·nanotime();
sec = ns / 1000000000LL; sec = ns / 1000000000LL;
usec = ns - sec * 1000000000LL; usec = ns - sec * 1000000000LL;
...@@ -431,3 +431,9 @@ runtime·memlimit(void) ...@@ -431,3 +431,9 @@ runtime·memlimit(void)
{ {
return 0; return 0;
} }
void
runtime·setprof(bool on)
{
USED(on);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment