(suffix string, visualizers []string)
| 408 | } |
| 409 | |
| 410 | func invokeVisualizer(suffix string, visualizers []string) PostProcessor { |
| 411 | return func(input io.Reader, output io.Writer, ui plugin.UI) error { |
| 412 | tempFile, err := newTempFile(os.TempDir(), "pprof", "."+suffix) |
| 413 | if err != nil { |
| 414 | return err |
| 415 | } |
| 416 | deferDeleteTempFile(tempFile.Name()) |
| 417 | if _, err := io.Copy(tempFile, input); err != nil { |
| 418 | return err |
| 419 | } |
| 420 | tempFile.Close() |
| 421 | // Try visualizers until one is successful |
| 422 | for _, v := range visualizers { |
| 423 | // Separate command and arguments for exec.Command. |
| 424 | args := strings.Split(v, " ") |
| 425 | if len(args) == 0 { |
| 426 | continue |
| 427 | } |
| 428 | viewer := exec.Command(args[0], append(args[1:], tempFile.Name())...) |
| 429 | viewer.Stderr = os.Stderr |
| 430 | if err = viewer.Start(); err == nil { |
| 431 | // Wait for a second so that the visualizer has a chance to |
| 432 | // open the input file. This needs to be done even if we're |
| 433 | // waiting for the visualizer as it can be just a wrapper that |
| 434 | // spawns a browser tab and returns right away. |
| 435 | defer func(t <-chan time.Time) { |
| 436 | <-t |
| 437 | }(time.After(time.Second)) |
| 438 | // On interactive mode, let the visualizer run in the background |
| 439 | // so other commands can be issued. |
| 440 | if !interactiveMode { |
| 441 | return viewer.Wait() |
| 442 | } |
| 443 | return nil |
| 444 | } |
| 445 | } |
| 446 | return err |
| 447 | } |
| 448 | } |
| 449 | |
| 450 | // stringToBool is a custom parser for bools. We avoid using strconv.ParseBool |
| 451 | // to remain compatible with old pprof behavior (e.g., treating "" as true). |
no test coverage detected
searching dependent graphs…