blob: adb7661301c1ea7efcd8881d72c575430a36c536 [file] [log] [blame]
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <link.h>
#include "lib.h"
#include "../../runtime/funcdata.h"
static void
addvarint(Pcdata *d, uint32 val)
{
int32 n;
uint32 v;
uchar *p;
n = 0;
for(v = val; v >= 0x80; v >>= 7)
n++;
n++;
if(d->n + n > d->m) {
d->m = (d->n + n)*2;
d->p = erealloc(d->p, d->m);
}
p = d->p + d->n;
for(v = val; v >= 0x80; v >>= 7)
*p++ = v | 0x80;
*p = v;
d->n += n;
}
static int32
addpctab(LSym *ftab, int32 off, Pcdata *d)
{
int32 start;
start = ftab->np;
symgrow(ctxt, ftab, start + d->n);
memmove(ftab->p + start, d->p, d->n);
return setuint32(ctxt, ftab, off, start);
}
static int32
ftabaddstring(LSym *ftab, char *s)
{
int32 n, start;
n = strlen(s)+1;
start = ftab->np;
symgrow(ctxt, ftab, start+n+1);
strcpy((char*)ftab->p + start, s);
return start;
}
static void
renumberfiles(Link *ctxt, LSym **files, int nfiles, Pcdata *d)
{
int i;
LSym *f;
Pcdata out;
Pciter it;
uint32 v;
int32 oldval, newval, val, dv;
// Give files numbers.
for(i=0; i<nfiles; i++) {
f = files[i];
if(f->type != SFILEPATH) {
f->value = ++ctxt->nhistfile;
f->type = SFILEPATH;
f->next = ctxt->filesyms;
ctxt->filesyms = f;
}
}
newval = -1;
memset(&out, 0, sizeof out);
for(pciterinit(ctxt, &it, d); !it.done; pciternext(&it)) {
// value delta
oldval = it.value;
if(oldval == -1)
val = -1;
else {
if(oldval < 0 || oldval >= nfiles)
sysfatal("bad pcdata %d", oldval);
val = files[oldval]->value;
}
dv = val - newval;
newval = val;
v = ((uint32)dv<<1) ^ (uint32)(int32)(dv>>31);
addvarint(&out, v);
// pc delta
addvarint(&out, (it.nextpc - it.pc) / it.pcscale);
}
// terminating value delta
addvarint(&out, 0);
free(d->p);
*d = out;
}
static int
container(LSym *s)
{
// We want to generate func table entries only for the "lowest level" symbols,
// not containers of subsymbols.
if(s != nil && s->sub != nil)
return 1;
return 0;
}
// pclntab initializes the pclntab symbol with
// runtime function and file name information.
void
pclntab(void)
{
int32 i, nfunc, start, funcstart;
LSym *ftab, *s, *last;
int32 off, end, frameptrsize;
int64 funcdata_bytes;
Pcln *pcln;
Pciter it;
static Pcln zpcln;
funcdata_bytes = 0;
ftab = linklookup(ctxt, "runtime.pclntab", 0);
ftab->type = SPCLNTAB;
ftab->reachable = 1;
// See golang.org/s/go12symtab for the format. Briefly:
// 8-byte header
// nfunc [thearch.ptrsize bytes]
// function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes]
// end PC [thearch.ptrsize bytes]
// offset to file table [4 bytes]
nfunc = 0;
for(ctxt->cursym = ctxt->textp; ctxt->cursym != nil; ctxt->cursym = ctxt->cursym->next) {
if(!container(ctxt->cursym))
nfunc++;
}
symgrow(ctxt, ftab, 8+thearch.ptrsize+nfunc*2*thearch.ptrsize+thearch.ptrsize+4);
setuint32(ctxt, ftab, 0, 0xfffffffb);
setuint8(ctxt, ftab, 6, thearch.minlc);
setuint8(ctxt, ftab, 7, thearch.ptrsize);
setuintxx(ctxt, ftab, 8, nfunc, thearch.ptrsize);
nfunc = 0;
last = nil;
for(ctxt->cursym = ctxt->textp; ctxt->cursym != nil; ctxt->cursym = ctxt->cursym->next) {
last = ctxt->cursym;
if(container(ctxt->cursym))
continue;
pcln = ctxt->cursym->pcln;
if(pcln == nil)
pcln = &zpcln;
funcstart = ftab->np;
funcstart += -ftab->np & (thearch.ptrsize-1);
setaddr(ctxt, ftab, 8+thearch.ptrsize+nfunc*2*thearch.ptrsize, ctxt->cursym);
setuintxx(ctxt, ftab, 8+thearch.ptrsize+nfunc*2*thearch.ptrsize+thearch.ptrsize, funcstart, thearch.ptrsize);
// fixed size of struct, checked below
off = funcstart;
end = funcstart + thearch.ptrsize + 3*4 + 5*4 + pcln->npcdata*4 + pcln->nfuncdata*thearch.ptrsize;
if(pcln->nfuncdata > 0 && (end&(thearch.ptrsize-1)))
end += 4;
symgrow(ctxt, ftab, end);
// entry uintptr
off = setaddr(ctxt, ftab, off, ctxt->cursym);
// name int32
off = setuint32(ctxt, ftab, off, ftabaddstring(ftab, ctxt->cursym->name));
// args int32
// TODO: Move into funcinfo.
off = setuint32(ctxt, ftab, off, ctxt->cursym->args);
// frame int32
// TODO: Remove entirely. The pcsp table is more precise.
// This is only used by a fallback case during stack walking
// when a called function doesn't have argument information.
// We need to make sure everything has argument information
// and then remove this.
frameptrsize = thearch.ptrsize;
if(ctxt->cursym->leaf)
frameptrsize = 0;
off = setuint32(ctxt, ftab, off, ctxt->cursym->locals + frameptrsize);
if(pcln != &zpcln) {
renumberfiles(ctxt, pcln->file, pcln->nfile, &pcln->pcfile);
if(0) {
// Sanity check the new numbering
for(pciterinit(ctxt, &it, &pcln->pcfile); !it.done; pciternext(&it)) {
if(it.value < 1 || it.value > ctxt->nhistfile) {
diag("bad file number in pcfile: %d not in range [1, %d]\n", it.value, ctxt->nhistfile);
errorexit();
}
}
}
}
// pcdata
off = addpctab(ftab, off, &pcln->pcsp);
off = addpctab(ftab, off, &pcln->pcfile);
off = addpctab(ftab, off, &pcln->pcline);
off = setuint32(ctxt, ftab, off, pcln->npcdata);
off = setuint32(ctxt, ftab, off, pcln->nfuncdata);
for(i=0; i<pcln->npcdata; i++)
off = addpctab(ftab, off, &pcln->pcdata[i]);
// funcdata, must be pointer-aligned and we're only int32-aligned.
// Missing funcdata will be 0 (nil pointer).
if(pcln->nfuncdata > 0) {
if(off&(thearch.ptrsize-1))
off += 4;
for(i=0; i<pcln->nfuncdata; i++) {
if(pcln->funcdata[i] == nil)
setuintxx(ctxt, ftab, off+thearch.ptrsize*i, pcln->funcdataoff[i], thearch.ptrsize);
else {
// TODO: Dedup.
funcdata_bytes += pcln->funcdata[i]->size;
setaddrplus(ctxt, ftab, off+thearch.ptrsize*i, pcln->funcdata[i], pcln->funcdataoff[i]);
}
}
off += pcln->nfuncdata*thearch.ptrsize;
}
if(off != end) {
diag("bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, pcln->npcdata, pcln->nfuncdata, thearch.ptrsize);
errorexit();
}
nfunc++;
}
// Final entry of table is just end pc.
setaddrplus(ctxt, ftab, 8+thearch.ptrsize+nfunc*2*thearch.ptrsize, last, last->size);
// Start file table.
start = ftab->np;
start += -ftab->np & (thearch.ptrsize-1);
setuint32(ctxt, ftab, 8+thearch.ptrsize+nfunc*2*thearch.ptrsize+thearch.ptrsize, start);
symgrow(ctxt, ftab, start+(ctxt->nhistfile+1)*4);
setuint32(ctxt, ftab, start, ctxt->nhistfile);
for(s = ctxt->filesyms; s != nil; s = s->next)
setuint32(ctxt, ftab, start + s->value*4, ftabaddstring(ftab, s->name));
ftab->size = ftab->np;
if(debug['v'])
Bprint(&bso, "%5.2f pclntab=%lld bytes, funcdata total %lld bytes\n", cputime(), (vlong)ftab->size, (vlong)funcdata_bytes);
}
enum {
BUCKETSIZE = 256*MINFUNC,
SUBBUCKETS = 16,
SUBBUCKETSIZE = BUCKETSIZE/SUBBUCKETS,
NOIDX = 0x7fffffff
};
// findfunctab generates a lookup table to quickly find the containing
// function for a pc. See src/runtime/symtab.go:findfunc for details.
void
findfunctab(void)
{
LSym *t, *s, *e;
int32 idx, i, j, nbuckets, n, base;
vlong min, max, p, q;
int32 *indexes;
t = linklookup(ctxt, "runtime.findfunctab", 0);
t->type = SRODATA;
t->reachable = 1;
// find min and max address
min = ctxt->textp->value;
max = 0;
for(s = ctxt->textp; s != nil; s = s->next)
max = s->value + s->size;
// for each subbucket, compute the minimum of all symbol indexes
// that map to that subbucket.
n = (max-min+SUBBUCKETSIZE-1)/SUBBUCKETSIZE;
indexes = (int32*)malloc(n*4);
if(indexes == nil) {
diag("out of memory");
errorexit();
}
for(i = 0; i < n; i++)
indexes[i] = NOIDX;
idx = 0;
for(s = ctxt->textp; s != nil; s = s->next) {
if(container(s))
continue;
p = s->value;
e = s->next;
while(container(e))
e = e->next;
if(e != nil)
q = e->value;
else
q = max;
//print("%d: [%lld %lld] %s\n", idx, p, q, s->name);
for(; p < q; p += SUBBUCKETSIZE) {
i = (p - min) / SUBBUCKETSIZE;
if(indexes[i] > idx)
indexes[i] = idx;
}
i = (q - 1 - min) / SUBBUCKETSIZE;
if(indexes[i] > idx)
indexes[i] = idx;
idx++;
}
// allocate table
nbuckets = (max-min+BUCKETSIZE-1)/BUCKETSIZE;
symgrow(ctxt, t, 4*nbuckets + n);
// fill in table
for(i = 0; i < nbuckets; i++) {
base = indexes[i*SUBBUCKETS];
if(base == NOIDX)
diag("hole in findfunctab");
setuint32(ctxt, t, i*(4+SUBBUCKETS), base);
for(j = 0; j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++) {
idx = indexes[i*SUBBUCKETS+j];
if(idx == NOIDX)
diag("hole in findfunctab");
if(idx - base >= 256) {
diag("too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base);
}
setuint8(ctxt, t, i*(4+SUBBUCKETS)+4+j, idx-base);
}
}
free(indexes);
}