func invokeVisualizer(interactive **bool, format PostProcessor, suffix string, visualizers []string) PostProcessor { return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error { if err := makeVizTmpDir(); err != nil { return err } tempFile, err := tempfile.New(vizTmpDir, "pprof", "."+suffix) if err != nil { return err } tempfile.DeferDelete(tempFile.Name()) if err = format(input, tempFile, ui); err != nil { return err } tempFile.Close() // on windows, if the file is Open, start cannot access it. // Try visualizers until one is successful for _, v := range visualizers { // Separate command and arguments for exec.Command. args := strings.Split(v, " ") if len(args) == 0 { continue } viewer := exec.Command(args[0], append(args[1:], tempFile.Name())...) viewer.Stderr = os.Stderr if err = viewer.Start(); err == nil { if !**interactive { // In command-line mode, wait for the viewer to be closed // before proceeding return viewer.Wait() } return nil } } return err } }
// awayFromTTY saves the output in a file if it would otherwise go to // the terminal screen. This is used to avoid dumping binary data on // the screen. func awayFromTTY(format string) PostProcessor { return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error { if output == os.Stdout && ui.IsTerminal() { tempFile, err := tempfile.New("", "profile", "."+format) if err != nil { return err } ui.PrintErr("Generating report in ", tempFile.Name()) _, err = fmt.Fprint(tempFile, input) return err } _, err := fmt.Fprint(output, input) return err } }
// grabProfile fetches and symbolizes a profile. func grabProfile(source, exec, buildid string, fetch plugin.Fetcher, sym plugin.Symbolizer, obj plugin.ObjTool, ui plugin.UI, f *flags) (*profile.Profile, error) { source, host, duration := adjustURL(source, *f.flagSeconds, ui) remote := host != "" if remote { ui.Print("Fetching profile from ", source) if duration != 0 { ui.Print("Please wait... (" + duration.String() + ")") } } now := time.Now() // Fetch profile from source. // Give 50% slack on the timeout. p, err := fetch(source, duration+duration/2, ui) if err != nil { return nil, err } // Update the time/duration if the profile source doesn't include it. // TODO(rsilvera): Remove this when we remove support for legacy profiles. if remote { if p.TimeNanos == 0 { p.TimeNanos = now.UnixNano() } if duration != 0 && p.DurationNanos == 0 { p.DurationNanos = int64(duration) } } // Replace executable/buildID with the options provided in the // command line. Assume the executable is the first Mapping entry. if exec != "" || buildid != "" { if len(p.Mapping) == 0 { // Create a fake mapping to hold the user option, and associate // all samples to it. m := &profile.Mapping{ ID: 1, } for _, l := range p.Location { l.Mapping = m } p.Mapping = []*profile.Mapping{m} } if exec != "" { p.Mapping[0].File = exec } if buildid != "" { p.Mapping[0].BuildID = buildid } } if err := sym(*f.flagSymbolize, source, p, obj, ui); err != nil { return nil, err } // Save a copy of any remote profiles, unless the user is explicitly // saving it. if remote && !f.isFormat("proto") { prefix := "pprof." if len(p.Mapping) > 0 && p.Mapping[0].File != "" { prefix = prefix + filepath.Base(p.Mapping[0].File) + "." } if !strings.ContainsRune(host, os.PathSeparator) { prefix = prefix + host + "." } for _, s := range p.SampleType { prefix = prefix + s.Type + "." } dir := os.Getenv("PPROF_TMPDIR") tempFile, err := tempfile.New(dir, prefix, ".pb.gz") if err == nil { if err = p.Write(tempFile); err == nil { ui.PrintErr("Saved profile in ", tempFile.Name()) } } if err != nil { ui.PrintErr("Could not save profile: ", err) } } if err := p.Demangle(obj.Demangle); err != nil { ui.PrintErr("Failed to demangle profile: ", err) } if err := p.CheckValid(); err != nil { return nil, fmt.Errorf("Grab %s: %v", source, err) } return p, nil }