1// Copyright 2023 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Preprofile creates an intermediate representation of a pprof profile for use
6// during PGO in the compiler. This transformation depends only on the profile
7// itself and is thus wasteful to perform in every invocation of the compiler.
8//
9// Usage:
10//
11//	go tool preprofile [-v] [-o output] -i input
12//
13//
14
15package main
16
17import (
18	"bufio"
19	"cmd/internal/objabi"
20	"cmd/internal/pgo"
21	"cmd/internal/telemetry/counter"
22	"flag"
23	"fmt"
24	"log"
25	"os"
26)
27
28func usage() {
29	fmt.Fprintf(os.Stderr, "usage: go tool preprofile [-v] [-o output] -i input\n\n")
30	flag.PrintDefaults()
31	os.Exit(2)
32}
33
34var (
35	output = flag.String("o", "", "output file path")
36	input  = flag.String("i", "", "input pprof file path")
37)
38
39func preprocess(profileFile string, outputFile string) error {
40	f, err := os.Open(profileFile)
41	if err != nil {
42		return fmt.Errorf("error opening profile: %w", err)
43	}
44	defer f.Close()
45
46	r := bufio.NewReader(f)
47	d, err := pgo.FromPProf(r)
48	if err != nil {
49		return fmt.Errorf("error parsing profile: %w", err)
50	}
51
52	var out *os.File
53	if outputFile == "" {
54		out = os.Stdout
55	} else {
56		out, err = os.Create(outputFile)
57		if err != nil {
58			return fmt.Errorf("error creating output file: %w", err)
59		}
60		defer out.Close()
61	}
62
63	w := bufio.NewWriter(out)
64	if _, err := d.WriteTo(w); err != nil {
65		return fmt.Errorf("error writing output file: %w", err)
66	}
67
68	return nil
69}
70
71func main() {
72	objabi.AddVersionFlag()
73
74	log.SetFlags(0)
75	log.SetPrefix("preprofile: ")
76	counter.Open()
77
78	flag.Usage = usage
79	flag.Parse()
80	counter.Inc("preprofile/invocations")
81	counter.CountFlags("preprofile/flag:", *flag.CommandLine)
82	if *input == "" {
83		log.Print("Input pprof path required (-i)")
84		usage()
85	}
86
87	if err := preprocess(*input, *output); err != nil {
88		log.Fatal(err)
89	}
90}
91