Skip to content
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

Profiling CPU and Memory of Go programs #1685

Open
jmaister opened this issue Aug 11, 2021 · 12 comments
Open

Profiling CPU and Memory of Go programs #1685

jmaister opened this issue Aug 11, 2021 · 12 comments

Comments

@jmaister
Copy link

Is your feature request related to a problem? Please describe.
I'm always frustrated when I want to profile a Go program and I have to use several manual steps.

Describe the solution you'd like
A way of profiling the execution of Go applications in terms of CPU and/or Memory usage.

Describe alternatives you've considered
The way of doing it requires several manual steps with the command line. It requires adding code to the "func main()", then commands to parse the results to present it as text or PDF.

Additional context
Go land docs about profiling: https://blog.golang.org/pprof

Simplest way of using pprof: https://golangdocs.com/profiling-in-golang

@hyangah
Copy link
Contributor

hyangah commented Aug 20, 2021

For test profiling, I think we want it too. @firelizzard18 's Go test adapter extension already had a similar integration for VS code.

But I am not sure about this comment:

... It requires adding code to the "func main()", ....

Do you mean you want this extension to inject code to your program temporarily?

@firelizzard18
Copy link
Contributor

@jmaister, can you describe the user experience you want? Profiling tests and benchmarks is straightforward - I plan to migrate the test and benchmark profiling capability from my extension (which @hyangah mentioned) into this extension.

But it sounds like you want something different? If you have some existing way to generate profiles (cpu.prof/mem.prof), I can add a command to display those. But AFAIK there is no way to capture a pprof profile from an arbitrary program. Do you want to the extension to recompile your code with a generated main function? How would the extension know what to do in the main function? If your program embeds the pprof http handler and exposes an HTTP server, that can be used to capture traces, but again, that requires modifications to the program.

I'm always frustrated when I want to profile a Go program and I have to use several manual steps.

Can you describe the steps you're taking? If you give specifics, we can discuss how the extension might make it easier.

@jmaister
Copy link
Author

jmaister commented Aug 20, 2021

Hi @firelizzard18
The experience that I would like to get is to select the main.go (or the main() function), right-click and select the option to "Run with profiler...". A screen for the options appears to select CPU, Memory or both and the folder/file to store the resulting files.
Then, after the execution, a new tab for each type of profile opened to see the results. Depending on the type the CPU would show the list of methods, time, %, etc... and the Memory objects size, %, etc...

However, I've investigated a bit more and having to modify main.go to add the pprof package and the code could be quite difficult, also if the user already added it, could it be verified? Then I've found that the "go test" already has the profiler integrated.

With all this, the experience would be easier to implement by adding an option to run the tests with the profiler activated. Then open the tabs with the results as described above.

Additional note: go test already has the profiler activated. https://go.dev/blog/pprof "If the code used the Go testing package’s benchmarking support, we could use gotest’s standard -cpuprofile and -memprofile flags."

go test -cpuprofile cpu.prof -memprofile mem.prof -bench .

@firelizzard18
Copy link
Contributor

With all this, the experience would be easier to implement by adding an option to run the tests with the profiler activated. Then open the tabs with the results as described above.

This is definitely easier, and is essentially what my extension does. I do plan on adding that capability to this extension.

How do you want to see the results? Do you want access to the files? My extension stores the files in a temporary directory and displays the profiles as a graph, which you can drag and zoom. go tool pprof can generate a variety of outputs. If you want to see more than just the graph, I can find a way to do that, but I'll stick to just the graph if that's sufficient.

I'm still open to the idea of profiling executables (main), but we need to figure out how to do that first.

@jmaister
Copy link
Author

The graph will be good. I also found that you can generate the graph based on 2 pprof files to see the improvements/degradation.

$go tool pprof
...
-diff_base source     Source of base profile for comparison

That could be a nice feature too: "Compare with a previous run ...".

About running the main, could the main() function be run from a made-up test to get the results?

package main_test

import ...

func TestUserLogin(t *testing.T) {
    main.main()
} 

