Skip to content

cmd/trace: analyze Go 1.22+ traces scalably #65315

Open

Description

@mknyszek

Currently cmd/trace loads the entire expanded trace into memory, always. However, the Go 1.22+ trace API is a streaming API, so it's totally possible for the tool to split up traces and only touch the necessary parts.

This will require some refactoring, and will definitely be easier once we remove the old tracer and the old tracer parser (putting old trace parsing behind the new API in internal/trace/v2).

Activity

  1. added this to the Backlog milestone on Jan 26, 2024
  2. added
    FeatureRequestIssues asking for a new feature that does not need a proposal.
    on Jan 26, 2024
  3. added
    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.
    on Jan 26, 2024
  4. self-assigned this
    on Jan 31, 2024
  5. self-assigned this
    on Jul 19, 2024
  6. removed their assignment
    on May 8, 2025
  7. cstockton commented on Dec 7, 2025

    @cstockton

    Just wanted to say awesome work and this is a very exciting feature to me, I am going to do some reading on the new trace API. I think we could make some incredible visual tooling with it. I've done some work in this area #18744 along with some (not so polished) UI's, I'll evaluate how many changes there are to see if I can update my old library.

  8. dwisiswant0 commented on Dec 16, 2025

    @dwisiswant0

    +1, this has significantly impacted me as well. (#76849)

    I also tried to work around this by extracting individual profiles only, but that doesn’t help. Running go tool trace -pprof={net,sync,syscall,sched} consistently results in the process being OOM-killed, which suggests the full trace is still being parsed/expanded even when only a single profile is requested.

  9. mknyszek commented on Dec 19, 2025

    @mknyszek
    ContributorAuthor

    @dwisiswant0 If you're up to try out something that should work better, give https://go.dev/cl/731509 a try.

    git clone https://go.googlesource.com/go
    cd go
    git fetch https://go.googlesource.com/go refs/changes/09/731509/7 && git checkout FETCH_HEAD
    cd src
    ./make.bash
    cd ..
    bin/go tool trace <trace>
    

    I could definitely use your feedback, but be wary of rough edges. 🙈

    EDIT: I've made a good bit of progress and sanded down quite a few rough edges. Still, be wary of bugs.

  10. moved this from Todo to In Progress in Go Compiler / Runtime / Commandon Dec 19, 2025
  11. gopherbot commented on Dec 19, 2025

    @gopherbot
    Contributor

    Change https://go.dev/cl/706375 mentions this issue: internal/trace: add Index for scalably exploring a trace

  12. gopherbot commented on Dec 19, 2025

    @gopherbot
    Contributor

    Change https://go.dev/cl/731504 mentions this issue: cmd/trace: parse trace events on demand

  13. gopherbot commented on Dec 19, 2025

    @gopherbot
    Contributor

    Change https://go.dev/cl/731509 mentions this issue: cmd/trace: reorient web UI in terms of trace slice selections

  14. romaindoumenc commented on Mar 23, 2026

    @romaindoumenc

    I tried with patchset 20 (GOVERSION='go1.27-devel_578e81d808'), and I think the full trace is still processed in memory – getting OOM killed after eating up all 32GB of RAM on a 2GB trace.

    2026/03/23 13:30:06 Pre-processing trace (0.0%)...
    2026/03/23 13:30:07 Pre-processing trace (0.0%)...
    2026/03/23 13:30:08 Pre-processing trace (0.0%)...
    go tool trace: signal: killed
    

    Happy to share the trace itself if it helps @mknyszek .

  15. mknyszek commented on Mar 23, 2026

    @mknyszek
    ContributorAuthor

    Please do share the trace. It definitely doesn't load the whole trace into memory anymore, but it does keep summary information around that should be relatively compact.

  16. romaindoumenc commented on Mar 23, 2026

    @romaindoumenc

    Please find the trace attach (it is a bit of a gzip bomb, uncompressing to 1.2GB), shasum ba09eeb5f630d849ee6059b3fc7aeff242e9e0ba.

    The code generating said trace seems to be stuck in an expensive loop (I am investigating the actual root cause, I can revert back if this is any use).

    trace_example_loop.gz

  17. mknyszek commented on Mar 23, 2026

    @mknyszek
    ContributorAuthor

    Oh no, almost the entirety of this trace is region start/end events.

    ~ $ go tool trace -d=footprint ./trace_example_loop
    Event                Bytes      %       Count      %
    -                    -          -       -          -
    UserRegionEnd        616584704  49.95%  123316595  49.96%
    UserRegionBegin      616584499  49.95%  123316596  49.96%
    GoStart              277530     0.02%   47579      0.02%
    EventBatch           226524     0.02%   18873      0.01%
    GoUnblock            200290     0.02%   27123      0.01%
    GoSyscallBegin       137159     0.01%   27103      0.01%
    GoCreate             119975     0.01%   19136      0.01%
    GoBlock              110083     0.01%   27125      0.01%
    GoSyscallEnd         55787      0.00%   26964      0.01%
    GoDestroy            38543      0.00%   19133      0.01%
    String               21835      0.00%   651        0.00%
    Stack                15352      0.00%   242        0.00%
    GoStop               4844       0.00%   1179       0.00%
    HeapAlloc            2972       0.00%   480        0.00%
    GoStatusStack        1606       0.00%   106        0.00%
    ProcSteal            1126       0.00%   139        0.00%
    ProcStart            996        0.00%   148        0.00%
    GoSyscallEndBlocked  424        0.00%   139        0.00%
    GoStatus             386        0.00%   32         0.00%
    GCSweepEnd           226        0.00%   35         0.00%
    GCSweepBegin         109        0.00%   35         0.00%
    ClockSnapshot        36         0.00%   2          0.00%
    ProcStatus           32         0.00%   8          0.00%
    ProcsChange          20         0.00%   5          0.00%
    ProcStop             14         0.00%   6          0.00%
    STWBegin             13         0.00%   3          0.00%
    HeapGoal             12         0.00%   2          0.00%
    Frequency            10         0.00%   2          0.00%
    STWEnd               7          0.00%   3          0.00%
    GoLabel              6          0.00%   2          0.00%
    GCBegin              4          0.00%   1          0.00%
    GCEnd                3          0.00%   1          0.00%
    GCMarkAssistBegin    3          0.00%   1          0.00%
    GCMarkAssistEnd      3          0.00%   1          0.00%
    Strings              2          0.00%   2          0.00%
    Stacks               2          0.00%   2          0.00%
    Sync                 2          0.00%   2          0.00%
    EndOfGeneration      2          0.00%   2          0.00%
    

    Region events are one of the few events retained by the summary structure, hence the enormous memory usage. Worse still, these region events are generated in a trace that only has 2 generations (see the EndOfGeneration) which are the split points that allow the trace parser to observe only a subset of the trace.

    I honestly am not sure what to do about this. Without summary information, choosing how to slice and dice the trace is quite hard because you don't have a sense of what's going on and what might be interesting to look at. We can probably make things slightly better by not keeping the actual events around, but that wouldn't help all that much in this case since the summary information needs most of the region information anyway to produce useful summaries (like histograms of regions and such). We could also possibly produce the region summary information on-demand? That would require more re-architecting and I probably won't have time to get to that.

    We could maybe do something more user-friendly and tell the user that there's a problem and just not show summary information at all in such cases, but I'm also not sure what heuristic we should use for that.

  18. mknyszek commented on Mar 23, 2026

    @mknyszek
    ContributorAuthor

    @prattmic also notes that we could do better here by having more generations. In this particular case we could add splitting by trace size, which is something I considered long ago but never added.

    Though, that wouldn't help the OOM issue, since the summary structures would still retain ~99.9% of events anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

FeatureRequestIssues asking for a new feature that does not need a proposal.NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.compiler/runtimeIssues related to the Go compiler and/or runtime.

Type

No type

Projects

Relationships

None yet

Development

No branches or pull requests

Issue actions