Skip to content

cmd/trace: visualize time taken by syscall #57307

Closed
@aktau

Description

@aktau

Currently (Go 1.20-dev), syscalls as processed by go tool trace from a runtime/trace profile visualize as points in time, being about 1 pixel wide no matter how long they took (see reproducer below). It would be nicer if they were slices, to visualize how much time is spent in (conceptually blocking) syscalls for a given goroutine.

image

Somewhere in the last release cycle (1.19), I tried making the obvious patch to cmd/trace:

                case trace.EvGoSysCall:
-                       ctx.emitInstant(ev, "syscall", "")
+                       if ev.Link != nil {
+                               ctx.emitSlice(ev, "syscall")
+                       } else {
+                               ctx.emitInstant(ev, "syscall", "")
+                       }

And I remembered it didn't work: most (all?) EvGoSysCall events don't have a link to the finish event. But before filing this proposal I tried patching up cmd/trace from 1.20 and to my surprise it did work. I assume something changed in Go 1.20 and now more (most?) EvGoSysCall events have a link, which is great. Debugging output of my patched go tool trace:

...
2022/12/14 05:42:10 syscall event with link (6/12):
    101597987   GoSysCall       p=1     g=1     off=2876        stkID=22        seq=0
 -> 101613258   GoSysExit       p=1000003       g=1     off=264 stkID=0 seq=0 { g=1 seq=17 ts=0 }
 -> 101613376   GoStart p=0     g=1     off=270 stkID=0 seq=0 { g=1 seq=0 }
 -> 101619219   GoSysBlock      p=0     g=1     off=283 stkID=0 seq=0
2022/12/14 05:42:10 syscall event with link (6/13):
    101618055   GoSysCall       p=0     g=1     off=279 stkID=22        seq=0
 -> 101633491   GoSysExit       p=1000003       g=1     off=297 stkID=0 seq=0 { g=1 seq=19 ts=0 }
 -> 101633634   GoStart p=0     g=1     off=303 stkID=0 seq=0 { g=1 seq=0 }
 -> 101637671   GoSysBlock      p=0     g=1     off=315 stkID=0 seq=0
...

Resulting trace view comparison:

image

Reproducer:

The program that generated this trace was:

package main

import (
        "fmt"
        "os"
        "runtime/trace"
        "time"
)

func main() {
        if err := rmain(); err != nil {
                fmt.Fprintf(os.Stderr, "error: %v", err)
                os.Exit(1)
        }
}

func rmain() error {
        if err := trace.Start(os.Stdout); err != nil {
                return err
        }
        defer trace.Stop()
        f, err := os.Open("/dev/urandom")
        if err != nil {
                return err
        }
        var buf [1024 * 1024 * 1024]byte
        for i, last := 0, time.Now(); i < 5; i++ {
                fmt.Fprintf(os.Stderr, "start...")
                n, err := f.Read(buf[:])
                if err != nil {
                        return err
                }
                if n == 0 {
                        return fmt.Errorf("read: returned empty slice (%d, %v)", n, err)
                }
                now := time.Now()
                fmt.Fprintf(os.Stderr, "took %v\n", now.Sub(last))
                last = now
        }

        return nil
}

Running it:

$ go run longsyscall.go > longsyscall.trace
start...took 2.203378744s
start...took 2.207477581s
start...took 2.211924562s
start...took 2.202174787s
start...took 2.219617454s

Metadata

Metadata

Assignees

No one assigned

    Labels

    FeatureRequestIssues asking for a new feature that does not need a proposal.FrozenDueToAgeNeedsInvestigationSomeone 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

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions