Skip to content

Commit

Permalink
fix some bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
lqs committed Oct 2, 2023
1 parent 2923296 commit 2f7dc9f
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 68 deletions.
18 changes: 18 additions & 0 deletions common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,21 @@ type Process struct {
Agent bool
StartTime time.Time
}

type CallStack struct {
Id int
Name string
State string
Wait string
Frames []*Frame
ParentGoroutineId int
ParentFrame *Frame
Children []*CallStack
}

type Frame struct {
Package string
Func string
Params string
File string
}
40 changes: 12 additions & 28 deletions gops/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,16 @@ import (
"slices"
"strconv"
"strings"
)

type Goroutine struct {
Id int
Name string
State string
Wait string
Frames []Frame
ParentGoroutineId int
ParentFrame Frame
Children []*Goroutine
}

type Frame struct {
Package string
Func string
Params string
File string
}
"github.com/lqs/pscope/common"
)

var headerRegex = regexp.MustCompile(`^goroutine (\d+) \[(.+?)(?:, (.+))?]:`)
var frameRegex = regexp.MustCompile(`^([a-zA-Z0-9._/\-]+\.)((?:\(\*?[^()]*\)\.)?\w+(?:\[\.\.\.])?)(?:\(([^()]*)\))?$`)
var createdByRegex = regexp.MustCompile(`^created by .+ in goroutine (\d+)$`)

