diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 861c7fd35..a65297f4a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -154,31 +154,7 @@ If you want to trigger a debug session from VSCode, you can use the following sn ## Profiling -If you want to investigate what's contributing to CPU usage you can add the following to the top of the `main()` function in `main.go` - -```go -import "runtime/pprof" - -func main() { - f, err := os.Create("cpu.prof") - if err != nil { - log.Fatal("could not create CPU profile: ", err) - } - defer f.Close() - if err := pprof.StartCPUProfile(f); err != nil { - log.Fatal("could not start CPU profile: ", err) - } - defer pprof.StopCPUProfile() - ... -``` - -Then run lazygit, and afterwards, from your terminal, run: - -```sh -go tool pprof --web cpu.prof -``` - -That should open an application which allows you to view the breakdown of CPU usage. +If you want to investigate what's contributing to CPU or memory usage, see [this separate document](docs/dev/Profiling.md). ## Testing diff --git a/docs/dev/Profiling.md b/docs/dev/Profiling.md new file mode 100644 index 000000000..bfdffe4f9 --- /dev/null +++ b/docs/dev/Profiling.md @@ -0,0 +1,69 @@ +# Profiling Lazygit + +If you want to investigate what's contributing to CPU or memory usage, start +lazygit with the `-profile` command line flag. This tells it to start an +integrated web server that listens for profiling requests. + +## Save profile data + +### CPU + +While lazygit is running with the `-profile` flag, perform a CPU profile and +save it to a file by running this command in another terminal window: + +```sh +curl -o cpu.out http://127.0.0.1:6060/debug/pprof/profile +``` + +By default, it profiles for 30 seconds. To change the duration, use + +```sh +curl -o cpu.out 'http://127.0.0.1:6060/debug/pprof/profile?seconds=60' +``` + +### Memory + +To save a heap profile (containing information about all memory allocated so +far since startup), use + +```sh +curl -o mem.out http://127.0.0.1:6060/debug/pprof/heap +``` + +Sometimes it can be useful to get a delta log, i.e. to see how memory usage +developed from one point in time to another. For that, use + +```sh +curl -o mem.out 'http://127.0.0.1:6060/debug/pprof/heap?seconds=20' +``` + +This will log the memory usage difference between now and 20 seconds later, so +it gives you 20 seconds to perform the action in lazygit that you are interested +in measuring. + +## View profile data + +To display the profile data, you can either use speedscope.app, or the pprof +tool that comes with go. I prefer the former because it has a nicer UI and is a +little more powerful; however, I have seen cases where it wasn't able to load a +profile for some reason, in which case it's good to have the pprof tool as a +fallback. + +### Speedscope.app + +Go to https://www.speedscope.app/ in your browser, and drag the saved profile +onto the browser window. Refer to [the +documentation](https://github.com/jlfwong/speedscope?tab=readme-ov-file#usage) +for how to navigate the data. + +### Pprof tool + +To view a profile that you saved as `cpu.out`, use + +```sh +go tool pprof -http=:8080 cpu.out +``` + +By default this shows the graph view, which I don't find very useful myself. +Choose "Flame Graph" from the View menu to show a much more useful +representation of the data. diff --git a/docs/dev/README.md b/docs/dev/README.md index dad24cfb6..fcfcf2741 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -5,3 +5,4 @@ * [Integration Tests](../../pkg/integration/README.md) * [Demo Recordings](./Demo_Recordings.md) * [Find base commit for fixup design](Find_Base_Commit_For_Fixup_Design.md) +* [Profiling](Profiling.md) diff --git a/pkg/app/entry_point.go b/pkg/app/entry_point.go index baeb43ae5..96f6176c7 100644 --- a/pkg/app/entry_point.go +++ b/pkg/app/entry_point.go @@ -4,6 +4,8 @@ import ( "bytes" "fmt" "log" + "net/http" + _ "net/http/pprof" "os" "os/exec" "path/filepath" @@ -30,6 +32,7 @@ type cliArgs struct { PrintVersionInfo bool Debug bool TailLogs bool + Profile bool PrintDefaultConfig bool PrintConfigDir bool UseConfigDir string @@ -145,6 +148,14 @@ func Start(buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTes return } + if cliArgs.Profile { + go func() { + if err := http.ListenAndServe("localhost:6060", nil); err != nil { + log.Fatal(err) + } + }() + } + parsedGitArg := parseGitArg(cliArgs.GitArg) Run(appConfig, common, appTypes.NewStartArgs(cliArgs.FilterPath, parsedGitArg, integrationTest)) @@ -171,6 +182,9 @@ func parseCliArgsAndEnvVars() *cliArgs { tailLogs := false flaggy.Bool(&tailLogs, "l", "logs", "Tail lazygit logs (intended to be used when `lazygit --debug` is called in a separate terminal tab)") + profile := false + flaggy.Bool(&profile, "", "profile", "Start the profiler and serve it on http port 6060. See CONTRIBUTING.md for more info.") + printDefaultConfig := false flaggy.Bool(&printDefaultConfig, "c", "config", "Print the default config") @@ -202,6 +216,7 @@ func parseCliArgsAndEnvVars() *cliArgs { PrintVersionInfo: printVersionInfo, Debug: debug, TailLogs: tailLogs, + Profile: profile, PrintDefaultConfig: printDefaultConfig, PrintConfigDir: printConfigDir, UseConfigDir: useConfigDir,