Skip to content

Commit

Permalink
Merge pull request #6 for v0.3 Release
Browse files Browse the repository at this point in the history
  • Loading branch information
jeevatkm committed Mar 26, 2018
2 parents 53c8316 + f71c90d commit 2086b03
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 96 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ branches:

go:
- 1.8
- 1.8.x
- 1.9
- "1.10"
- tip

go_import_path: aahframework.org/aruntime.v0
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2016-2017 Jeevanandam M., https://myjeeva.com <[email protected]>
Copyright (c) 2016-2018 Jeevanandam M., https://myjeeva.com <[email protected]>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# aruntime - aah framework
[![Build Status](https://travis-ci.org/go-aah/aruntime.svg?branch=master)](https://travis-ci.org/go-aah/aruntime) [![codecov](https://codecov.io/gh/go-aah/aruntime/branch/master/graph/badge.svg)](https://codecov.io/gh/go-aah/aruntime/branch/master) [![Go Report Card](https://goreportcard.com/badge/aahframework.org/aruntime.v0)](https://goreportcard.com/report/aahframework.org/aruntime.v0) [![Version](https://img.shields.io/badge/version-0.2.2-blue.svg)](https://github.com/go-aah/aruntime/releases/latest) [![GoDoc](https://godoc.org/aahframework.org/aruntime.v0?status.svg)](https://godoc.org/aahframework.org/aruntime.v0) [![License](https://img.shields.io/github/license/go-aah/aruntime.svg)](LICENSE)
[![Build Status](https://travis-ci.org/go-aah/aruntime.svg?branch=master)](https://travis-ci.org/go-aah/aruntime) [![codecov](https://codecov.io/gh/go-aah/aruntime/branch/master/graph/badge.svg)](https://codecov.io/gh/go-aah/aruntime/branch/master) [![Go Report Card](https://goreportcard.com/badge/aahframework.org/aruntime.v0)](https://goreportcard.com/report/aahframework.org/aruntime.v0) [![Version](https://img.shields.io/badge/version-0.3-blue.svg)](https://github.com/go-aah/aruntime/releases/latest) [![GoDoc](https://godoc.org/aahframework.org/aruntime.v0?status.svg)](https://godoc.org/aahframework.org/aruntime.v0) [![License](https://img.shields.io/github/license/go-aah/aruntime.svg)](LICENSE)

***v0.2.2 [released](https://github.com/go-aah/aruntime/releases/latest) and tagged on Apr 23, 2017***
***v0.3 [released](https://github.com/go-aah/aruntime/releases/latest) and tagged on Mar 26, 2018***

Runtime library is built to get, manipulate GoRoutines stack trace, etc.

Expand Down
3 changes: 0 additions & 3 deletions aruntime.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,3 @@
// license that can be found in the LICENSE file.

package aruntime

// Version no. of aah framework aruntime library
const Version = "0.2.2"
186 changes: 102 additions & 84 deletions stacktrace.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"fmt"
"io"
"path/filepath"
"regexp"
"runtime"
"runtime/debug"
"strconv"
Expand All @@ -21,40 +20,39 @@ import (
)

const (
goroutinePrefix = "goroutine"
createdByPrefix = "created by"
createdByPrefix = "created by "
panicPrefix = "panic("
basePathPrefix = "#base-path#"
)

var goroutineRegEx = regexp.MustCompile(`goroutine\s?\d+\s\[.*\]\:`)

type (
// Stacktrace holds the parse information of `debug.Stack()`. It's easier to
// debug and understand.
Stacktrace struct {
Raw string
Recover interface{}
RoutineCnt int
IsParsed bool
GoRoutines []*GoRoutine

maxFileLen int
gopathSrc string
gorootSrc string
Raw string
Recover interface{}
IsParsed bool
StripSrcBase bool
GoRoutines []*GoRoutine
}

// GoRoutine holds information of single Go routine stack trace.
GoRoutine struct {
Header string
Packages []string
Functions []string
Header string
MaxFuncLen int
MaxPkgLen int
HasPanic bool
PanicIndex int
Packages []string
Functions []string
LineNo []string
}
)

// NewStacktrace method collects debug stack information and parsing them into
// easy understanding and returns the instance.
func NewStacktrace(r interface{}, appCfg *config.Config) *Stacktrace {
strace := &Stacktrace{
Raw: string(debug.Stack()),
Recover: r,
}

Expand All @@ -78,7 +76,7 @@ func NewStacktrace(r interface{}, appCfg *config.Config) *Stacktrace {
strace.Raw = string(debug.Stack())
}

strace.initPath()
strace.StripSrcBase = appCfg.BoolDefault("runtime.debug.strip_src_base", false)

return strace
}
Expand All @@ -89,69 +87,92 @@ func NewStacktrace(r interface{}, appCfg *config.Config) *Stacktrace {

// Parse method parses the go debug stacktrace into easy to understand.
func (st *Stacktrace) Parse() {
routines := goroutineRegEx.FindAllString(st.Raw, -1)
st.RoutineCnt = len(routines)
st.GoRoutines = make([]*GoRoutine, st.RoutineCnt)

ri := -1
gopathSrcLen := len(st.gopathSrc) + 1
gorootSrcLen := len(st.gorootSrc) + 1
var sections [][]string
var section []string

var lines []string
scanner := bufio.NewScanner(strings.NewReader(st.Raw))
for scanner.Scan() {
lines = append(lines, scanner.Text())
}

for linePos := 0; linePos < len(lines); linePos++ {
sline := strings.TrimSpace(lines[linePos])
if len(sline) == 0 {
line := scanner.Text()
if len(strings.TrimSpace(line)) == 0 {
sections = append(sections, section)
section = make([]string, 0)
continue
}
section = append(section, line)
}

if strings.HasPrefix(sline, goroutinePrefix) {
ri++
st.GoRoutines[ri] = &GoRoutine{
Header: sline,
Packages: []string{},
Functions: []string{},
}
// Only one go routine section found
if len(sections) == 0 && len(section) > 0 {
sections = append(sections, section)
}

continue
}
for _, s := range sections {
gr := &GoRoutine{Header: s[0]}
lnCnt := 1

if strings.HasPrefix(sline, "/") || strings.HasPrefix(sline[2:], "/") {
if strings.HasPrefix(sline, st.gopathSrc) {
sline = sline[gopathSrcLen:]
} else if strings.HasPrefix(sline, st.gorootSrc) {
sline = sline[gorootSrcLen:]
}
for _, ln := range s[1:] {
ln = strings.Replace(strings.TrimSpace(ln), "%2e", ".", -1)
if lnCnt%2 == 0 { // File Path
// Strip hexa chars (+0x15c, etc)
if idx := strings.IndexByte(ln, ' '); idx > 0 {
ln = ln[:idx]
}

sline = sline[:strings.LastIndex(sline, " ")]
if len(sline) > st.maxFileLen {
st.maxFileLen = len(sline)
}
// Separate the path and line no
if idx := strings.LastIndexByte(ln, ':'); idx > 0 {
gr.LineNo = append(gr.LineNo, ln[idx+1:])
ln = ln[:idx]
}

st.GoRoutines[ri].Packages = append(st.GoRoutines[ri].Packages, sline)
} else {
isCreatedBy := strings.HasPrefix(sline, createdByPrefix)
sline = filepath.Base(sline)

if !isCreatedBy {
rparen := strings.LastIndex(sline, "(")
if rparen != -1 {
comma := strings.IndexByte(sline[rparen:], ',')
if comma == -1 {
sline = sline[:rparen+1] + ")"
} else {
sline = sline[:rparen+1] + " ... )"
// Strip base path i.e. before `.../src/`
if st.StripSrcBase {
if idx := strings.Index(ln, "src"); idx > 0 {
ln = basePathPrefix + ln[idx+3:]
}
}

// Find max len
if l := len(ln); l > gr.MaxPkgLen {
gr.MaxPkgLen = l
}

gr.Packages = append(gr.Packages, ln)
} else { // Function Info
// Strip parameters hexa values
if !strings.HasPrefix(ln, createdByPrefix) {
if rparen := strings.LastIndex(ln, "("); rparen != -1 {
if comma := strings.IndexByte(ln[rparen:], ','); comma == -1 {
ln = ln[:rparen+1] + ")"
} else {
ln = ln[:rparen+1] + "...)"
}
}
}

ln = filepath.Base(ln)

// Find func max len
if l := len(ln); l > gr.MaxFuncLen {
gr.MaxFuncLen = l
}

// Check this goroutine has `panic(...)`
if yes := strings.HasPrefix(ln, panicPrefix); yes || !gr.HasPanic {
gr.HasPanic = yes

// Capture panic index
if gr.HasPanic {
gr.PanicIndex = len(gr.Functions)
}
}

gr.Functions = append(gr.Functions, ln)
}

st.GoRoutines[ri].Functions = append(st.GoRoutines[ri].Functions, strings.Replace(sline, "%2e", ".", -1))
lnCnt++
}

st.GoRoutines = append(st.GoRoutines, gr)
}

st.IsParsed = true
Expand All @@ -167,25 +188,22 @@ func (st *Stacktrace) Print(w io.Writer) {
st.Parse()
}

printFmt := "\t%-" + strconv.Itoa(st.maxFileLen+1) + "s-> %v\n"
_, _ = w.Write([]byte(fmt.Sprintf("STACKTRACE:\n%v\n", st.Recover)))
fmt.Fprintf(w, "STACKTRACE:\n%v\n", st.Recover)
for _, gr := range st.GoRoutines {
fmt.Fprint(w, "\n"+gr.Header+"\n")
hdrStr := fmt.Sprintf(" %-"+strconv.Itoa(gr.MaxPkgLen+1)+"s %-"+strconv.Itoa(gr.MaxFuncLen)+"s %s\n",
"FILE", "FUNCTION", "LINE NO")
fmt.Fprint(w, hdrStr)
fmt.Fprint(w, " ")
for idx := 1; idx < len(hdrStr)-4; idx++ {
fmt.Fprint(w, "-")
}
fmt.Fprint(w, "\n")

for _, rv := range st.GoRoutines {
_, _ = w.Write([]byte("\n" + rv.Header + "\n"))
for idx, f := range rv.Packages {
_, _ = w.Write([]byte(fmt.Sprintf(printFmt, f, rv.Functions[idx])))
printFmt := " %-" + strconv.Itoa(gr.MaxPkgLen+1) + "s %-" + strconv.Itoa(gr.MaxFuncLen) + "s #%s\n"
for idx, f := range gr.Packages[gr.PanicIndex:] {
idx += gr.PanicIndex
fmt.Fprintf(w, printFmt, f, gr.Functions[idx], gr.LineNo[idx])
}
}
}

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// Unexported methods
//___________________________________

func (st *Stacktrace) initPath() {
gopath, _ := ess.GoPath()
goroot := runtime.GOROOT()

st.gopathSrc = filepath.Join(gopath, "src")
st.gorootSrc = filepath.Join(goroot, "src")
}
57 changes: 52 additions & 5 deletions stacktrace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,16 @@ import (

func TestStacktrace(t *testing.T) {
strace := Stacktrace{
Raw: getStacktrace(),
Recover: "this is test case",
Raw: getStacktrace(),
Recover: "this is test case",
StripSrcBase: true,
}

strace.initPath()

buf := &bytes.Buffer{}
strace.Print(buf)
t.Log(buf.String())

assert.Equal(t, 4, strace.RoutineCnt)
assert.Equal(t, 5, len(strace.GoRoutines))
assert.Equal(t, "goroutine 5 [running]:", strace.GoRoutines[0].Header)
assert.Equal(t, "goroutine 1 [running]:", strace.GoRoutines[1].Header)
assert.Equal(t, "goroutine 1 [IO wait]:", strace.GoRoutines[2].Header)
Expand All @@ -43,6 +42,20 @@ func TestStacktrace(t *testing.T) {
assert.Equal(t, 1, len(strace.GoRoutines[3].Functions))
}

func TestSingleStacktrace(t *testing.T) {
strace := Stacktrace{
Raw: getSingleStacktrace(),
Recover: "this is single test case",
}

buf := &bytes.Buffer{}
strace.Print(buf)
t.Log(buf.String())

assert.Equal(t, 1, len(strace.GoRoutines))
assert.Equal(t, "goroutine 18 [running]:", strace.GoRoutines[0].Header)
}

func TestNewStacktrace(t *testing.T) {
cfg, _ := config.ParseString(``)
strace := NewStacktrace("testing", cfg)
Expand Down Expand Up @@ -151,5 +164,39 @@ main.main()
goroutine 17 [syscall, locked to thread]:
runtime.goexit()
/usr/local/go/src/runtime/asm_amd64.s:2086 +0x1
goroutine 18 [running]:
testing.tRunner.func1(0xc0420ea0f0)
c:/Go/src/testing/testing.go:742 +0x2a4
panic(0x5dd180, 0x6538f0)
c:/Go/src/runtime/panic.go:505 +0x237
aahframework.org/aruntime%2ev0.(*Stacktrace).Parse(0xc04203df28)
C:/Users/jeeva/go/src/aahframework.org/aruntime.v0/stacktrace.go:146 +0xb37
aahframework.org/aruntime%2ev0.(*Stacktrace).Print(0xc04204bf28, 0x654960, 0xc0420cefc0)
C:/Users/jeeva/go/src/aahframework.org/aruntime.v0/stacktrace.go:156 +0x446
aahframework.org/aruntime%2ev0.TestMeStacktrace(0xc0420ea0f0)
C:/Users/jeeva/go/src/aahframework.org/aruntime.v0/stacktrace_test.go:25 +0xc9
testing.tRunner(0xc0420ea0f0, 0x63aa40)
c:/Go/src/testing/testing.go:777 +0xd7
created by testing.(*T).Run
c:/Go/src/testing/testing.go:824 +0x2e7
`
}

func getSingleStacktrace() string {
return `goroutine 18 [running]:
testing.tRunner.func1(0xc0420ea0f0)
c:/Go/src/testing/testing.go:742 +0x2a4
panic(0x5dd180, 0x6538f0)
c:/Go/src/runtime/panic.go:505 +0x237
aahframework.org/aruntime%2ev0.(*Stacktrace).Parse(0xc04203df28)
C:/Users/jeeva/go/src/aahframework.org/aruntime.v0/stacktrace.go:146 +0xb37
aahframework.org/aruntime%2ev0.(*Stacktrace).Print(0xc04204bf28, 0x654960, 0xc0420cefc0)
C:/Users/jeeva/go/src/aahframework.org/aruntime.v0/stacktrace.go:156 +0x446
aahframework.org/aruntime%2ev0.TestMeStacktrace(0xc0420ea0f0)
C:/Users/jeeva/go/src/aahframework.org/aruntime.v0/stacktrace_test.go:25 +0xc9
testing.tRunner(0xc0420ea0f0, 0x63aa40)
c:/Go/src/testing/testing.go:777 +0xd7
created by testing.(*T).Run
c:/Go/src/testing/testing.go:824 +0x2e7`
}
8 changes: 8 additions & 0 deletions version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright (c) Jeevanandam M (https://github.com/jeevatkm)
// go-aah/aruntime source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.

package aruntime

// Version no. of aah framework aruntime library
const Version = "0.3"

0 comments on commit 2086b03

Please sign in to comment.