@firelizzard18
Copy link
Contributor

@jmaister If there are specific features you want implemented, I suggest you add a task list and/or open specific issues, e.g. "I want <this feature>". I am open to working on these features, but there are a lot of different things I want to do, and I'm going to forget details unless they are listed clearly somewhere easy to find. And a task list and/or separate issues would make it easier for the Go team to work on this too.

About running the main, could the main() function be run from a made-up test to get the results?

That didn't occur to me. It would have to be something like this:

package main

func TestMain(t *testing.T) { main() }

The actual test name isn't important, but main.main() won't work: cannot refer to unexported name main.main.

This is clearly possible. I need to think more - I'm not sure if it can be done without adding files to the user's workspace, and I'm not sure I find that acceptable. I could delete them afterwards, similar to how delve handles __debug_bin and friends, but that's still not great.

I also found that you can generate the graph based on 2 pprof files to see the improvements/degradation.

That's pretty cool. I'd like to see a UI with a list of profiles, including past runs, but it may be a while before that happens. So I suggest adding a separate issue for this, so it doesn't get lost or forgotten.

@jmaister
Copy link
Author

Features:

  • I want to run a test using the profiler to collect CPU and/or Memory data
  • I want to see the results for the profiler in a graphical way
  • The same for all the tests/benchmarks in a package
  • I want to be able to compare two profiler results

@firelizzard18
Copy link
Contributor

@jmaister I created #1724 and #1725 to cover profile diffs and profiling main, respectively. At some point, I'll create a CL for showing graphical test/benchmark profiles and mark it as Fixes #1685.

@firelizzard18
Copy link
Contributor

firelizzard18 commented Aug 26, 2021

@hyangah Do you have any ideas on a good way to persist profiles? Currently, I'm storing profiles in the per-session temp dir. As a user, it is frustrating that profiles go poof when I reload the window (since the extension uses a new temp dir for each activation). But on the other hand, it probably wouldn't be good UX to keep all of the profiles around indefinitely, not to mention consuming disk space. On reload, I could retain the last profile for each test (like VSCode does for test results AFAIK), but there could be hundreds or thousands of tests.

gopherbot pushed a commit that referenced this issue Sep 7, 2021
Adds support for running tests with --cpuprofile, --memprofile,
--mutexprofile, or --blockprofile and then displaying the profile using
`go tool pprof -tree`.

There are two ways of profiling a test: Run with the 'Go (Profile)'
test run profile, or right click the test and execute the Profile
command. We may want to remove one of these.

When a test has been run with a profiling flag, a new option 'Show Last
Profile' appears in the context menu for that test. This can be used to
display the profile from the last test run.

Updates #1685

Change-Id: I74f4d2ae52f03d4428fe633b0a1084373950e3d6
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/344149
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
Trust: Hyang-Ah Hana Kim <hyangah@gmail.com>
Trust: Robert Findley <rfindley@google.com>
@gopherbot
Copy link
Collaborator

Change https://golang.org/cl/344149 mentions this issue: src/goTest: basic profiling support

@firelizzard18
Copy link
Contributor

The same for all the tests/benchmarks in a package

@jmaister Do you mean you want to be able to capture (and view) a profile for each test? Or do you mean you want to capture a profile that includes all tests and benchmarks in a package? In my personal experience, the latter isn't terribly useful, because the results from the various benchmarks are mixed up together. Driven by that experience, and some technical challenges, I decided to always capture a profile for a single benchmark or test at a time. If you use Go nightly and profile a package, it will create a separate profile for each test.

@jmaister
Copy link
Author

jmaister commented Sep 9, 2021

@firelizzard18 I mean to capture the the profile data by test function or by benchmark function.
I agree, it would not be useful to show the results for the whole package.

@hyangah hyangah added the go-test issues related to go test support (test output, test explorer, ...) label Jan 26, 2022
@hyangah hyangah removed the go-test issues related to go test support (test output, test explorer, ...) label Dec 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants