Source file src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph.go

     1  // Copyright 2017 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package driver
    16  
    17  import (
    18  	"encoding/json"
    19  	"html/template"
    20  	"net/http"
    21  	"strings"
    22  
    23  	"github.com/google/pprof/internal/graph"
    24  	"github.com/google/pprof/internal/measurement"
    25  	"github.com/google/pprof/internal/report"
    26  )
    27  
    28  type treeNode struct {
    29  	Name      string      `json:"n"`
    30  	FullName  string      `json:"f"`
    31  	Cum       int64       `json:"v"`
    32  	CumFormat string      `json:"l"`
    33  	Percent   string      `json:"p"`
    34  	Children  []*treeNode `json:"c"`
    35  }
    36  
    37  // flamegraph generates a web page containing a flamegraph.
    38  func (ui *webInterface) flamegraph(w http.ResponseWriter, req *http.Request) {
    39  	// Force the call tree so that the graph is a tree.
    40  	// Also do not trim the tree so that the flame graph contains all functions.
    41  	rpt, errList := ui.makeReport(w, req, []string{"svg"}, func(cfg *config) {
    42  		cfg.CallTree = true
    43  		cfg.Trim = false
    44  	})
    45  	if rpt == nil {
    46  		return // error already reported
    47  	}
    48  
    49  	// Generate dot graph.
    50  	g, config := report.GetDOT(rpt)
    51  	var nodes []*treeNode
    52  	nroots := 0
    53  	rootValue := int64(0)
    54  	nodeArr := []string{}
    55  	nodeMap := map[*graph.Node]*treeNode{}
    56  	// Make all nodes and the map, collect the roots.
    57  	for _, n := range g.Nodes {
    58  		v := n.CumValue()
    59  		fullName := n.Info.PrintableName()
    60  		node := &treeNode{
    61  			Name:      graph.ShortenFunctionName(fullName),
    62  			FullName:  fullName,
    63  			Cum:       v,
    64  			CumFormat: config.FormatValue(v),
    65  			Percent:   strings.TrimSpace(measurement.Percentage(v, config.Total)),
    66  		}
    67  		nodes = append(nodes, node)
    68  		if len(n.In) == 0 {
    69  			nodes[nroots], nodes[len(nodes)-1] = nodes[len(nodes)-1], nodes[nroots]
    70  			nroots++
    71  			rootValue += v
    72  		}
    73  		nodeMap[n] = node
    74  		// Get all node names into an array.
    75  		nodeArr = append(nodeArr, n.Info.Name)
    76  	}
    77  	// Populate the child links.
    78  	for _, n := range g.Nodes {
    79  		node := nodeMap[n]
    80  		for child := range n.Out {
    81  			node.Children = append(node.Children, nodeMap[child])
    82  		}
    83  	}
    84  
    85  	rootNode := &treeNode{
    86  		Name:      "root",
    87  		FullName:  "root",
    88  		Cum:       rootValue,
    89  		CumFormat: config.FormatValue(rootValue),
    90  		Percent:   strings.TrimSpace(measurement.Percentage(rootValue, config.Total)),
    91  		Children:  nodes[0:nroots],
    92  	}
    93  
    94  	// JSON marshalling flame graph
    95  	b, err := json.Marshal(rootNode)
    96  	if err != nil {
    97  		http.Error(w, "error serializing flame graph", http.StatusInternalServerError)
    98  		ui.options.UI.PrintErr(err)
    99  		return
   100  	}
   101  
   102  	ui.render(w, req, "flamegraph", rpt, errList, config.Labels, webArgs{
   103  		FlameGraph: template.JS(b),
   104  		Nodes:      nodeArr,
   105  	})
   106  }
   107  

View as plain text