mirror of
https://github.com/golang/go
synced 2024-11-13 19:00:25 -07:00
code coverage tool
$ 6cov -g 235.go 6.out 235.go:62,62 main·main 0x27c9-0x2829 MOVL $main·.stringo(SB),AX 235.go:30,30 main·main 0x2856-0x285e ADDQ $6c0,SP $ and assorted fixes. R=r DELTA=743 (732 added, 8 deleted, 3 changed) OCL=19226 CL=19243
This commit is contained in:
parent
2355395550
commit
7832ab5ba0
28
src/cmd/cov/Makefile
Normal file
28
src/cmd/cov/Makefile
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
include ../../Make.conf
|
||||||
|
|
||||||
|
# The directory is cov because the source is portable and general.
|
||||||
|
# We call the binary 6cov to avoid confusion and because this binary
|
||||||
|
# is linked only with amd64 and x86 support.
|
||||||
|
|
||||||
|
TARG=6cov
|
||||||
|
OFILES=\
|
||||||
|
main.$O\
|
||||||
|
tree.$O\
|
||||||
|
|
||||||
|
HFILES=\
|
||||||
|
tree.h\
|
||||||
|
|
||||||
|
$(TARG): $(OFILES)
|
||||||
|
$(LD) -o $(TARG) -L$(GOROOT)/lib $(OFILES) -lmach_amd64 -lregexp9 -lbio -l9
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(OFILES) $(TARG)
|
||||||
|
|
||||||
|
install: $(TARG)
|
||||||
|
cp $(TARG) $(BIN)/$(TARG)
|
||||||
|
|
||||||
|
$(OFILES): $(HFILES)
|
385
src/cmd/cov/main.c
Normal file
385
src/cmd/cov/main.c
Normal file
@ -0,0 +1,385 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* code coverage
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <u.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <libc.h>
|
||||||
|
#include <bio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <regexp9.h>
|
||||||
|
#include "tree.h"
|
||||||
|
|
||||||
|
#include <ureg_amd64.h>
|
||||||
|
#include <mach_amd64.h>
|
||||||
|
typedef struct Ureg Ureg;
|
||||||
|
|
||||||
|
void
|
||||||
|
usage(void)
|
||||||
|
{
|
||||||
|
fprint(2, "usage: cov 6.out [-lv] [-g regexp] [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;
|
||||||
|
Map *mem;
|
||||||
|
Map *text;
|
||||||
|
Fhdr fhdr;
|
||||||
|
Reprog *grep;
|
||||||
|
char cwd[1000];
|
||||||
|
int ncwd;
|
||||||
|
|
||||||
|
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 %#lux+%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);
|
||||||
|
n->pc = epc;
|
||||||
|
n->epc = oldepc;
|
||||||
|
treeput(&breakpoints, n, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 src1[1000];
|
||||||
|
char src2[1000];
|
||||||
|
char buf[100];
|
||||||
|
Symbol s;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
if(!findsym(pc, CTEXT, &s) || !fileline(src1, sizeof src1, pc) || !fileline(src2, sizeof src2, pc)) {
|
||||||
|
print("%#llux-%#llux\n", pc, epc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pc == s.value) {
|
||||||
|
// never entered function
|
||||||
|
print("%s %s never called (%#llux-%#llux)\n", shortname(src1), 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.throwindex
|
||||||
|
buf[0] = 0;
|
||||||
|
machdata->das(text, pc, 0, buf, sizeof buf);
|
||||||
|
if(strstr(buf, "throwindex"))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// show first instruction to make clear where we were.
|
||||||
|
machdata->das(text, pc, 0, buf, sizeof buf);
|
||||||
|
|
||||||
|
// cut filename off src2, leaving just line number.
|
||||||
|
p = strrchr(src2, ':');
|
||||||
|
if(p != nil)
|
||||||
|
p++;
|
||||||
|
else
|
||||||
|
p = src2;
|
||||||
|
print("%s,%s %s %#llux-%#llux %s\n", shortname(src1), p, s.name, pc, epc, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
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(grep == nil || regexec9(grep, buf, nil, 0) > 0 || regexec9(grep, s.name, nil, 0) > 0)
|
||||||
|
lastpc = s.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uvlong
|
||||||
|
rgetzero(Map *map, char *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");
|
||||||
|
execv(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;
|
||||||
|
char *regexp;
|
||||||
|
|
||||||
|
ARGBEGIN{
|
||||||
|
case 'g':
|
||||||
|
regexp = EARGF(usage());
|
||||||
|
if((grep = regcomp9(regexp)) == nil)
|
||||||
|
sysfatal("bad regexp %s", regexp);
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
longnames++;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
chatty++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
}ARGEND
|
||||||
|
|
||||||
|
getwd(cwd, sizeof cwd);
|
||||||
|
ncwd = strlen(cwd);
|
||||||
|
|
||||||
|
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");
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
246
src/cmd/cov/tree.c
Normal file
246
src/cmd/cov/tree.c
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
// Renamed from Map to Tree to avoid conflict with libmach.
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 2003-2007 Russ Cox, Tom Bergan, Austin Clements,
|
||||||
|
Massachusetts Institute of Technology
|
||||||
|
Portions Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Mutable map structure, but still based on
|
||||||
|
// Okasaki, Red Black Trees in a Functional Setting, JFP 1999,
|
||||||
|
// which is a lot easier than the traditional red-black
|
||||||
|
// and plenty fast enough for me. (Also I could copy
|
||||||
|
// and edit fmap.c.)
|
||||||
|
|
||||||
|
#include <u.h>
|
||||||
|
#include <libc.h>
|
||||||
|
#include "tree.h"
|
||||||
|
|
||||||
|
#define TreeNode TreeNode
|
||||||
|
#define Tree Tree
|
||||||
|
|
||||||
|
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);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
src/cmd/cov/tree.h
Normal file
47
src/cmd/cov/tree.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// 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*);
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
include ../../Make.conf
|
include ../../Make.conf
|
||||||
|
|
||||||
# The directory is db because the source is portable and general.
|
# The directory is prof because the source is portable and general.
|
||||||
# We call the binary 6prof to avoid confusion and because this binary
|
# We call the binary 6prof to avoid confusion and because this binary
|
||||||
# is linked only with amd64 and x86 support.
|
# is linked only with amd64 and x86 support.
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include <mach_amd64.h>
|
#include <mach_amd64.h>
|
||||||
#include <ureg_amd64.h>
|
#include <ureg_amd64.h>
|
||||||
typedef struct Ureg Ureg;
|
typedef struct Ureg Ureg;
|
||||||
|
#undef waitpid /* want Unix waitpid, not Plan 9 */
|
||||||
|
|
||||||
extern mach_port_t mach_reply_port(void); // should be in system headers, is not
|
extern mach_port_t mach_reply_port(void); // should be in system headers, is not
|
||||||
|
|
||||||
@ -185,6 +186,7 @@ static int nthr;
|
|||||||
static pthread_mutex_t mu;
|
static pthread_mutex_t mu;
|
||||||
static pthread_cond_t cond;
|
static pthread_cond_t cond;
|
||||||
static void* excthread(void*);
|
static void* excthread(void*);
|
||||||
|
static void* waitthread(void*);
|
||||||
static mach_port_t excport;
|
static mach_port_t excport;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@ -215,9 +217,10 @@ addpid(int pid, int force)
|
|||||||
pthread_t p;
|
pthread_t p;
|
||||||
|
|
||||||
excport = mach_reply_port();
|
excport = mach_reply_port();
|
||||||
pthread_create(&p, nil, excthread, nil);
|
|
||||||
pthread_mutex_init(&mu, nil);
|
pthread_mutex_init(&mu, nil);
|
||||||
pthread_cond_init(&cond, nil);
|
pthread_cond_init(&cond, nil);
|
||||||
|
pthread_create(&p, nil, excthread, nil);
|
||||||
|
pthread_create(&p, nil, waitthread, (void*)(uintptr)pid);
|
||||||
first = 0;
|
first = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -483,6 +486,7 @@ machregrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr)
|
|||||||
uint nn;
|
uint nn;
|
||||||
mach_port_t thread;
|
mach_port_t thread;
|
||||||
int reg;
|
int reg;
|
||||||
|
char buf[100];
|
||||||
union {
|
union {
|
||||||
x86_thread_state64_t regs;
|
x86_thread_state64_t regs;
|
||||||
uchar p[1];
|
uchar p[1];
|
||||||
@ -517,7 +521,11 @@ machregrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr)
|
|||||||
if(me(thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)&u.regs, &nn)) < 0){
|
if(me(thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)&u.regs, &nn)) < 0){
|
||||||
if(!isr)
|
if(!isr)
|
||||||
thread_resume(thread);
|
thread_resume(thread);
|
||||||
werrstr("thread_get_state: %r");
|
rerrstr(buf, sizeof buf);
|
||||||
|
if(strcmp(buf, "send invalid dest") == 0)
|
||||||
|
werrstr("process exited");
|
||||||
|
else
|
||||||
|
werrstr("thread_get_state: %r");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -636,14 +644,15 @@ havet:
|
|||||||
ncode = nelem(t->code);
|
ncode = nelem(t->code);
|
||||||
memmove(t->code, code, ncode*sizeof t->code[0]);
|
memmove(t->code, code, ncode*sizeof t->code[0]);
|
||||||
|
|
||||||
|
// Suspend thread, so that we can look at it & restart it later.
|
||||||
|
if(me(thread_suspend(thread)) < 0)
|
||||||
|
fprint(2, "catch_exception_raise thread_suspend: %r\n");
|
||||||
|
|
||||||
// Synchronize with waitstop below.
|
// Synchronize with waitstop below.
|
||||||
pthread_mutex_lock(&mu);
|
pthread_mutex_lock(&mu);
|
||||||
pthread_cond_broadcast(&cond);
|
pthread_cond_broadcast(&cond);
|
||||||
pthread_mutex_unlock(&mu);
|
pthread_mutex_unlock(&mu);
|
||||||
|
|
||||||
// Suspend thread, so that we can look at it & restart it later.
|
|
||||||
if(me(thread_suspend(thread)) < 0)
|
|
||||||
fprint(2, "catch_exception_raise thread_suspend: %r\n");
|
|
||||||
return KERN_SUCCESS;
|
return KERN_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -656,12 +665,29 @@ excthread(void *v)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait for pid to exit.
|
||||||
|
static int exited;
|
||||||
|
static void*
|
||||||
|
waitthread(void *v)
|
||||||
|
{
|
||||||
|
int pid, status;
|
||||||
|
|
||||||
|
pid = (int)(uintptr)v;
|
||||||
|
waitpid(pid, &status, 0);
|
||||||
|
exited = 1;
|
||||||
|
// Synchronize with waitstop below.
|
||||||
|
pthread_mutex_lock(&mu);
|
||||||
|
pthread_cond_broadcast(&cond);
|
||||||
|
pthread_mutex_unlock(&mu);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for thread t to stop.
|
// Wait for thread t to stop.
|
||||||
static int
|
static int
|
||||||
waitstop(Thread *t)
|
waitstop(Thread *t)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&mu);
|
pthread_mutex_lock(&mu);
|
||||||
while(!threadstopped(t))
|
while(!exited && !threadstopped(t))
|
||||||
pthread_cond_wait(&cond, &mu);
|
pthread_cond_wait(&cond, &mu);
|
||||||
pthread_mutex_unlock(&mu);
|
pthread_mutex_unlock(&mu);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -672,7 +672,7 @@ textsym(Symbol *s, int index)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get ith file name
|
* Get ith file name
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
@ -894,7 +894,7 @@ file2pc(char *file, int32 line)
|
|||||||
if(name == 0) { /* encode the file name */
|
if(name == 0) { /* encode the file name */
|
||||||
werrstr("file %s not found", file);
|
werrstr("file %s not found", file);
|
||||||
return ~0;
|
return ~0;
|
||||||
}
|
}
|
||||||
/* find this history stack */
|
/* find this history stack */
|
||||||
for(i = 0, fp = files; i < nfiles; i++, fp++)
|
for(i = 0, fp = files; i < nfiles; i++, fp++)
|
||||||
if (hline(fp, name, &line))
|
if (hline(fp, name, &line))
|
||||||
@ -1019,7 +1019,7 @@ hline(File *fp, short *name, int32 *line)
|
|||||||
else if(depth++ == 1) /* push */
|
else if(depth++ == 1) /* push */
|
||||||
offset -= hp->line;
|
offset -= hp->line;
|
||||||
} else if(--depth == 1) /* pop */
|
} else if(--depth == 1) /* pop */
|
||||||
offset += hp->line;
|
offset += hp->line;
|
||||||
}
|
}
|
||||||
*line = ln+offset;
|
*line = ln+offset;
|
||||||
return 1;
|
return 1;
|
||||||
@ -1163,6 +1163,8 @@ fileelem(Sym **fp, uchar *cp, char *buf, int n)
|
|||||||
bp = buf;
|
bp = buf;
|
||||||
end = buf+n-1;
|
end = buf+n-1;
|
||||||
for(i = 1; j = (cp[i]<<8)|cp[i+1]; i+=2){
|
for(i = 1; j = (cp[i]<<8)|cp[i+1]; i+=2){
|
||||||
|
if(j >= fmaxi) // TODO(rsc): should not happen, but does!
|
||||||
|
break;
|
||||||
c = fp[j]->name;
|
c = fp[j]->name;
|
||||||
if(bp != buf && bp[-1] != '/' && bp < end)
|
if(bp != buf && bp[-1] != '/' && bp < end)
|
||||||
*bp++ = '/';
|
*bp++ = '/';
|
||||||
@ -1277,7 +1279,7 @@ pc2sp(uvlong pc)
|
|||||||
currsp += 4*u;
|
currsp += 4*u;
|
||||||
else if (u < 129)
|
else if (u < 129)
|
||||||
currsp -= 4*(u-64);
|
currsp -= 4*(u-64);
|
||||||
else
|
else
|
||||||
currpc += mach->pcquant*(u-129);
|
currpc += mach->pcquant*(u-129);
|
||||||
currpc += mach->pcquant;
|
currpc += mach->pcquant;
|
||||||
}
|
}
|
||||||
@ -1316,7 +1318,7 @@ pc2line(uvlong pc)
|
|||||||
currline += u;
|
currline += u;
|
||||||
else if(u < 129)
|
else if(u < 129)
|
||||||
currline -= (u-64);
|
currline -= (u-64);
|
||||||
else
|
else
|
||||||
currpc += mach->pcquant*(u-129);
|
currpc += mach->pcquant*(u-129);
|
||||||
currpc += mach->pcquant;
|
currpc += mach->pcquant;
|
||||||
}
|
}
|
||||||
@ -1371,7 +1373,7 @@ line2addr(int32 line, uvlong basepc, uvlong endpc)
|
|||||||
currline += u;
|
currline += u;
|
||||||
else if(u < 129)
|
else if(u < 129)
|
||||||
currline -= (u-64);
|
currline -= (u-64);
|
||||||
else
|
else
|
||||||
currpc += mach->pcquant*(u-129);
|
currpc += mach->pcquant*(u-129);
|
||||||
currpc += mach->pcquant;
|
currpc += mach->pcquant;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user