func ParseGoStack(stack []byte) []*Goroutine {
var goroutines []*Goroutine
func ParseGoStack(stack []byte) []*common.CallStack {
var goroutines []*common.CallStack
reader := bufio.NewReader(bytes.NewReader(stack))
for {
line, err := reader.ReadString('\n')
Expand All @@ -46,13 +30,13 @@ func ParseGoStack(stack []byte) []*Goroutine {
}

goroutineId, _ := strconv.Atoi(matches[1])
goroutine := &Goroutine{
goroutine := &common.CallStack{
Id: goroutineId,
State: matches[2],
Wait: matches[3],
}
for {
frame := Frame{}
frame := &common.Frame{}
line, _ := reader.ReadString('\n')
line = strings.TrimSpace(line)
if line == "" {
Expand Down Expand Up @@ -88,8 +72,8 @@ func ParseGoStack(stack []byte) []*Goroutine {
return roots
}

func makeTree(goroutines []*Goroutine) []*Goroutine {
goroutineMap := make(map[int]*Goroutine)
func makeTree(goroutines []*common.CallStack) []*common.CallStack {
goroutineMap := make(map[int]*common.CallStack)
for i := range goroutines {
goroutineMap[goroutines[i].Id] = goroutines[i]
}
Expand All @@ -99,7 +83,7 @@ func makeTree(goroutines []*Goroutine) []*Goroutine {
parent, ok := goroutineMap[goroutine.ParentGoroutineId]
if !ok {
// make a dummy parent goroutine
parent = &Goroutine{
parent = &common.CallStack{
Id: goroutine.ParentGoroutineId,
State: "terminated",
ParentGoroutineId: 1,
Expand All @@ -111,7 +95,7 @@ func makeTree(goroutines []*Goroutine) []*Goroutine {
}
}

var roots []*Goroutine
var roots []*common.CallStack
for i := range goroutines {
goroutine := goroutines[i]
if goroutine.ParentGoroutineId == 0 {
Expand All @@ -121,8 +105,8 @@ func makeTree(goroutines []*Goroutine) []*Goroutine {
return roots
}

func sortGoroutines(goroutines []*Goroutine) {
slices.SortFunc(goroutines, func(a, b *Goroutine) int {
func sortGoroutines(goroutines []*common.CallStack) {
slices.SortFunc(goroutines, func(a, b *common.CallStack) int {
return b.Id - a.Id
})
for _, goroutine := range goroutines {
Expand Down
21 changes: 8 additions & 13 deletions jvmhsperf/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"strconv"
"strings"

"github.com/lqs/pscope/gops"
"github.com/lqs/pscope/common"
)

// "http-nio-8080-exec-1" #20 daemon prio=5 os_prio=0 cpu=126.11ms elapsed=144.34s tid=0x00007f9048f3c290 nid=0xc038 waiting on condition [0x00007f8faf7f9000]
Expand All @@ -19,16 +19,8 @@ var threadStateRegex = regexp.MustCompile(`^java\.lang\.Thread\.State: (.+)$`)
// at some.package.Class$SubClass.method(RequestMappingHandlerAdapter.java:878)
var frameRegex = regexp.MustCompile(`^at (?:([a-zA-Z0-9.]*?)\.)?([a-zA-Z0-9$]+)\.([a-zA-Z0-9]+)\((.+):(\d+)\)$`)

type Thread struct {
Id int
Name string
State string
Elapsed string
Frames []gops.Frame
}

func ParseJavaThreadDump(threadDump []byte) []*Thread {
var threads []*Thread
func ParseJavaThreadDump(threadDump []byte) []*common.CallStack {
var threads []*common.CallStack
reader := bufio.NewReader(bytes.NewReader(threadDump))
for {
line, err := reader.ReadString('\n')
Expand All @@ -39,7 +31,7 @@ func ParseJavaThreadDump(threadDump []byte) []*Thread {

if matches := headerRegex.FindStringSubmatch(line); matches != nil {
threadId, _ := strconv.Atoi(matches[2])
thread := &Thread{
thread := &common.CallStack{
Id: threadId,
Name: matches[1],
State: matches[5],
Expand All @@ -49,13 +41,16 @@ func ParseJavaThreadDump(threadDump []byte) []*Thread {
thread := threads[len(threads)-1]
thread.State = matches[1]
} else if matches := frameRegex.FindStringSubmatch(line); matches != nil {
frame := gops.Frame{
frame := &common.Frame{
Package: matches[1],
Func: matches[2] + "." + matches[3],
File: matches[4],
}
thread := threads[len(threads)-1]
thread.Frames = append(thread.Frames, frame)
} else if strings.Contains(line, "- Coroutine dump -") {
// TODO: add coroutine support
break
}
}
return threads
Expand Down
38 changes: 20 additions & 18 deletions ui/goroutinelist.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (

type GoroutineStackView struct {
*tview.Flex
selectGoroutine func(goroutine *gops.Goroutine)
selectGoroutine func(goroutine *common.CallStack)
cancel context.CancelFunc
}

Expand All @@ -27,11 +27,11 @@ type GoroutineStackViewParams struct {
type GoroutineListView struct {
*tview.Table
process common.Process
indexToGoroutine map[int]*gops.Goroutine
indexToGoroutine []*common.CallStack
currentRow int
}

func (g *GoroutineListView) add(goroutine *gops.Goroutine, level int, isLastChild bool) {
func (g *GoroutineListView) add(goroutine *common.CallStack, level int, isLastChild bool) {
prefix := ""
if level > 0 {
for i := 0; i < level-1; i++ {
Expand All @@ -55,23 +55,24 @@ func (g *GoroutineListView) add(goroutine *gops.Goroutine, level int, isLastChil
g.SetCell(g.currentRow, 1, tview.NewTableCell(goroutine.State))
}

g.indexToGoroutine[g.currentRow] = goroutine
g.indexToGoroutine = append(g.indexToGoroutine, goroutine)
g.currentRow++

for i, child := range goroutine.Children {
g.add(child, level+1, i+1 == len(goroutine.Children))
}
}

func (g *GoroutineListView) Apply(goroutines []*gops.Goroutine) {
//g.SetTitle(" Goroutines (" + strconv.Itoa(len(goroutines)) + ") ")
//for g.GetRowCount() > len(goroutines)+1 {
// g.RemoveRow(g.GetRowCount() - 1)
//}
g.indexToGoroutine = make(map[int]*gops.Goroutine)
func (g *GoroutineListView) Apply(callStacks []*common.CallStack) {
g.indexToGoroutine = nil
g.currentRow = 1
for i, goroutine := range goroutines {
g.add(goroutine, 0, i+1 == len(goroutines))
for i, goroutine := range callStacks {
g.add(goroutine, 0, i+1 == len(callStacks))
}
if g.process.Type == common.ProcessTypeGo {
g.SetTitle(" Goroutines (" + strconv.Itoa(len(g.indexToGoroutine)) + ") ")
} else {
g.SetTitle(" Threads (" + strconv.Itoa(len(g.indexToGoroutine)-1) + ") ")
}
for g.GetRowCount() > g.currentRow+1 {
g.RemoveRow(g.GetRowCount() - 1)
Expand All @@ -90,7 +91,6 @@ func (v *GoroutineStackView) newGoroutineList(process common.Process) *Goroutine
}

table.SetBorder(true)
table.SetTitle(" Goroutines ")
table.SetBorderPadding(0, 0, 1, 1)
table.SetFixed(1, 3)
switch process.Type {
Expand All @@ -108,14 +108,16 @@ func (v *GoroutineStackView) newGoroutineList(process common.Process) *Goroutine
row = 1
table.Select(row, column)
}
v.selectGoroutine(g.indexToGoroutine[row])
if row < len(g.indexToGoroutine) {
v.selectGoroutine(g.indexToGoroutine[row-1])
}
})
table.SetSelectable(true, false)

return g
}

func newStackList(frames []gops.Frame) tview.Primitive {
func newStackList(frames []*common.Frame) tview.Primitive {
table := tview.NewTable()
table.SetBorder(true).SetTitle(" Stack Frames ")
table.SetBorderPadding(0, 0, 1, 1)
Expand Down Expand Up @@ -174,15 +176,15 @@ func NewGoroutineStackView(params GoroutineStackViewParams) Widget {
})

NewReloader(ctx, func() {
var goroutines []*gops.Goroutine
var goroutines []*common.CallStack
switch params.Process.Type {
case common.ProcessTypeGo:
result, _ := gops.Cmd(ctx, params.Process.PID, signal.StackTrace)
goroutines = gops.ParseGoStack(result)
case common.ProcessTypeJava:
result, _ := jvmhsperf.Execute(params.Process.PID, "threaddump")
for _, thread := range jvmhsperf.ParseJavaThreadDump(result) {
goroutines = append(goroutines, &gops.Goroutine{
goroutines = append(goroutines, &common.CallStack{
Id: thread.Id,
Name: thread.Name,
State: thread.State,
Expand All @@ -192,7 +194,7 @@ func NewGoroutineStackView(params GoroutineStackViewParams) Widget {
}

params.Application.QueueUpdateDraw(func() {
v.selectGoroutine = func(goroutine *gops.Goroutine) {
v.selectGoroutine = func(goroutine *common.CallStack) {
if flex.GetItemCount() > 1 {
flex.RemoveItem(flex.GetItem(1))
}
Expand Down
16 changes: 11 additions & 5 deletions ui/processdetail.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,17 @@ func NewProcessDetailView(params ProcessDetailViewParams) ProcessDetailView {

form.AddTextView("PID", strconv.Itoa(params.Process.PID), 0, 1, true, false)
form.AddTextView("Name", name, 0, 1, true, false)
form.AddTextView("CPU Percent", fmt.Sprintf("%.1f%%", cpuPercent*100), 0, 1, true, false)

form.AddButton("Stack", params.OnShowStack)
form.AddButton("CPU Profile", params.OnCPUProfile)
form.AddButton("Heap Profile", params.OnHeapProfile)
form.AddTextView("%CPU (average)", fmt.Sprintf("%.1f%%", cpuPercent*100), 0, 1, true, false)

if params.Process.Agent {
form.AddButton("Stack Dump", params.OnShowStack)
if params.Process.Type == common.ProcessTypeGo {
form.AddButton("CPU Profile", params.OnCPUProfile)
form.AddButton("Heap Profile", params.OnHeapProfile)
}
} else {
form.AddButton("gops agent not started. Press Esc to go back", nil)
}
form.SetButtonStyle(buttonStyle)
form.SetButtonActivatedStyle(buttonActivatedStyle)
form.SetCancelFunc(func() {
Expand Down
8 changes: 4 additions & 4 deletions ui/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ func (v ProfileView) start(ctx context.Context) {
progressDialog := tview.NewModal()
progressDialog.SetBackgroundColor(tcell.ColorSilver)
progressDialog.SetTextColor(tcell.ColorBlack)
//progressDialog.AddButtons([]string{"Cancel"})
//progressDialog.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
// params.OnClose()
//})
progressDialog.AddButtons([]string{"Don't shutdown your computer"})
progressDialog.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
//params.OnClose()
})
v.Pages.AddPage("progressDialog", progressDialog, true, true)

done := make(chan struct{})
Expand Down

0 comments on commit 2f7dc9f

Please sign in to comment.