blob: b8083b0ec4b5b925dffed2306596b80b3bbaeea8 [file] [log] [blame] [edit]
// 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 <u.h>
#include <libc.h>
#include <bio.h>
#include <link.h>
enum
{
HISTSZ = 10,
NSYM = 50,
};
int
linklinefmt(Link *ctxt, Fmt *fp)
{
struct
{
Hist* incl; /* start of this include file */
int32 idel; /* delta line number to apply to include */
Hist* line; /* start of this #line directive */
int32 ldel; /* delta line number to apply to #line */
} a[HISTSZ];
int32 lno, d;
int i, n;
Hist *h;
lno = va_arg(fp->args, int32);
n = 0;
for(h=ctxt->hist; h!=nil; h=h->link) {
if(h->offset < 0)
continue;
if(lno < h->line)
break;
if(h->name) {
if(h->offset > 0) {
// #line directive
if(n > 0 && n < HISTSZ) {
a[n-1].line = h;
a[n-1].ldel = h->line - h->offset + 1;
}
} else {
// beginning of file
if(n < HISTSZ) {
a[n].incl = h;
a[n].idel = h->line;
a[n].line = 0;
}
n++;
}
continue;
}
n--;
if(n > 0 && n < HISTSZ) {
d = h->line - a[n].incl->line;
a[n-1].ldel += d;
a[n-1].idel += d;
}
}
if(n > HISTSZ)
n = HISTSZ;
for(i=n-1; i>=0; i--) {
if(i != n-1) {
if(fp->flags & ~(FmtWidth|FmtPrec))
break;
fmtprint(fp, " ");
}
if(ctxt->debugline || (fp->flags&FmtLong))
fmtprint(fp, "%s/", ctxt->pathname);
if(a[i].line)
fmtprint(fp, "%s:%d[%s:%d]",
a[i].line->name, lno-a[i].ldel+1,
a[i].incl->name, lno-a[i].idel+1);
else
fmtprint(fp, "%s:%d",
a[i].incl->name, lno-a[i].idel+1);
lno = a[i].incl->line - 1; // now print out start of this file
}
if(n == 0)
fmtprint(fp, "<unknown line number>");
return 0;
}
// Does s have t as a path prefix?
// That is, does s == t or does s begin with t followed by a slash?
// For portability, we allow ASCII case folding, so that haspathprefix("a/b/c", "A/B") is true.
// Similarly, we allow slash folding, so that haspathprefix("a/b/c", "a\\b") is true.
static int
haspathprefix(char *s, char *t)
{
int i, cs, ct;
if(t == nil)
return 0;
for(i=0; t[i]; i++) {
cs = s[i];
ct = t[i];
if('A' <= cs && cs <= 'Z')
cs += 'a' - 'A';
if('A' <= ct && ct <= 'Z')
ct += 'a' - 'A';
if(cs == '\\')
cs = '/';
if(ct == '\\')
ct = '/';
if(cs != ct)
return 0;
}
return s[i] == '\0' || s[i] == '/' || s[i] == '\\';
}
// This is a simplified copy of linklinefmt above.
// It doesn't allow printing the full stack, and it returns the file name and line number separately.
// TODO: Unify with linklinefmt somehow.
void
linkgetline(Link *ctxt, int32 line, LSym **f, int32 *l)
{
struct
{
Hist* incl; /* start of this include file */
int32 idel; /* delta line number to apply to include */
Hist* line; /* start of this #line directive */
int32 ldel; /* delta line number to apply to #line */
} a[HISTSZ];
int32 lno, d, dlno;
int n;
Hist *h;
char buf[1024], buf1[1024], *file;
lno = line;
n = 0;
for(h=ctxt->hist; h!=nil; h=h->link) {
if(h->offset < 0)
continue;
if(lno < h->line)
break;
if(h->name) {
if(h->offset > 0) {
// #line directive
if(n > 0 && n < HISTSZ) {
a[n-1].line = h;
a[n-1].ldel = h->line - h->offset + 1;
}
} else {
// beginning of file
if(n < HISTSZ) {
a[n].incl = h;
a[n].idel = h->line;
a[n].line = 0;
}
n++;
}
continue;
}
n--;
if(n > 0 && n < HISTSZ) {
d = h->line - a[n].incl->line;
a[n-1].ldel += d;
a[n-1].idel += d;
}
}
if(n > HISTSZ)
n = HISTSZ;
if(n <= 0) {
*f = linklookup(ctxt, "??", HistVersion);
*l = 0;
return;
}
n--;
if(a[n].line) {
file = a[n].line->name;
dlno = a[n].ldel-1;
} else {
file = a[n].incl->name;
dlno = a[n].idel-1;
}
if((!ctxt->windows && file[0] == '/') || (ctxt->windows && file[1] == ':') || file[0] == '<')
snprint(buf, sizeof buf, "%s", file);
else
snprint(buf, sizeof buf, "%s/%s", ctxt->pathname, file);
// Remove leading ctxt->trimpath, or else rewrite $GOROOT to $GOROOT_FINAL.
if(haspathprefix(buf, ctxt->trimpath)) {
if(strlen(buf) == strlen(ctxt->trimpath))
strcpy(buf, "??");
else {
snprint(buf1, sizeof buf1, "%s", buf+strlen(ctxt->trimpath)+1);
if(buf1[0] == '\0')
strcpy(buf1, "??");
strcpy(buf, buf1);
}
} else if(ctxt->goroot_final != nil && haspathprefix(buf, ctxt->goroot)) {
snprint(buf1, sizeof buf1, "%s%s", ctxt->goroot_final, buf+strlen(ctxt->goroot));
strcpy(buf, buf1);
}
lno -= dlno;
*f = linklookup(ctxt, buf, HistVersion);
*l = lno;
}
void
linklinehist(Link *ctxt, int lineno, char *f, int offset)
{
Hist *h;
if(0) // debug['f']
if(f) {
if(offset)
print("%4d: %s (#line %d)\n", lineno, f, offset);
else
print("%4d: %s\n", lineno, f);
} else
print("%4d: <pop>\n", lineno);
h = malloc(sizeof(Hist));
memset(h, 0, sizeof *h);
h->name = f;
h->line = lineno;
h->offset = offset;
h->link = nil;
if(ctxt->ehist == nil) {
ctxt->hist = h;
ctxt->ehist = h;
return;
}
ctxt->ehist->link = h;
ctxt->ehist = h;
}
void
linkprfile(Link *ctxt, int32 l)
{
int i, n;
Hist a[HISTSZ], *h;
int32 d;
n = 0;
for(h = ctxt->hist; h != nil; h = h->link) {
if(l < h->line)
break;
if(h->name) {
if(h->offset == 0) {
if(n >= 0 && n < HISTSZ)
a[n] = *h;
n++;
continue;
}
if(n > 0 && n < HISTSZ)
if(a[n-1].offset == 0) {
a[n] = *h;
n++;
} else
a[n-1] = *h;
continue;
}
n--;
if(n >= 0 && n < HISTSZ) {
d = h->line - a[n].line;
for(i=0; i<n; i++)
a[i].line += d;
}
}
if(n > HISTSZ)
n = HISTSZ;
for(i=0; i<n; i++)
print("%s:%ld ", a[i].name, (long)(l-a[i].line+a[i].offset+1));
}
/*
* start a new Prog list.
*/
Plist*
linknewplist(Link *ctxt)
{
Plist *pl;
pl = malloc(sizeof(*pl));
memset(pl, 0, sizeof *pl);
if(ctxt->plist == nil)
ctxt->plist = pl;
else
ctxt->plast->link = pl;
ctxt->plast = pl;
return pl;
}