package main

import (
	"fmt"
	"net/http"
	httppprof "net/http/pprof"
	"os"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"sync"
	"time"
)

var retained [][]byte
var temporary []byte
var sink uint64

//go:noinline
func churn() {
	for range 128 {
		b := make([]byte, 64<<10)
		b[0] = 1
		temporary = b
	}
	temporary = nil
}

//go:noinline
func keep() {
	for range 64 {
		retained = append(retained, make([]byte, 64<<10))
	}
}

//go:noinline
func burn(d time.Duration) {
	x := uint64(1)
	until := time.Now().Add(d)
	for time.Now().Before(until) {
		for range 10000 {
			x = x*1664525 + 1013904223
		}
	}
	sink = x
}
func dump(dir, name string) error {
	f, err := os.Create(filepath.Join(dir, name+".pb.gz"))
	if err != nil {
		return err
	}
	err = pprof.Lookup(name).WriteTo(f, 0)
	closeErr := f.Close()
	if err != nil {
		return err
	}
	return closeErr
}
func adminMux() *http.ServeMux {
	m := http.NewServeMux()
	m.HandleFunc("GET /debug/pprof/", httppprof.Index)
	m.HandleFunc("GET /debug/pprof/profile", httppprof.Profile)
	m.HandleFunc("GET /debug/pprof/trace", httppprof.Trace)
	return m
}
func run(dir string) error {
	if err := os.MkdirAll(dir, 0700); err != nil {
		return err
	}
	// Only this short-lived laboratory enables full sampling.
	runtime.MemProfileRate = 1
	runtime.SetBlockProfileRate(1)
	old := runtime.SetMutexProfileFraction(1)
	defer runtime.SetBlockProfileRate(0)
	defer runtime.SetMutexProfileFraction(old)
	f, err := os.Create(filepath.Join(dir, "cpu.pb.gz"))
	if err != nil {
		return err
	}
	if err := pprof.StartCPUProfile(f); err != nil {
		f.Close()
		return err
	}
	burn(350 * time.Millisecond)
	pprof.StopCPUProfile()
	if err := f.Close(); err != nil {
		return err
	}
	keep()
	churn()
	runtime.GC()
	runtime.GC()
	for _, name := range []string{"heap", "allocs"} {
		if err := dump(dir, name); err != nil {
			return err
		}
	}
	ready := make(chan struct{}, 4)
	release := make(chan struct{})
	var waiting sync.WaitGroup
	for range 4 {
		waiting.Go(func() { ready <- struct{}{}; <-release })
	}
	for range 4 {
		<-ready
	}
	if err := dump(dir, "goroutine"); err != nil {
		close(release)
		waiting.Wait()
		return err
	}
	close(release)
	waiting.Wait()
	var mu sync.Mutex
	var wg sync.WaitGroup
	for range 4 {
		wg.Go(func() {
			for range 20 {
				mu.Lock()
				time.Sleep(time.Millisecond)
				mu.Unlock()
			}
		})
	}
	wg.Wait()
	for _, name := range []string{"block", "mutex", "threadcreate"} {
		if err := dump(dir, name); err != nil {
			return err
		}
	}
	fmt.Println("retained_bytes:", len(retained)*(64<<10))
	fmt.Println("profiles: cpu heap allocs goroutine block mutex threadcreate")
	return nil
}
func main() {
	dir := "out"
	if len(os.Args) > 1 {
		dir = os.Args[1]
	}
	if err := run(dir); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
