-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improvements to ListGoroutines #1440
Conversation
aarzilli
commented
Dec 6, 2018
This is a series of improvements to GoroutinesInfo/ListGoroutines connected to microsoft/vscode-go#2133:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the improvement to FindGoroutine
and the cache fix, however I think we can create a smarter cache in the future.
What I'm not convinced of is the semantics of using start to signal a sorting of goroutines by trying to return only running first. I think maybe it's better to be explicit and add a parameter runningOnly bool
to control this behavior, that way the logic becomes much simpler and it's more declarative.
Sorting and prioritizing running goroutines can be difficult because of how we're essentially trying to paginate the list of goroutines.
service/debugger/debugger.go
Outdated
@@ -916,7 +917,44 @@ func (d *Debugger) Goroutines(start, count int) ([]*api.Goroutine, int, error) { | |||
if err != nil { | |||
return nil, 0, err | |||
} | |||
if start > 0 || nextg >= 0 { | |||
// Ensure that the first batch of goroutines always contains all running |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I think we should reword this comment, as we don't make any explicit guarentee that we are returning all running goroutines in the first batch, as that's limited by the count the user specifies. It would suffice, I think, to simply say that we sort and prioritize running goroutines first.
service/debugger/debugger.go
Outdated
@@ -916,7 +917,44 @@ func (d *Debugger) Goroutines(start, count int) ([]*api.Goroutine, int, error) { | |||
if err != nil { | |||
return nil, 0, err | |||
} | |||
if start > 0 || nextg >= 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused, here we're checking if start > 0
and then trying to return all running goroutines, however in the commit and documentation it's specified that we only want to prioritize and return only running goroutines when start == 0
. This leads us to having a bunch of conditionals below, I think we can clean this up by having this check first if start ==0
, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are two paths here: start == 0 && nextg >= 0
where we add running goroutines and start > 0
where we remove them (because we returned them before when we had start == 0
). It is a bit confusing as it is, I rewrote it by factoring out the common code and moving the conditions outside, hopefully it's clearer.
service/debugger/debugger.go
Outdated
@@ -906,6 +907,25 @@ func (d *Debugger) SetVariableInScope(scope api.EvalScope, symbol, value string) | |||
return s.SetVariable(symbol, value) | |||
} | |||
|
|||
func (d *Debugger) getRunningGoroutines(count int) map[int]*proc.G { | |||
threads := d.target.ThreadList() | |||
sort.Slice(threads, func(i, j int) bool { return threads[i].ThreadID() < threads[j].ThreadID() }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does sorting buy us here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The output of ThreadList is not guaranteed to always be in the same order (because the native backend is converting from a map).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but we just iterate them and them stuff them into another map and return it, I don't see why we should sort them first, order doesn't really matter here unless I'm missing something obvious.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this to make the call idempotent when called with a same count
value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
service/debugger/debugger.go
Outdated
@@ -916,7 +936,31 @@ func (d *Debugger) Goroutines(start, count int) ([]*api.Goroutine, int, error) { | |||
if err != nil { | |||
return nil, 0, err | |||
} | |||
|
|||
// Prioritize running goroutines in the first batch we return (adds up to | |||
// 'count' running goroutines to the first batch and insures that they |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/insures/ensures/
service/debugger/debugger.go
Outdated
// Prioritize running goroutines in the first batch we return (adds up to | ||
// 'count' running goroutines to the first batch and insures that they | ||
// aren't returned a second time in the batches after the first one). | ||
if start > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still don't like the semantics here, and I think it would be better to have a parameter like onlyRunning
if a caller is only concerned with running Goroutines.
Consider a caller specifies start == 0, count == 5
, then (and I don't know why they would, but just consider) they call it again with start == 1, count ==5
. All running goroutines would be removed from the list, which just kind of seems like odd behavior. Lastly, I don't think we should ever return more than count
items, that's just very unexpected behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't make sense to call proc.GoroutinesInfo
with arbitrary values of start
, it will skip an arbitrary number n (with 0 <= n < start
).
You've convinced me that this feature (returning running goroutines first) isn't worth it. I don't think we can implement it without the weird behavior and since running goroutines are returned as part of GetState it isn't that useful anyway.
I've removed this code and added an exetended explanation of Start and Count.
The allg cache was corrupted when the count parameter was actually reached. Fix the bug and add a test for this.
FindGoroutine can be slow when there are many goroutines running. This can not be fixed in the general case, however: 1. Instead of getting the entire list of goroutines at once just get a few at a time and return as soon as we find the one we want. 2. Since FindGoroutine is mostly called by ConvertEvalScope and users are more likely to request informations about a goroutine running on a thread, look within the threads first.
Describe how the Start and Count parameters of ListGoroutines are used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM