-
Notifications
You must be signed in to change notification settings - Fork 607
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
Flame Graph support on Web UI #188
Conversation
Codecov Report
@@ Coverage Diff @@
## master #188 +/- ##
==========================================
+ Coverage 65.29% 65.84% +0.55%
==========================================
Files 34 35 +1
Lines 7212 7346 +134
==========================================
+ Hits 4709 4837 +128
- Misses 2109 2113 +4
- Partials 394 396 +2
Continue to review full report at Codecov.
|
Given that pprof is vendored into Golang sources and given that it's used internally at Google, I am skeptical about us being able to accept the source which <script>'s a number of locations on the Internet. @rsc @rauls5382 @rakyll |
@aalexand If that's a concern, it should be possible to source those files from pprof's http server. |
Thank you. This looks pretty cool. I agree we should freeze the version of all scripts/plugins and serve them from the pprof http server. That way pprof won't be broken if there are incompatible changes in the future. The Go distribution would have to vendor any dependent packages as well, similar to what they already do to pick up the ianlancetaylor/demangle package. If they decide against it (which I doubt), we could find a way to make it easy to trim that functionality via plugins. |
Thanks @rauls5382 Versioning shouldn't theoretically be a problem, since all external resource references are versioned CDN urls. Security-wise, the external resource could be compromised, so I've added integrity check to all resources to be on the safe side. If vendor the dependent packages is a better alternative, there are a few different ways of getting this done, and I'm not sure what would be best option, from a pprof perspective. Let me know and I can make the changes. |
@spiermar Can you compile all the external scripts in a single one and embed the final artifact in a Go file? Having said that, I am not sure how licensing will work. @aalexand, @rauls5382, do we need a separate in the repo with a licensing file to be able to vendor the external dependencies? |
@rakyll already have the scripts to create combined and minified |
@rakyll Yes, I would probably expect something like third_party/d3 and third_party/flame-something-something for the dependencies along with the LICENSE files in there. It would be good to pre-check with the Go team maybe. What concerns me the most is having pprof which is a local tool fetch any resources from elsewhere on the web. Security aside, pprof and "go tool pprof" should be functional without requiring internet access. |
I'll make the changes later today |
https://github.com/google/pprof/blob/master/third_party/svg/svgpan.go is along the lines of what we want. |
JavaScript resources are inline now and I've also removed the bootstrap dependency. License files are also checked in. |
Thanks. Some comments:
|
|
@aalexand Finally got a bit of time to get back to this. The |
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.
Thanks!
Is the current code rebased to the latest master?
third_party/d3tip/d3_tip.go
Outdated
package d3tip | ||
|
||
// D3TIP returns the d3-tip.js file | ||
const D3TIP = ` |
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.
D3TIP
is not in style compliant with Go style. Should be thisStyle (for package private) or ThisStyle (for public). For this variable, how about something like JSSource or Source?
This comment applies to d3.go and d3_flame_graph.go as well.
third_party/d3tip/d3_tip.go
Outdated
|
||
// D3TIP returns the d3-tip.js file | ||
const D3TIP = ` | ||
{{define "d3tipscript"}} |
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.
For consistency with svgpan.go can we move the template definition part out of this file and only have the JS source constant defined here?
This comment applies to d3.go and d3_flame_graph.go as well.
internal/driver/flamegraph.go
Outdated
|
||
// percentage computes the percentage of total of a value, and encodes | ||
// it as a string. At least two digits of precision are printed. | ||
func percentage(value, total int64) string { |
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 I think we should do #265 and use measurement.Percentage
in the code below so that this function can be dropped and so that the percentage formatting is enforced to be consistent.
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.
Should I wait until it gets merged?
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 merged that, you can rebase.
internal/driver/flamegraph.go
Outdated
// Calculate root value | ||
rootValue := int64(0) | ||
for _, n := range nodes[0:nroots] { | ||
rootValue = rootValue + n.Cum |
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: this can be done near nroots++
above, no need to have a separate loop.
@aalexand Yes, it was rebased. |
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 also wonder if the size of the dependencies can be further reduced. I saw d3 is composed of microlibraries. Do we need all of d3 here or we can limit the dependency to a couple of those?
I could create a custom D3 build. Let me check how much that would reduce the bundle size. |
@spiermar Great, thanks a lot, that's much smaller! Can you add a README.md in the d3 directory which documents how the steps you did can be repeated to upgrade from upstream when we need to? |
internal/driver/flamegraph.go
Outdated
// flamegraph generates a web page containing a flamegraph. | ||
func (ui *webInterface) flamegraph(w http.ResponseWriter, req *http.Request) { | ||
// Force the call tree so that the graph is a tree. | ||
rpt, errList := ui.makeReport(w, req, []string{"svg"}, "call_tree", "true") |
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.
Please update this to
// Force the call tree so that the graph is a tree. Also do not trim the tree
// so that the flame graph contains all functions.
rpt, errList := ui.makeReport(w, req, []string{"svg"}, "call_tree", "true", "trim", "false")
The GetDOT
call trims the tree by default, and for the flame graph I think it's reasonable to override that.
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.
Up to you. I prefer to have the flame graph contain everything, but that's not the default behavior for the directed graph, so I wasn't really sure what would the users prefer.
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 graph does it by default because displaying the full graph for large programs becomes messy. Flame graph appears to be compact and readable enough on some larger profiles I tried so I think trimming the tree would be more confusing that useful here.
third_party/d3tip/d3_tip.go
Outdated
|
||
// Source returns the d3-tip.js file | ||
const Source = ` | ||
{{define "d3tipscript"}} |
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.
How difficult it is to push out the template directive out of these files? This is a nit, I am just trying to make all third party files consistent here - like svgpan they should only contain the JS source as is, without the additional directives so that they are plain simple and once updated to a newer version can be easily upgraded without accidentally losing any content.
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.
Should be simple, just move the directives. It's being called only in one place. Will check it later tonight.
@spiermar Overall I think it looks good now. One thing: would you be open (and would it be difficult) to switch the flame graph to grow down rather than up? We found recently that with deep flame graphs it looks more natural in the UI when they grow down and you have to scroll to get the finer level of details (deeper elements) rather than having to scroll just to see the root. See the attached example. |
@aalexand it's in the roadmap for the d3 flame graph plugin (spiermar/d3-flame-graph#73), but I don't have a target date for the release yet. |
My original flamegraph.pl did this layout too (icicle), but here's how I interpret each flame graph:
Last week I had large plateau of UTF-8 processing (51%), then swept bottom up to look for UTF-8 or string-related functions, skipping the rest (a lot of framework frames). This meant I could find the relevant UTF-8/string frames more quickly, after knowing the on-CPU context first. Another recent one was gzip. Another was kernel network interrupt processing -- reading that bottom up would be a waste of time, since what's ultimately burning CPU is unrelated to the user-level program that's running. Reading bottom-up (root first) still gets the job done. My point is that knowing some on-CPU context first can make that quicker, as you have a clue as to what you're looking for. I've used the icicle layout when reversing the stack order as well, so that the top frame remains what is on-CPU, and beneath it is ancestry. That's one of the big reasons we wanted to rewrite this in d3, so that flipping the layout direction and merge direction could be button presses in an interactive d3 graph. Doing it with my Perl program meant rerunning the Perl program. |
@spiermar Note a couple of other open comments I made. |
@aalexand regarding the D3 build, sure. I used rollup to build the distribution and the configuration is on my Github page (https://github.com/spiermar/d3-pprof). With that it's just a single command to generate the build. If it's Ok to link to the repo, I'll add the instructions to a README.md file on pprof and also update the repo with the instructions. |
@spiermar I'd prefer to include self-contained instructions on how to do the build inside of pprof than point to another repo. This is to keep the deps minimal, even if just for the build process. |
# Conflicts: # CONTRIBUTORS
Implements #166
Details:
http
output./flamegraph
, and there's a link in/
,samples
andcpu
.A demo can be found in pprof_flame.html
Happy to make any changes deemed necessary.