blob: 4c2ffa78e1d3c619d6aac549ac0939a2fc0d5eba [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 "l.h"
#include "lib.h"
#include "../../pkg/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;
}
// pclntab initializes the pclntab symbol with
// runtime function and file name information.
void
pclntab(void)
{
int32 i, nfunc, start, funcstart;
LSym *ftab, *s;
int32 off, end, frameptrsize;
int64 funcdata_bytes;
Pcln *pcln;
Pciter it;
static Pcln zpcln;
funcdata_bytes = 0;
ftab = linklookup(ctxt, "pclntab", 0);
ftab->type = SPCLNTAB;
ftab->reachable = 1;
// See golang.org/s/go12symtab for the format. Briefly:
// 8-byte header
// nfunc [PtrSize bytes]
// function table, alternating PC and offset to func struct [each entry PtrSize bytes]
// end PC [PtrSize bytes]
// offset to file table [4 bytes]
nfunc = 0;
for(ctxt->cursym = ctxt->textp; ctxt->cursym != nil; ctxt->cursym = ctxt->cursym->next)
nfunc++;
symgrow(ctxt, ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize+4);
setuint32(ctxt, ftab, 0, 0xfffffffb);
setuint8(ctxt, ftab, 6, MINLC);
setuint8(ctxt, ftab, 7, PtrSize);
setuintxx(ctxt, ftab, 8, nfunc, PtrSize);
nfunc = 0;
for(ctxt->cursym = ctxt->textp; ctxt->cursym != nil; ctxt->cursym = ctxt->cursym->next, nfunc++) {
pcln = ctxt->cursym->pcln;
if(pcln == nil)
pcln = &zpcln;
funcstart = ftab->np;
funcstart += -ftab->np & (PtrSize-1);
setaddr(ctxt, ftab, 8+PtrSize+nfunc*2*PtrSize, ctxt->cursym);
setuintxx(ctxt, ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize, funcstart, PtrSize);
// fixed size of struct, checked below
off = funcstart;
end = funcstart + PtrSize + 3*4 + 5*4 + pcln->npcdata*4 + pcln->nfuncdata*PtrSize;
if(pcln->nfuncdata > 0 && (end&(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 = 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&(PtrSize-1))
off += 4;
for(i=0; i<pcln->nfuncdata; i++) {
if(pcln->funcdata[i] == nil)
setuintxx(ctxt, ftab, off+PtrSize*i, pcln->funcdataoff[i], PtrSize);
else {
// TODO: Dedup.
funcdata_bytes += pcln->funcdata[i]->size;
setaddrplus(ctxt, ftab, off+PtrSize*i, pcln->funcdata[i], pcln->funcdataoff[i]);
}
}
off += pcln->nfuncdata*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, PtrSize);
errorexit();
}
// Final entry of table is just end pc.
if(ctxt->cursym->next == nil)
setaddrplus(ctxt, ftab, 8+PtrSize+(nfunc+1)*2*PtrSize, ctxt->cursym, ctxt->cursym->size);
}
// Start file table.
start = ftab->np;
start += -ftab->np & (PtrSize-1);
setuint32(ctxt, ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize, start);
symgrow(ctxt, ftab, start+(ctxt->nhistfile+1)*4);
setuint32(ctxt, ftab, start, ctxt->nhistfile);
for(s = ctxt->filesyms; s != S; 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);
}