// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Serving of pprof-like profiles.

package traceviewer

import (
	
	
	
	
	
	
	
	
	
	
)

type ProfileFunc func(r *http.Request) ([]ProfileRecord, error)

// SVGProfileHandlerFunc serves pprof-like profile generated by prof as svg.
func ( ProfileFunc) http.HandlerFunc {
	return func( http.ResponseWriter,  *http.Request) {
		if .FormValue("raw") != "" {
			.Header().Set("Content-Type", "application/octet-stream")

			 := func( string,  ...any) {
				.Header().Set("Content-Type", "text/plain; charset=utf-8")
				.Header().Set("X-Go-Pprof", "1")
				http.Error(, fmt.Sprintf(, ...), http.StatusInternalServerError)
			}
			,  := ()
			if  != nil {
				("failed to get records: %v", )
				return
			}
			if  := BuildProfile().Write();  != nil {
				("failed to write profile: %v", )
				return
			}
			return
		}

		,  := os.CreateTemp("", "block")
		if  != nil {
			http.Error(, fmt.Sprintf("failed to create temp file: %v", ), http.StatusInternalServerError)
			return
		}
		defer func() {
			.Close()
			os.Remove(.Name())
		}()
		,  := ()
		if  != nil {
			http.Error(, fmt.Sprintf("failed to generate profile: %v", ), http.StatusInternalServerError)
		}
		 := bufio.NewWriter()
		if  := BuildProfile().Write();  != nil {
			http.Error(, fmt.Sprintf("failed to write profile: %v", ), http.StatusInternalServerError)
			return
		}
		if  := .Flush();  != nil {
			http.Error(, fmt.Sprintf("failed to flush temp file: %v", ), http.StatusInternalServerError)
			return
		}
		if  := .Close();  != nil {
			http.Error(, fmt.Sprintf("failed to close temp file: %v", ), http.StatusInternalServerError)
			return
		}
		 := .Name() + ".svg"
		if ,  := exec.Command(goCmd(), "tool", "pprof", "-svg", "-output", , .Name()).CombinedOutput();  != nil {
			http.Error(, fmt.Sprintf("failed to execute go tool pprof: %v\n%s", , ), http.StatusInternalServerError)
			return
		}
		defer os.Remove()
		.Header().Set("Content-Type", "image/svg+xml")
		http.ServeFile(, , )
	}
}

type ProfileRecord struct {
	Stack []*trace.Frame
	Count uint64
	Time  time.Duration
}

func ( []ProfileRecord) *profile.Profile {
	 := &profile.Profile{
		PeriodType: &profile.ValueType{Type: "trace", Unit: "count"},
		Period:     1,
		SampleType: []*profile.ValueType{
			{Type: "contentions", Unit: "count"},
			{Type: "delay", Unit: "nanoseconds"},
		},
	}
	 := make(map[uint64]*profile.Location)
	 := make(map[string]*profile.Function)
	for ,  := range  {
		var  []*profile.Location
		for ,  := range .Stack {
			 := [.PC]
			if  == nil {
				 := [.File+.Fn]
				if  == nil {
					 = &profile.Function{
						ID:         uint64(len(.Function) + 1),
						Name:       .Fn,
						SystemName: .Fn,
						Filename:   .File,
					}
					.Function = append(.Function, )
					[.File+.Fn] = 
				}
				 = &profile.Location{
					ID:      uint64(len(.Location) + 1),
					Address: .PC,
					Line: []profile.Line{
						{
							Function: ,
							Line:     int64(.Line),
						},
					},
				}
				.Location = append(.Location, )
				[.PC] = 
			}
			 = append(, )
		}
		.Sample = append(.Sample, &profile.Sample{
			Value:    []int64{int64(.Count), int64(.Time)},
			Location: ,
		})
	}
	return 
}

func goCmd() string {
	var  string
	if runtime.GOOS == "windows" {
		 = ".exe"
	}
	 := filepath.Join(runtime.GOROOT(), "bin", "go"+)
	if ,  := os.Stat();  == nil {
		return 
	}
	return "go"
}