#! /usr/bin/env perl
# 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
# back into the official source before long.
# It's convenient to have a copy here because we need just the one
# Copyright (c) 1998-2007, Google Inc.
# All rights reserved.
-#
+#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
-#
+#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
-#
+#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
my $cmd = sprintf("$objdump -C -d -l --no-show-raw-insn " .
"--start-address=0x$start_addr " .
"--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");
my @result = ();
my $filename = "";
my $cumulative = shift;
my $list_opts = shift;
my $html = shift;
-
+
my $output = \*STDOUT;
my $fname = "";
-
+
if ($html) {
# Arrange to write the output to a temporary file
printf $output ("<div class=\"legend\">%s<br>Total: %s %s</div>\n",
$main::prog, Unparse($total), Units());
}
-
+
my $listed = 0;
foreach my $lib (@{$libs}) {
my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts);
z = 0.1;
if(z > 10.0)
z = 10.0;
-
+
var g = svgDoc.getElementById("viewport");
var p = getEventPoint(evt);
$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
# nm to get what info we can (function names, but not line numbers).
if (system("$addr2line --help >/dev/null 2>&1") != 0) {
if ($debug) {
print("----\n");
system("cat $main::tmpfile_sym");
- print("----\n");
+ print("---- $cmd\n");
system("$cmd <$main::tmpfile_sym");
print("----\n");
}
# Trim overly long symbols found in disassembler output
sub CleanDisassembly {
my $d = shift;
- while ($d =~ s/(?<!\.)\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax)
+ while ($d =~ s/(?<!\.)\([^()%A-Z]*\)(\s*const)?//g) { } # Argument types, not (%rax)
while ($d =~ s/(\w+)<[^<>]*>/$1/g) { } # Remove template arguments
return $d;
}
my $dirname = $`; # this is everything up to and including the last slash
if (-x "$dirname$tool") {
$path = "$dirname$tool";
- } else {
+ } else {
$path = $tool;
}
}
--- /dev/null
+// 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);
+}
find(char *p, char **l, int n)
{
int i;
-
+
for(i=0; i<n; i++)
if(streq(p, l[i]))
return i;
char *p;
int i;
Buf b;
-
+
binit(&b);
xgetenv(&b, "GOROOT");
xsetenv("GOROOT", goroot);
xsetenv("GOARCH", goarch);
xsetenv("GOOS", goos);
-
+
// Make the environment more predictable.
xsetenv("LANG", "C");
xsetenv("LANGUAGE", "en_US.UTF8");
int i, nrev;
Buf b, path, bmore, branch;
Vec tags;
-
+
binit(&b);
binit(&path);
binit(&bmore);
binit(&branch);
vinit(&tags);
-
+
// The $GOROOT/VERSION file takes priority, for distributions
// without the Mercurial repo.
bpathf(&path, "%s/VERSION", goroot);
bprintf(&b, "branch.%s", bstr(&branch));
tag = btake(&b);
}
-
+
if(rev[0]) {
// Tag is before the revision we're building.
// Add extra information.
run(&bmore, goroot, CheckExit, "hg", "log", "--template", " +{node|short}", "-r", rev, nil);
chomp(&bmore);
}
-
+
bprintf(&b, "%s", tag);
if(bmore.len > 0)
bwriteb(&b, &bmore);
done:
p = btake(&b);
-
-
+
+
bfree(&b);
bfree(&path);
bfree(&bmore);
bfree(&branch);
vfree(&tags);
-
+
return p;
}
xremoveall(p);
xmkdirall(p);
}
-
+
// Create object directory.
// 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
static Vec gccargs;
// 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.
static struct {
char *prefix; // prefix of target
vinit(&clean);
vinit(&lib);
vinit(&extra);
-
+
// path = full path to dir.
bpathf(&path, "%s/src/%s", goroot, dir);
name = lastelem(dir);
exe = "";
if(streq(gohostos, "windows"))
exe = ".exe";
-
+
// Start final link command line.
// Note: code below knows that link.p[targ] is the target.
if(islib) {
}
files.len = n;
continue;
- }
+ }
vadd(&files, p);
}
}
}
vuniq(&files);
-
+
// Convert to absolute paths.
for(i=0; i<files.len; i++) {
if(!isabs(files.p[i])) {
files.p[n++] = files.p[i];
}
files.len = n;
-
+
for(i=0; i<lib.len && !stale; i++)
if(mtime(lib.p[i]) > ttarg)
stale = 1;
-
+
if(!stale)
goto out;
copy(bpathf(&b, "%s/zasm_GOOS_GOARCH.h", workdir),
bpathf(&b1, "%s/zasm_%s_%s.h", bstr(&path), goos, goarch), 0);
}
-
+
// Generate .c files from .goc files.
- if(streq(dir, "pkg/runtime")) {
+ if(streq(dir, "pkg/runtime")) {
for(i=0; i<files.len; i++) {
p = files.p[i];
if(!hassuffix(p, ".goc"))
}
vuniq(&files);
}
-
+
if((!streq(goos, gohostos) || !streq(goarch, gohostarch)) && isgo) {
// We've generated the right files; the go command can do the build.
if(vflag > 1)
vadd(&compile, "-m32");
if(streq(dir, "lib9"))
vadd(&compile, "-DPLAN9PORT");
-
+
vadd(&compile, "-I");
vadd(&compile, bpathf(&b, "%s/include", goroot));
-
+
vadd(&compile, "-I");
vadd(&compile, bstr(&path));
-
+
// lib9/goos.c gets the default constants hard-coded.
if(streq(name, "goos.c")) {
vadd(&compile, bprintf(&b, "-DGOOS=\"%s\"", goos));
vadd(&compile, bprintf(&b, "-DGOROOT=\"%s\"", bstr(&b1)));
vadd(&compile, bprintf(&b, "-DGOVERSION=\"%s\"", goversion));
}
-
+
// gc/lex.c records the GOEXPERIMENT setting used during the build.
if(streq(name, "lex.c")) {
xgetenv(&b, "GOEXPERIMENT");
vadd(&compile, workdir);
vadd(&compile, bprintf(&b, "-DGOOS_%s", goos));
vadd(&compile, bprintf(&b, "-DGOARCH_%s", goarch));
- }
+ }
bpathf(&b, "%s/%s", workdir, lastelem(files.p[i]));
doclean = 1;
vadd(&clean, bstr(&b));
}
bgwait();
-
+
if(isgo) {
// The last loop was compiling individual files.
// Hand the Go files to the compiler en masse.
vadd(&compile, bstr(&b));
vadd(&clean, bstr(&b));
vadd(&link, bstr(&b));
-
+
vadd(&compile, "-p");
if(hasprefix(dir, "pkg/"))
vadd(&compile, dir+4);
else
vadd(&compile, "main");
-
+
if(streq(dir, "pkg/runtime"))
vadd(&compile, "-+");
-
+
vcopy(&compile, go.p, go.len);
runv(nil, bstr(&path), CheckExit, &compile);
int i, j, ret;
Buf b;
Vec lines, fields;
-
+
// Check file name for GOOS or GOARCH.
name = lastelem(file);
for(i=0; i<nelem(okgoos); i++)
for(i=0; i<nelem(okgoarch); i++)
if(contains(name, okgoarch[i]) && !streq(okgoarch[i], goarch))
return 0;
-
+
// Omit test files.
if(contains(name, "_test"))
return 0;
-
+
// cmd/go/doc.go has a giant /* */ comment before
// it gets to the important detail that it is not part of
// package main. We don't parse those comments,
bfree(&b);
vfree(&lines);
vfree(&fields);
-
+
return ret;
}
copy(char *dst, char *src, int exec)
{
Buf b;
-
+
if(vflag > 1)
xprintf("cp %s %s\n", src, dst);
"lib9",
"libbio",
"libmach",
-
+
"misc/pprof",
+ "cmd/addr2line",
"cmd/cov",
"cmd/nm",
+ "cmd/objdump",
"cmd/pack",
"cmd/prof",
"pkg/go/scanner",
"pkg/go/ast",
"pkg/go/parser",
- "pkg/go/build",
"pkg/os/exec",
"pkg/net/url",
"pkg/text/template/parse",
"pkg/text/template",
"pkg/go/doc",
+ "pkg/go/build",
"cmd/go",
};
"cmd/8c",
"cmd/8g",
"cmd/8l",
+ "cmd/addr2line",
"cmd/cc",
"cmd/cov",
"cmd/gc",
"cmd/go",
"cmd/nm",
+ "cmd/objdump",
"cmd/pack",
"cmd/prof",
"lib9",
int i, j, k;
Buf b, path;
Vec dir;
-
+
binit(&b);
binit(&path);
vinit(&dir);
-
+
for(i=0; i<nelem(cleantab); i++) {
bpathf(&path, "%s/src/%s", goroot, cleantab[i]);
xreaddir(&dir, bstr(&path));
if(rebuildall) {
// Remove object tree.
xremoveall(bpathf(&b, "%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch));
-
+
// Remove installed packages and tools.
xremoveall(bpathf(&b, "%s/pkg/%s_%s", goroot, gohostos, gohostarch));
xremoveall(bpathf(&b, "%s/pkg/%s_%s", goroot, goos, goarch));
if(argc > 0)
usage();
-
+
xprintf(format, "GOROOT", goroot);
xprintf(format, "GOBIN", gobin);
xprintf(format, "GOARCH", goarch);
clean();
goversion = findgoversion();
setup();
-
+
// For the main bootstrap, building for host os/arch.
oldgoos = goos;
oldgoarch = goarch;
gochar = gohostchar;
xsetenv("GOARCH", goarch);
xsetenv("GOOS", goos);
-
+
for(i=0; i<nelem(buildorder); i++) {
install(bprintf(&b, buildorder[i], gohostchar));
if(!streq(oldgochar, gohostchar) && xstrstr(buildorder[i], "%s"))
{
char *p;
Buf pwd, src, real_src;
-
+
binit(&pwd);
binit(&src);
binit(&real_src);
bfree(&pwd);
bfree(&src);
bfree(&real_src);
-
+
return p;
}
default:
usage();
}ARGEND
-
+
if(argc == 0)
install(defaulttarg());
binit(&b);
binit(&b1);
binit(&search);
-
+
xprintf("\n");
xprintf("---\n");
xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot);
"On OS X the debuggers must be installed setgrp procmod.\n"
"Read and run ./sudo.bash to install the debuggers.\n");
}
-
+
if(!streq(goroot_final, goroot)) {
xprintf("\n"
"The binaries expect %s to be copied or moved to %s\n",
--- /dev/null
+// 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
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/8db.c
//
-// Copyright © 1994-1999 Lucent Technologies Inc.
-// Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
-// Portions Copyright © 1997-1999 Vita Nuova Limited.
-// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
-// Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+// Copyright © 1994-1999 Lucent Technologies Inc.
+// Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+// Portions Copyright © 1997-1999 Vita Nuova Limited.
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+// Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
// Portions Copyright © 2009 The Go Authors. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
static void
pea(Instr *ip)
{
+ int base;
+
+ base = ip->base;
+ if(base >= 0 && (ip->rex & REXB))
+ base += 8;
+
if (ip->mod == 3) {
if (ip->osize == 'B')
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
- bprint(ip, "%s%s", ANAME(ip), reg[(uchar)ip->base]);
+ bprint(ip, "%s%s", ANAME(ip), reg[base]);
return;
}
+
if (ip->segment)
bprint(ip, ip->segment);
- if (ip->asize == 'E' && ip->base == SP)
+ if (ip->asize == 'E' && base == SP)
plocal(ip);
else {
if (ip->base < 0)
void
runtime·notesleep(Note *n)
{
+ if(m->profilehz > 0)
+ runtime·setprof(false);
while(runtime·atomicload(&n->key) == 0)
runtime·futexsleep(&n->key, 0, -1);
+ if(m->profilehz > 0)
+ runtime·setprof(true);
}
void
if(runtime·atomicload(&n->key) != 0)
return;
+ if(m->profilehz > 0)
+ runtime·setprof(false);
deadline = runtime·nanotime() + ns;
for(;;) {
runtime·futexsleep(&n->key, 0, ns);
if(runtime·atomicload(&n->key) != 0)
- return;
+ break;
now = runtime·nanotime();
if(now >= deadline)
- return;
+ break;
ns = deadline - now;
}
+ if(m->profilehz > 0)
+ runtime·setprof(true);
}
return;
}
// Queued. Sleep.
+ if(m->profilehz > 0)
+ runtime·setprof(false);
runtime·semasleep(-1);
+ if(m->profilehz > 0)
+ runtime·setprof(true);
}
void
return;
}
+ if(m->profilehz > 0)
+ runtime·setprof(false);
deadline = runtime·nanotime() + ns;
for(;;) {
// Registered. Sleep.
if(runtime·semasleep(ns) >= 0) {
// Acquired semaphore, semawakeup unregistered us.
// Done.
+ if(m->profilehz > 0)
+ runtime·setprof(true);
return;
}
ns = deadline - now;
}
+ if(m->profilehz > 0)
+ runtime·setprof(true);
+
// Deadline arrived. Still registered. Semaphore not acquired.
// Want to give up and return, but have to unregister first,
// so that any notewakeup racing with the return does not
#define NSIG 32
#define SI_USER 0 /* empirically true, but not what headers say */
+#define SIG_BLOCK 1
+#define SIG_UNBLOCK 2
#define SIG_SETMASK 3
+
void runtime·sigaction(int32, struct sigaction*, struct sigaction*);
void runtime·sigprocmask(Sigset *, Sigset *);
void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool);
-void runtiem·setitimerval(int32, Itimerval*, Itimerval*);
void runtime·setitimer(int32, Itimerval*, Itimerval*);
int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr);
void runtime·sigaction(int32, struct sigaction*, struct sigaction*);
void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool);
void runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp);
-void runtime·setitimerval(int32, Itimerval*, Itimerval*);
void runtime·setitimer(int32, Itimerval*, Itimerval*);
int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr);
void runtime·sigaction(int32, struct sigaction*, struct sigaction*);
void runtime·setsig(int32, void(*)(int32, Siginfo*, void*, G*), bool);
void runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp);
-void runtime·setitimerval(int32, Itimerval*, Itimerval*);
void runtime·setitimer(int32, Itimerval*, Itimerval*);
int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr);
"text/tabwriter"
)
-// BUG(rsc): CPU profiling is broken on OS X, due to an Apple kernel bug.
-// For details, see http://code.google.com/p/go/source/detail?r=35b716c94225.
+// BUG(rsc): A bug in the OS X Snow Leopard 64-bit kernel prevents
+// CPU profiling from giving accurate results on that system.
// A Profile is a collection of stack traces showing the call sequences
// that led to instances of a particular event, such as allocation.
}
// 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
// call to Remove. Add panics if the profile already contains a stack for value.
//
import (
"bytes"
"hash/crc32"
+ "os/exec"
"runtime"
. "runtime/pprof"
"strings"
func TestCPUProfile(t *testing.T) {
switch runtime.GOOS {
case "darwin":
- // see Apple Bug Report #9177434 (copied into change description)
- return
+ out, err := exec.Command("uname", "-a").CombinedOutput()
+ 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":
// unimplemented
return
m->mcache = runtime·allocmcache();
runtime·callers(1, m->createstack, nelem(m->createstack));
-
+
// Add to runtime·allm so garbage collector doesn't free m
// when it is just in a register or thread-local storage.
m->alllink = runtime·allm;
// so other calls can reuse this stack space.
runtime·gosave(&m->g0->sched);
m->g0->sched.pc = (void*)-1; // make sure it is never used
-
runtime·asminit();
runtime·minit();
schedule(nil);
{
uint32 v;
+ if(m->profilehz > 0)
+ runtime·setprof(false);
+
// Leave SP around for gc and traceback.
runtime·gosave(&g->sched);
g->gcsp = g->sched.sp;
// Garbage collector isn't running (since we are),
// so okay to clear gcstack.
g->gcstack = nil;
+
+ if(m->profilehz > 0)
+ runtime·setprof(true);
return;
}
void runtime·ifaceE2I(struct InterfaceType*, Eface, Iface*);
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);
r = &mc->ss;
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;
}
if(pc[0] == 0xF6 || pc[0] == 0xF7)
info->si_code = FPE_INTDIV;
}
-
+
// Make it look like a call to the signal func.
// Have to pass arguments out of band since
// augmenting the stack frame would break
r = &mc->ss;
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;
}
if(pc[0] == 0xF6 || pc[0] == 0xF7)
info->si_code = FPE_INTDIV;
}
-
+
// Make it look like a call to the signal func.
// Have to pass arguments out of band since
// augmenting the stack frame would break
gp->sigcode0 = info->si_code;
gp->sigcode1 = (uintptr)info->si_addr;
gp->sigpc = r->rip;
-
+
// Only push runtime·sigpanic if r->rip != 0.
// If r->rip == 0, probably panicked because of a
// call to a nil func. Not pushing that onto sp will
r->rip = (uintptr)runtime·sigpanic;
return;
}
-
+
if(info->si_code == SI_USER || (t->flags & SigNotify))
if(runtime·sigsend(sig))
return;
runtime·resetcpuprofiler(int32 hz)
{
Itimerval it;
-
+
runtime·memclr((byte*)&it, sizeof it);
if(hz == 0) {
runtime·setitimer(ITIMER_PROF, &it, nil);
- runtime·setsig(SIGPROF, SIG_IGN, true);
+ runtime·setprof(false);
} else {
- runtime·setsig(SIGPROF, runtime·sighandler, true);
it.it_interval.tv_sec = 0;
it.it_interval.tv_usec = 1000000 / hz;
it.it_value = it.it_interval;
runtime·setitimer(ITIMER_PROF, &it, nil);
+ runtime·setprof(true);
}
m->profilehz = hz;
}
IMULL $1000, BX
ADDL BX, AX
ADCL $0, DX
-
+
MOVL ret+0(FP), DI
MOVL AX, 0(DI)
MOVL DX, 4(DI)
RET
TEXT runtime·sigprocmask(SB),7,$0
- MOVL $48, AX
+ MOVL $329, AX // pthread_sigmask (on OS X, sigprocmask==entire process)
INT $0x80
JAE 2(PC)
CALL runtime·notok(SB)
MOVL 8(SP), DI
MOVQ 16(SP), SI
MOVQ 24(SP), DX
- MOVL $(0x2000000+48), AX // syscall entry
+ MOVL $(0x2000000+329), AX // pthread_sigmask (on OS X, sigprocmask==entire process)
SYSCALL
JCC 2(PC)
CALL runtime·notok(SB)
static Sigset sigset_all = ~(Sigset)0;
static Sigset sigset_none;
+static Sigset sigset_prof = 1<<(SIGPROF-1);
static void
unimplemented(int8 *name)
int32
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
runtime·sigprocmask(SIG_SETMASK, &sigset_all, &oset);
errno = runtime·bsdthread_create(stk, m, g, fn);
runtime·sigprocmask(SIG_SETMASK, &oset, nil);
-
+
if(errno < 0) {
runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\n", runtime·mcount(), -errno);
runtime·throw("runtime.newosproc");
// Initialize signal handling.
m->gsignal = runtime·malg(32*1024); // OS X wants >=8K, Linux >=2K
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
// the limit.
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);
+}
return rl.rlim_cur - used;
}
+
+void
+runtime·setprof(bool on)
+{
+ USED(on);
+}
Rlimit rl;
extern byte text[], end[];
uintptr used;
-
+
if(runtime·getrlimit(RLIMIT_AS, &rl) != 0)
return 0;
if(rl.rlim_cur >= 0x7fffffff)
return rl.rlim_cur - used;
}
+
+void
+runtime·setprof(bool on)
+{
+ USED(on);
+}
{
return 0;
}
+
+void
+runtime·setprof(bool on)
+{
+ USED(on);
+}
{
return 0;
}
+
+void
+runtime·setprof(bool on)
+{
+ USED(on);
+}
// The naïve implementation (without the cached
// file descriptor) is roughly four times slower
// in 9vx on a 2.16 GHz Intel Core 2 Duo.
-
+
if(fd < 0 && (fd = runtime·open((byte*)"/dev/bintime", OREAD|OCEXEC)) < 0)
return 0;
if(runtime·pread(fd, b, sizeof b, 0) != sizeof b)
{
return 0;
}
+
+void
+runtime·setprof(bool on)
+{
+ USED(on);
+}
time·now(int64 sec, int32 usec)
{
int64 ns;
-
+
ns = runtime·nanotime();
sec = ns / 1000000000LL;
usec = ns - sec * 1000000000LL;
{
return 0;
}
+
+void
+runtime·setprof(bool on)
+{
+ USED(on);
+}