From patchwork Wed Jan 30 22:24:44 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ian Lance Taylor X-Patchwork-Id: 217002 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id BCD9F2C007C for ; Thu, 31 Jan 2013 09:25:00 +1100 (EST) Comment: DKIM? See http://www.dkim.org DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=gcc.gnu.org; s=default; x=1360189501; h=Comment: DomainKey-Signature:Received:Received:Received:Received:Received: From:To:Subject:Date:Message-ID:User-Agent:MIME-Version: Content-Type:Mailing-List:Precedence:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:Sender:Delivered-To; bh=E2Let2L CXCzuE5t+ON96eZbnoGc=; b=Rihmvap7vIt90/xeD9W28qRB2+fQUZ1ihlSTaJR CDf+4dCMtbRQOPc5rYiT4n9wbhS5myhD8yH9D9bD2kXvEGBpqNebU3TPcehuDcLs T5H8mPaykesSpeEsqOpFjrOzrub8nqsgE4eNCbrm8uuVPhmi5QkEZnUkyDgPLg8T K+NY= Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=default; d=gcc.gnu.org; h=Received:Received:X-SWARE-Spam-Status:X-Spam-Check-By:Received:Received:X-Google-DKIM-Signature:X-Received:Received:From:To:Subject:Date:Message-ID:User-Agent:MIME-Version:Content-Type:X-Gm-Message-State:X-IsSubscribed:Mailing-List:Precedence:List-Id:List-Unsubscribe:List-Archive:List-Post:List-Help:Sender:Delivered-To; b=yea+d0Dvc9R7wIrsTFuYJ87rSEeNre6YCg/mWibwfwOdGJKVRwDUEK1JOfqMnx Xg1qkoIk4j9jXMvJrigovJhiOlsvqCu5CrsNStMgT9yDmqcAGSZ9//nBKcp53PRC lDS+osP8zUNtXfrWmG1Uum4NZ4XQKJRObjARbvnYcHNnQ=; Received: (qmail 25597 invoked by alias); 30 Jan 2013 22:24:57 -0000 Received: (qmail 25586 invoked by uid 22791); 30 Jan 2013 22:24:56 -0000 X-SWARE-Spam-Status: No, hits=-5.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, KHOP_RCVD_TRUST, RCVD_IN_DNSWL_LOW, RCVD_IN_HOSTKARMA_YE, RP_MATCHES_RCVD, T_TVD_MIME_NO_HEADERS X-Spam-Check-By: sourceware.org Received: from mail-pb0-f44.google.com (HELO mail-pb0-f44.google.com) (209.85.160.44) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Wed, 30 Jan 2013 22:24:47 +0000 Received: by mail-pb0-f44.google.com with SMTP id wz12so1220379pbc.31 for ; Wed, 30 Jan 2013 14:24:46 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:from:to:subject:date:message-id:user-agent:mime-version :content-type:x-gm-message-state; bh=Pj+gc+iUI3dh6+c2MZLP4HxvGIbG9LO8WKXvsvys/UA=; b=ayirIdJ4P12z6uIj6OBJZ316guY4u4FnKCi7CRPebEn7xEiHWkzrXoTViLGAFBpt0g uWTEe4ntwQdOmCVz1zZxiuk74HwM7qUavkOaZlo+egThZYFXCpaz69nH27bJJWp5/p26 rrDR1Q+8YLKlCzFWvwaMYwEfyUMxvkvpvFbyAiLlT93EtkX4yqs+1Lkk1urbgIar1okb hpo82pnd8b+usHYSEYlauVhtPOujRJMxBdUhamlxObvVwn7X7DX+XYgXAncin/XcBYrg LtTtV7iSg77Crd/UEfK1jxFfOVp8TA19a/v/E14QEl357t52xDeF7G9gnzVDdJv+d2CE FLtQ== X-Received: by 10.68.202.65 with SMTP id kg1mr16370051pbc.28.1359584686356; Wed, 30 Jan 2013 14:24:46 -0800 (PST) Received: from iant-glaptop.google.com ([2620:0:1000:3204:4d68:d7d7:afb8:f869]) by mx.google.com with ESMTPS id l8sm3279447pax.9.2013.01.30.14.24.45 (version=TLSv1.2 cipher=RC4-SHA bits=128/128); Wed, 30 Jan 2013 14:24:45 -0800 (PST) From: Ian Lance Taylor To: gcc-patches@gcc.gnu.org, gofrontend-dev@googlegroups.com Subject: libgo patch committed: Better backtraces Date: Wed, 30 Jan 2013 14:24:44 -0800 Message-ID: User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/23.3 (gnu/linux) MIME-Version: 1.0 X-Gm-Message-State: ALoCoQl+41JqSgEPeQ6uUn3mMtoTPErbAFFnbee/G3/32wL/M8Jim0yeilQMaaS/glW7jxHfJur0qOE/yd0pIXp347ULZ7vlPHw7pRZWQOX1hSaye4AJ+OddvQxY/XtXcKdMyEfaRSdQZ2mR27AQJwLKnPXA1E0uCmG8aubwrHBrvdK1O3AnKIKD+dA12VLtgLpZgpsyr2Nj X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org This patch to libgo fixes backtrace information when optimizing. Previously the backtraces just collected PC values and then converted them into file/line information. This of course works poorly when functions are inlined. This version uses the facilities of libbacktrace to get good backtraces in the presence of inlined functions. It also skips the split-stack functions, which are not of interest when getting a Go backtrace. Bootstrapped and ran Go testsuite on x86_64-unknown-linux-gnu. Committed to mainline. Ian diff -r 6edf35ba05b2 libgo/runtime/go-caller.c --- a/libgo/runtime/go-caller.c Tue Jan 29 17:30:28 2013 -0800 +++ b/libgo/runtime/go-caller.c Wed Jan 30 13:53:47 2013 -0800 @@ -166,16 +166,16 @@ Caller (int skip) { struct caller_ret ret; - uintptr pc; + Location loc; int32 n; - String fn; runtime_memclr (&ret, sizeof ret); - n = runtime_callers (skip + 1, &pc, 1); + n = runtime_callers (skip + 1, &loc, 1); if (n < 1) return ret; - ret.pc = pc; - __go_file_line (pc, &fn, &ret.file, &ret.line); + ret.pc = loc.pc; + ret.file = loc.filename; + ret.line = loc.lineno; ret.ok = 1; return ret; } diff -r 6edf35ba05b2 libgo/runtime/go-callers.c --- a/libgo/runtime/go-callers.c Tue Jan 29 17:30:28 2013 -0800 +++ b/libgo/runtime/go-callers.c Wed Jan 30 13:53:47 2013 -0800 @@ -15,20 +15,37 @@ struct callers_data { - uintptr *pcbuf; + Location *locbuf; int index; int max; }; -/* Callback function for backtrace_simple. Just collect the PC - values. Return zero to continue, non-zero to stop. */ +/* Callback function for backtrace_full. Just collect the locations. + Return zero to continue, non-zero to stop. */ static int -callback (void *data, uintptr_t pc) +callback (void *data, uintptr_t pc, const char *filename, int lineno, + const char *function) { struct callers_data *arg = (struct callers_data *) data; + Location *loc; - arg->pcbuf[arg->index] = pc; + /* Skip split stack functions. */ + if (function != NULL) + { + const char *p = function; + + if (__builtin_strncmp (p, "___", 3) == 0) + ++p; + if (__builtin_strncmp (p, "__morestack_", 12) == 0) + return 0; + } + + loc = &arg->locbuf[arg->index]; + loc->pc = pc; + loc->filename = runtime_gostring ((const byte *) filename); + loc->function = runtime_gostring ((const byte *) function); + loc->lineno = lineno; ++arg->index; return arg->index >= arg->max; } @@ -47,15 +64,15 @@ /* Gather caller PC's. */ int32 -runtime_callers (int32 skip, uintptr *pcbuf, int32 m) +runtime_callers (int32 skip, Location *locbuf, int32 m) { struct callers_data data; - data.pcbuf = pcbuf; + data.locbuf = locbuf; data.index = 0; data.max = m; - backtrace_simple (__go_get_backtrace_state (), skip + 1, callback, - error_callback, &data); + backtrace_full (__go_get_backtrace_state (), skip + 1, callback, + error_callback, &data); return data.index; } @@ -65,8 +82,19 @@ int Callers (int skip, struct __go_open_array pc) { + Location *locbuf; + int ret; + int i; + + locbuf = (Location *) runtime_mal (pc.__count * sizeof (Location)); + /* In the Go 1 release runtime.Callers has an off-by-one error, which we can not correct because it would break backward compatibility. Adjust SKIP here to be compatible. */ - return runtime_callers (skip - 1, (uintptr *) pc.__values, pc.__count); + ret = runtime_callers (skip - 1, locbuf, pc.__count); + + for (i = 0; i < ret; i++) + ((uintptr *) pc.__values)[i] = locbuf[i].pc; + + return ret; } diff -r 6edf35ba05b2 libgo/runtime/go-traceback.c --- a/libgo/runtime/go-traceback.c Tue Jan 29 17:30:28 2013 -0800 +++ b/libgo/runtime/go-traceback.c Wed Jan 30 13:53:47 2013 -0800 @@ -13,29 +13,25 @@ void runtime_traceback () { - uintptr pcbuf[100]; + Location locbuf[100]; int32 c; - c = runtime_callers (1, pcbuf, sizeof pcbuf / sizeof pcbuf[0]); - runtime_printtrace (pcbuf, c, true); + c = runtime_callers (1, locbuf, nelem (locbuf)); + runtime_printtrace (locbuf, c, true); } void -runtime_printtrace (uintptr *pcbuf, int32 c, bool current) +runtime_printtrace (Location *locbuf, int32 c, bool current) { int32 i; for (i = 0; i < c; ++i) { - String fn; - String file; - intgo line; - - if (__go_file_line (pcbuf[i], &fn, &file, &line) - && runtime_showframe (fn, current)) + if (runtime_showframe (locbuf[i].function, current)) { - runtime_printf ("%S\n", fn); - runtime_printf ("\t%S:%D\n", file, (int64) line); + runtime_printf ("%S\n", locbuf[i].function); + runtime_printf ("\t%S:%D\n", locbuf[i].filename, + (int64) locbuf[i].lineno); } } } diff -r 6edf35ba05b2 libgo/runtime/mprof.goc --- a/libgo/runtime/mprof.goc Tue Jan 29 17:30:28 2013 -0800 +++ b/libgo/runtime/mprof.goc Wed Jan 30 13:53:47 2013 -0800 @@ -11,6 +11,7 @@ #include "malloc.h" #include "defs.h" #include "go-type.h" +#include "go-string.h" // NOTE(rsc): Everything here could use cas if contention became an issue. static Lock proflock; @@ -46,7 +47,7 @@ }; uintptr hash; uintptr nstk; - uintptr stk[1]; + Location stk[1]; }; enum { BuckHashSize = 179999, @@ -58,9 +59,9 @@ // Return the bucket for stk[0:nstk], allocating new bucket if needed. static Bucket* -stkbucket(int32 typ, uintptr *stk, int32 nstk, bool alloc) +stkbucket(int32 typ, Location *stk, int32 nstk, bool alloc) { - int32 i; + int32 i, j; uintptr h; Bucket *b; @@ -72,7 +73,7 @@ // Hash stack. h = 0; for(i=0; i>6; } @@ -80,10 +81,18 @@ h ^= h>>11; i = h%BuckHashSize; - for(b = buckhash[i]; b; b=b->next) - if(b->typ == typ && b->hash == h && b->nstk == (uintptr)nstk && - runtime_mcmp((byte*)b->stk, (byte*)stk, nstk*sizeof stk[0]) == 0) - return b; + for(b = buckhash[i]; b; b=b->next) { + if(b->typ == typ && b->hash == h && b->nstk == (uintptr)nstk) { + for(j = 0; j < nstk; j++) { + if(b->stk[j].pc != stk[j].pc || + b->stk[j].lineno != stk[j].lineno || + !__go_strings_equal(b->stk[j].filename, stk[j].filename)) + break; + } + if (j == nstk) + return b; + } + } if(!alloc) return nil; @@ -241,7 +250,7 @@ { M *m; int32 nstk; - uintptr stk[32]; + Location stk[32]; Bucket *b; m = runtime_m(); @@ -298,7 +307,7 @@ { int32 nstk; int64 rate; - uintptr stk[32]; + Location stk[32]; Bucket *b; if(cycles <= 0) @@ -336,7 +345,7 @@ r->alloc_objects = b->allocs; r->free_objects = b->frees; for(i=0; instk && istk); i++) - r->stk[i] = b->stk[i]; + r->stk[i] = b->stk[i].pc; for(; istk); i++) r->stk[i] = 0; } @@ -396,7 +405,7 @@ r->count = b->count; r->cycles = b->cycles; for(i=0; (uintptr)instk && (uintptr)istk); i++) - r->stk[i] = b->stk[i]; + r->stk[i] = b->stk[i].pc; for(; (uintptr)istk); i++) r->stk[i] = 0; } @@ -413,6 +422,7 @@ func ThreadCreateProfile(p Slice) (n int, ok bool) { TRecord *r; M *first, *mp; + int32 i; first = runtime_atomicloadp(&runtime_allm); n = 0; @@ -423,7 +433,9 @@ ok = true; r = (TRecord*)p.__values; for(mp=first; mp; mp=mp->alllink) { - runtime_memmove(r->stk, mp->createstack, sizeof r->stk); + for(i = 0; (uintptr)i < nelem(r->stk); i++) { + r->stk[i] = mp->createstack[i].pc; + } r++; } } @@ -473,10 +485,14 @@ static void saveg(G *gp, TRecord *r) { - int32 n; + int32 n, i; + Location locstk[nelem(r->stk)]; - if(gp == runtime_g()) - n = runtime_callers(0, r->stk, nelem(r->stk)); + if(gp == runtime_g()) { + n = runtime_callers(0, locstk, nelem(r->stk)); + for(i = 0; i < n; i++) + r->stk[i] = locstk[i].pc; + } else { // FIXME: Not implemented. n = 0; diff -r 6edf35ba05b2 libgo/runtime/proc.c --- a/libgo/runtime/proc.c Tue Jan 29 17:30:28 2013 -0800 +++ b/libgo/runtime/proc.c Wed Jan 30 13:53:47 2013 -0800 @@ -631,7 +631,7 @@ struct Traceback { G* gp; - uintptr pcbuf[100]; + Location locbuf[100]; int32 c; }; @@ -677,7 +677,7 @@ runtime_gogo(gp); } - runtime_printtrace(tb.pcbuf, tb.c, false); + runtime_printtrace(tb.locbuf, tb.c, false); runtime_goroutinetrailer(gp); } } @@ -692,8 +692,8 @@ traceback = gp->traceback; gp->traceback = nil; - traceback->c = runtime_callers(1, traceback->pcbuf, - sizeof traceback->pcbuf / sizeof traceback->pcbuf[0]); + traceback->c = runtime_callers(1, traceback->locbuf, + sizeof traceback->locbuf / sizeof traceback->locbuf[0]); runtime_gogo(traceback->gp); } @@ -1742,13 +1742,14 @@ void (*fn)(uintptr*, int32); int32 hz; uintptr pcbuf[100]; + Location locbuf[100]; } prof; // Called if we receive a SIGPROF signal. void runtime_sigprof() { - int32 n; + int32 n, i; if(prof.fn == nil || prof.hz == 0) return; @@ -1758,7 +1759,9 @@ runtime_unlock(&prof); return; } - n = runtime_callers(0, prof.pcbuf, nelem(prof.pcbuf)); + n = runtime_callers(0, prof.locbuf, nelem(prof.locbuf)); + for(i = 0; i < n; i++) + prof.pcbuf[i] = prof.locbuf[i].pc; if(n > 0) prof.fn(prof.pcbuf, n); runtime_unlock(&prof); diff -r 6edf35ba05b2 libgo/runtime/runtime.h --- a/libgo/runtime/runtime.h Tue Jan 29 17:30:28 2013 -0800 +++ b/libgo/runtime/runtime.h Wed Jan 30 13:53:47 2013 -0800 @@ -83,6 +83,8 @@ typedef struct Traceback Traceback; +typedef struct Location Location; + /* * Per-CPU declaration. */ @@ -155,6 +157,16 @@ uint64 nosyield; uint64 nsleep; }; + +// A location in the program, used for backtraces. +struct Location +{ + uintptr pc; + String filename; + String function; + intgo lineno; +}; + struct G { Defer* defer; @@ -226,7 +238,7 @@ MCache *mcache; G* lockedg; G* idleg; - uintptr createstack[32]; // Stack that created this thread. + Location createstack[32]; // Stack that created this thread. M* nextwaitm; // next M waiting for lock uintptr waitsema; // semaphore for parking on locks uint32 waitsemacount; @@ -391,7 +403,8 @@ void runtime_goroutinetrailer(G*); void runtime_traceback(); void runtime_tracebackothers(G*); -void runtime_printtrace(uintptr*, int32, bool); +void runtime_printtrace(Location*, int32, bool); +String runtime_gostring(const byte*); String runtime_gostringnocopy(const byte*); void* runtime_mstart(void*); G* runtime_malg(int32, byte**, size_t*); @@ -406,7 +419,7 @@ void runtime_exitsyscall(void) __asm__ (GOSYM_PREFIX "syscall.Exitsyscall"); void siginit(void); bool __go_sigsend(int32 sig); -int32 runtime_callers(int32, uintptr*, int32); +int32 runtime_callers(int32, Location*, int32); int64 runtime_nanotime(void); int64 runtime_cputicks(void); int64 runtime_tickspersecond(void); diff -r 6edf35ba05b2 libgo/runtime/string.goc --- a/libgo/runtime/string.goc Tue Jan 29 17:30:28 2013 -0800 +++ b/libgo/runtime/string.goc Wed Jan 30 13:53:47 2013 -0800 @@ -10,6 +10,8 @@ #define charntorune(pv, str, len) __go_get_rune(str, len, pv) +const String runtime_emptystring; + intgo runtime_findnull(const byte *s) { @@ -18,6 +20,38 @@ return __builtin_strlen((const char*) s); } +static String +gostringsize(intgo l, byte** pmem) +{ + String s; + byte *mem; + + if(l == 0) { + *pmem = nil; + return runtime_emptystring; + } + // leave room for NUL for C runtime (e.g., callers of getenv) + mem = runtime_mallocgc(l+1, FlagNoPointers, 1, 0); + s.str = mem; + s.len = l; + mem[l] = 0; + *pmem = mem; + return s; +} + +String +runtime_gostring(const byte *str) +{ + intgo l; + String s; + byte *mem; + + l = runtime_findnull(str); + s = gostringsize(l, &mem); + runtime_memmove(mem, str, l); + return s; +} + String runtime_gostringnocopy(const byte *str) {