PprofInfoFromArchive uses the consolidated /api/v2/debug/profile endpoint to collect pprof data in a single request. The server temporarily enables block/mutex profiling, runs time-based profiles for the given duration, takes snapshots, and returns a tar.gz archive.
(ctx context.Context, client *codersdk.Client, log slog.Logger, duration time.Duration)
| 802 | // block/mutex profiling, runs time-based profiles for the given duration, |
| 803 | // takes snapshots, and returns a tar.gz archive. |
| 804 | func PprofInfoFromArchive(ctx context.Context, client *codersdk.Client, log slog.Logger, duration time.Duration) (*PprofCollection, error) { |
| 805 | if client == nil { |
| 806 | return nil, xerrors.New("client is nil") |
| 807 | } |
| 808 | |
| 809 | body, err := client.DebugCollectProfile(ctx, codersdk.DebugProfileOptions{ |
| 810 | Duration: duration, |
| 811 | // Use the server defaults plus trace. |
| 812 | Profiles: []string{"cpu", "heap", "allocs", "block", "mutex", "goroutine", "threadcreate", "trace"}, |
| 813 | }) |
| 814 | if err != nil { |
| 815 | return nil, xerrors.Errorf("fetch consolidated profile: %w", err) |
| 816 | } |
| 817 | defer body.Close() |
| 818 | |
| 819 | data, err := io.ReadAll(body) |
| 820 | if err != nil { |
| 821 | return nil, xerrors.Errorf("read profile archive: %w", err) |
| 822 | } |
| 823 | |
| 824 | var p PprofCollection |
| 825 | if client.URL != nil { |
| 826 | if u, err := client.URL.Parse("/api/v2/debug/profile"); err == nil { |
| 827 | p.EndpointURL = u.String() |
| 828 | } |
| 829 | } |
| 830 | if p.EndpointURL == "" { |
| 831 | p.EndpointURL = "/api/v2/debug/profile" |
| 832 | } |
| 833 | p.CollectedAt = time.Now() |
| 834 | |
| 835 | // Parse the tar.gz archive and populate the PprofCollection. |
| 836 | gr, err := gzip.NewReader(bytes.NewReader(data)) |
| 837 | if err != nil { |
| 838 | return nil, xerrors.Errorf("open gzip reader: %w", err) |
| 839 | } |
| 840 | defer gr.Close() |
| 841 | |
| 842 | tr := tar.NewReader(gr) |
| 843 | for { |
| 844 | hdr, err := tr.Next() |
| 845 | if errors.Is(err, io.EOF) { |
| 846 | break |
| 847 | } |
| 848 | if err != nil { |
| 849 | return nil, xerrors.Errorf("read tar entry %q: %w", hdr.Name, err) |
| 850 | } |
| 851 | |
| 852 | content, err := io.ReadAll(tr) |
| 853 | if err != nil { |
| 854 | log.Warn(ctx, "failed to read tar entry", slog.F("name", hdr.Name), slog.Error(err)) |
| 855 | continue |
| 856 | } |
| 857 | |
| 858 | // Files in the archive are named like "cpu.prof", "heap.prof", |
| 859 | // "trace.out", etc. Compress binary profile data for storage in |
| 860 | // the bundle, matching what PprofInfo() does. |
| 861 | base := path.Base(hdr.Name) |
no test coverage detected