From 1c5438aae896edcd1e9f9618f4776517f08053b3 Mon Sep 17 00:00:00 2001 From: Andrew Gerrand Date: Mon, 6 May 2013 17:33:44 -0700 Subject: go1.1rc2 --- VERSION | 1 + src/cmd/cov/Makefile | 5 - src/cmd/cov/doc.go | 36 -- src/cmd/cov/main.c | 484 --------------------------- src/cmd/cov/tree.c | 245 -------------- src/cmd/cov/tree.h | 47 --- src/cmd/prof/Makefile | 5 - src/cmd/prof/doc.go | 49 --- src/cmd/prof/main.c | 910 -------------------------------------------------- 9 files changed, 1 insertion(+), 1781 deletions(-) create mode 100644 VERSION delete mode 100644 src/cmd/cov/Makefile delete mode 100644 src/cmd/cov/doc.go delete mode 100644 src/cmd/cov/main.c delete mode 100644 src/cmd/cov/tree.c delete mode 100644 src/cmd/cov/tree.h delete mode 100644 src/cmd/prof/Makefile delete mode 100644 src/cmd/prof/doc.go delete mode 100644 src/cmd/prof/main.c diff --git a/VERSION b/VERSION new file mode 100644 index 0000000000..f02a1e8c70 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +go1.1rc2 \ No newline at end of file diff --git a/src/cmd/cov/Makefile b/src/cmd/cov/Makefile deleted file mode 100644 index 3f528d7517..0000000000 --- a/src/cmd/cov/Makefile +++ /dev/null @@ -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 diff --git a/src/cmd/cov/doc.go b/src/cmd/cov/doc.go deleted file mode 100644 index ab5d1220ad..0000000000 --- a/src/cmd/cov/doc.go +++ /dev/null @@ -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 diff --git a/src/cmd/cov/main.c b/src/cmd/cov/main.c deleted file mode 100644 index 33ef49e17d..0000000000 --- a/src/cmd/cov/main.c +++ /dev/null @@ -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 -#include -#include -#include "tree.h" - -#include -#include -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 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; uvinstsize(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); -} - diff --git a/src/cmd/cov/tree.c b/src/cmd/cov/tree.c deleted file mode 100644 index 366a47efd4..0000000000 --- a/src/cmd/cov/tree.c +++ /dev/null @@ -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 -#include -#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; - } - } -} diff --git a/src/cmd/cov/tree.h b/src/cmd/cov/tree.h deleted file mode 100644 index a716d83ada..0000000000 --- a/src/cmd/cov/tree.h +++ /dev/null @@ -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*); diff --git a/src/cmd/prof/Makefile b/src/cmd/prof/Makefile deleted file mode 100644 index 3f528d7517..0000000000 --- a/src/cmd/prof/Makefile +++ /dev/null @@ -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 diff --git a/src/cmd/prof/doc.go b/src/cmd/prof/doc.go deleted file mode 100644 index 2640167d3f..0000000000 --- a/src/cmd/prof/doc.go +++ /dev/null @@ -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 diff --git a/src/cmd/prof/main.c b/src/cmd/prof/main.c deleted file mode 100644 index 6c591ba180..0000000000 --- a/src/cmd/prof/main.c +++ /dev/null @@ -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 -#include -#include -#include -#include - -#define Ureg Ureg_amd64 - #include -#undef Ureg -#define Ureg Ureg_x86 - #include -#undef Ureg -#include - -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); -} -- cgit v1.2.3-54-g00ecf